From ecf1486289f185b5c842565bd3b17cf4ee7119d4 Mon Sep 17 00:00:00 2001 From: Radiks Alijevs Date: Tue, 21 Apr 2026 11:53:25 +0300 Subject: [PATCH 1/9] docs: add UI testing feature --- CHANGELOG.md | 2 +- README.md | 13 + docs/ENTRYPOINTS.md | 12 +- docs/LOCAL_ORCHESTRATOR_UI_ARCHITECTURE.md | 509 +++ docs/LOCAL_UI_REPORT_CHIP_GRID.md | 73 + docs/LOCAL_UI_SAAS_BLOCK_MIGRATION_PLAN.md | 242 ++ docs/LOCAL_UI_TAILWIND_VITE.md | 62 + docs/MONTE_CARLO_SIMULATION_IMPLEMENTATION.md | 6 +- docs/OPEN_CORE_INTEGRATION_PRINCIPLES.md | 12 +- docs/README.md | 7 +- docs/examples-map-payload-to-unified.md | 14 +- docs/examples/README.md | 2 +- package-lock.json | 3380 +++++++++++++---- package.json | 5 + packages/adapters/README.md | 13 + packages/cli/README.md | 29 + .../saas-mock-paper-tiger-report.json | 1037 +++++ packages/cli/fixtures/saas-mock-report.json | 1027 +++++ packages/cli/package.json | 22 +- packages/cli/src/commands/ui.ts | 1901 +++++++++ packages/cli/src/index.ts | 19 + packages/cli/web/index.html | 18 + packages/cli/web/postcss.config.cjs | 8 + packages/cli/web/src/ApiErrorBanner.tsx | 23 + packages/cli/web/src/App.tsx | 73 + packages/cli/web/src/index.css | 61 + packages/cli/web/src/legacy-global.css | 436 +++ packages/cli/web/src/legacy-orchestrator.ts | 1 + packages/cli/web/src/legacy/api.ts | 9 + packages/cli/web/src/legacy/json.ts | 3 + packages/cli/web/src/legacy/mount.tsx | 28 + .../cli/web/src/legacy/orchestrator-app.tsx | 5 + packages/cli/web/src/legacy/report-view.tsx | 771 ++++ packages/cli/web/src/legacy/types.ts | 58 + .../web/src/legacy/wizard/CsvAnalyzePanel.tsx | 22 + .../legacy/wizard/IntegrationJobLogsPanel.tsx | 247 ++ .../legacy/wizard/KiploksWorkspacePanel.tsx | 827 ++++ .../src/legacy/wizard/OrchestratorAppView.tsx | 42 + .../src/legacy/wizard/ReportsManagerPanel.tsx | 77 + .../web/src/legacy/wizard/StepPathPanel.tsx | 50 + .../src/legacy/wizard/WizardHeaderSection.tsx | 114 + .../src/legacy/wizard/integrationStepModel.ts | 33 + .../web/src/legacy/wizard/orchestratorUi.ts | 34 + .../src/legacy/wizard/useOrchestratorApp.ts | 632 +++ packages/cli/web/src/legacy/workflow.ts | 13 + packages/cli/web/src/main.tsx | 10 + .../cli/web/src/shell/PreflightSummary.tsx | 210 + packages/cli/web/src/shell/ShellAppHeader.tsx | 40 + .../cli/web/src/shell/ShellReportsStep.tsx | 130 + .../cli/web/src/shell/ShellStepMainCard.tsx | 98 + .../cli/web/src/shell/ShellWizardFooter.tsx | 53 + .../cli/web/src/shell/StepShellLayout.tsx | 77 + .../web/src/shell/orchestratorShellBridge.tsx | 44 + packages/cli/web/src/shell/preflightProbe.ts | 122 + .../shell/report/AnalysisBlockCardLite.tsx | 75 + .../web/src/shell/report/ReportBlocksView.tsx | 1177 ++++++ .../src/shell/report/ReportFullJsonPanel.tsx | 65 + .../web/src/shell/report/ReportSurface.tsx | 95 + .../src/shell/report/WfaBlockChartsLite.tsx | 461 +++ .../src/shell/report/analysisBlockStyles.ts | 22 + .../src/shell/report/mapLocalReportToLite.ts | 173 + .../shell/report/reportDisplayNormalize.ts | 485 +++ packages/cli/web/src/shell/report/types.ts | 66 + .../cli/web/src/shell/report/useReportData.ts | 52 + packages/cli/web/src/shell/stepMeta.ts | 39 + packages/cli/web/src/shell/useShellWizard.ts | 235 ++ packages/cli/web/src/vite-env.d.ts | 1 + packages/cli/web/tailwind.config.cjs | 65 + packages/cli/web/tsconfig.json | 14 + packages/cli/web/vite.config.mjs | 44 + packages/contracts/README.md | 13 + packages/core/README.md | 17 +- packages/core/src/analyzeCardSummary.test.ts | 18 + packages/core/src/analyzeCardSummary.ts | 64 + .../buildTestResultDataFromUnified.test.ts | 21 +- .../src/buildTestResultDataFromUnified.ts | 21 +- packages/core/src/canonicalMetrics.test.ts | 29 + packages/core/src/canonicalMetrics.ts | 42 +- packages/core/src/decisionArtifacts.test.ts | 46 + packages/core/src/decisionArtifacts.ts | 59 +- .../core/src/freqtradeExportFixture.test.ts | 69 + packages/core/src/proBenchmarkMetrics.ts | 4 +- packages/core/src/riskNarratives.ts | 4 +- packages/core/src/server.ts | 2 +- packages/test-vectors/README.md | 13 + 85 files changed, 15373 insertions(+), 874 deletions(-) create mode 100644 docs/LOCAL_ORCHESTRATOR_UI_ARCHITECTURE.md create mode 100644 docs/LOCAL_UI_REPORT_CHIP_GRID.md create mode 100644 docs/LOCAL_UI_SAAS_BLOCK_MIGRATION_PLAN.md create mode 100644 docs/LOCAL_UI_TAILWIND_VITE.md create mode 100644 packages/cli/fixtures/saas-mock-paper-tiger-report.json create mode 100644 packages/cli/fixtures/saas-mock-report.json create mode 100644 packages/cli/src/commands/ui.ts create mode 100644 packages/cli/web/index.html create mode 100644 packages/cli/web/postcss.config.cjs create mode 100644 packages/cli/web/src/ApiErrorBanner.tsx create mode 100644 packages/cli/web/src/App.tsx create mode 100644 packages/cli/web/src/index.css create mode 100644 packages/cli/web/src/legacy-global.css create mode 100644 packages/cli/web/src/legacy-orchestrator.ts create mode 100644 packages/cli/web/src/legacy/api.ts create mode 100644 packages/cli/web/src/legacy/json.ts create mode 100644 packages/cli/web/src/legacy/mount.tsx create mode 100644 packages/cli/web/src/legacy/orchestrator-app.tsx create mode 100644 packages/cli/web/src/legacy/report-view.tsx create mode 100644 packages/cli/web/src/legacy/types.ts create mode 100644 packages/cli/web/src/legacy/wizard/CsvAnalyzePanel.tsx create mode 100644 packages/cli/web/src/legacy/wizard/IntegrationJobLogsPanel.tsx create mode 100644 packages/cli/web/src/legacy/wizard/KiploksWorkspacePanel.tsx create mode 100644 packages/cli/web/src/legacy/wizard/OrchestratorAppView.tsx create mode 100644 packages/cli/web/src/legacy/wizard/ReportsManagerPanel.tsx create mode 100644 packages/cli/web/src/legacy/wizard/StepPathPanel.tsx create mode 100644 packages/cli/web/src/legacy/wizard/WizardHeaderSection.tsx create mode 100644 packages/cli/web/src/legacy/wizard/integrationStepModel.ts create mode 100644 packages/cli/web/src/legacy/wizard/orchestratorUi.ts create mode 100644 packages/cli/web/src/legacy/wizard/useOrchestratorApp.ts create mode 100644 packages/cli/web/src/legacy/workflow.ts create mode 100644 packages/cli/web/src/main.tsx create mode 100644 packages/cli/web/src/shell/PreflightSummary.tsx create mode 100644 packages/cli/web/src/shell/ShellAppHeader.tsx create mode 100644 packages/cli/web/src/shell/ShellReportsStep.tsx create mode 100644 packages/cli/web/src/shell/ShellStepMainCard.tsx create mode 100644 packages/cli/web/src/shell/ShellWizardFooter.tsx create mode 100644 packages/cli/web/src/shell/StepShellLayout.tsx create mode 100644 packages/cli/web/src/shell/orchestratorShellBridge.tsx create mode 100644 packages/cli/web/src/shell/preflightProbe.ts create mode 100644 packages/cli/web/src/shell/report/AnalysisBlockCardLite.tsx create mode 100644 packages/cli/web/src/shell/report/ReportBlocksView.tsx create mode 100644 packages/cli/web/src/shell/report/ReportFullJsonPanel.tsx create mode 100644 packages/cli/web/src/shell/report/ReportSurface.tsx create mode 100644 packages/cli/web/src/shell/report/WfaBlockChartsLite.tsx create mode 100644 packages/cli/web/src/shell/report/analysisBlockStyles.ts create mode 100644 packages/cli/web/src/shell/report/mapLocalReportToLite.ts create mode 100644 packages/cli/web/src/shell/report/reportDisplayNormalize.ts create mode 100644 packages/cli/web/src/shell/report/types.ts create mode 100644 packages/cli/web/src/shell/report/useReportData.ts create mode 100644 packages/cli/web/src/shell/stepMeta.ts create mode 100644 packages/cli/web/src/shell/useShellWizard.ts create mode 100644 packages/cli/web/src/vite-env.d.ts create mode 100644 packages/cli/web/tailwind.config.cjs create mode 100644 packages/cli/web/tsconfig.json create mode 100644 packages/cli/web/vite.config.mjs create mode 100644 packages/core/src/decisionArtifacts.test.ts create mode 100644 packages/core/src/freqtradeExportFixture.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7196845..2eda9a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `buildPathMonteCarloSimulation` in `@kiploks/engine-core`: path-based i.i.d. bootstrap over equity period returns; CAGR and max drawdown distributions, labels, `interpretation`, `meta` (`PATH_MONTE_CARLO_METHOD_VERSION`, currently **1.1.0**). - `calculateCagrFromYears` in `financialMath.ts`. - Contract types in `@kiploks/engine-contracts`: `PathMonteCarloResult`, `PathMonteCarloOptions`, `PathMonteCarloEquityPoint`, `DistributionStats` (incl. optional `varCornishFisher95`), extended `PathMonteCarloMeta`. -- `packages/core/src/prng.ts` — shared `createMulberry32` (path MC, professional window bootstrap, WFE). +- `packages/core/src/prng.ts` - shared `createMulberry32` (path MC, professional window bootstrap, WFE). - `AnalyzeConfig.monteCarloBootstrapN` for precomputed `analyzeFromWindows` (professional `monteCarloValidation` iterations; default 1000, clamp 100-50000). - Docs: `MONTE_CARLO_PATH.md`, `MONTE_CARLO_SIMULATION_IMPLEMENTATION.md`, `examples/monte-carlo-example.md`, golden `examples/monte-carlo-seed42.json`, `npm run engine:examples:generate-monte-carlo-fixture`. Demo tab in `examples/result-layout-demo.html`. diff --git a/README.md b/README.md index 520056d..208f88a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,18 @@ # Kiploks Engine (Open Core) +## New: easier Freqtrade bot testing in UI + +You can now run Freqtrade bot tests directly from the web interface with much less setup friction. + +![Kiploks UI preview](https://kiploks.com/video/kiploks-ui-sm.gif) + +- Pick a specific backtest artifact from the list, or run in `Auto (top_n)` mode. +- Start integration runs from a cleaner Step 4 workspace with collapsible sections. +- Get report links in run logs after successful local runs, so you can open results right away. +- Report title handling is automatic and predictable when switching between artifact and `top_n` modes. + +Demo video: [kiploks-ui.mp3](https://kiploks.com/video/kiploks-ui.mp3) + [![npm](https://img.shields.io/npm/v/@kiploks/engine-core)](https://www.npmjs.com/package/@kiploks/engine-core) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) diff --git a/docs/ENTRYPOINTS.md b/docs/ENTRYPOINTS.md index c6abaf6..ddaceca 100644 --- a/docs/ENTRYPOINTS.md +++ b/docs/ENTRYPOINTS.md @@ -148,7 +148,7 @@ Optional **`equityCurve`** on the input can help unlock benchmark-related paths ## Troubleshooting: `available: false` and warnings -A block with **`available: false`** is **not a crash** — it means the engine **refuses to fake** that block because required inputs are missing. Read **`reason`** on the block (`KiploksUnavailableReason` in [`packages/contracts/src/errors.ts`](../packages/contracts/src/errors.ts)). +A block with **`available: false`** is **not a crash** - it means the engine **refuses to fake** that block because required inputs are missing. Read **`reason`** on the block (`KiploksUnavailableReason` in [`packages/contracts/src/errors.ts`](../packages/contracts/src/errors.ts)). For **behavioral cautions** (low trade count, low window count, weak p-value, etc.), see **`warnings[]`** on the result and the full **[`ERROR_CATALOG.md`](ERROR_CATALOG.md)**. @@ -167,8 +167,8 @@ When contracts change, **this file can go stale**. Prefer: ## See also -- **[`OPEN_CORE_INTEGRATION_PRINCIPLES.md`](OPEN_CORE_INTEGRATION_PRINCIPLES.md)** — design principles and `runEverything()`. -- **[`examples-map-payload-to-unified.md`](examples-map-payload-to-unified.md)** — `mapPayloadToUnified` and CSV-first flow. -- **[`examples/README.md`](examples/README.md)** — step-by-step examples. -- **[`ERROR_CATALOG.md`](ERROR_CATALOG.md)** — warning and error codes. -- **[`MONTE_CARLO_SIMULATION_IMPLEMENTATION.md`](MONTE_CARLO_SIMULATION_IMPLEMENTATION.md)** — path MC vs window bootstrap index. +- **[`OPEN_CORE_INTEGRATION_PRINCIPLES.md`](OPEN_CORE_INTEGRATION_PRINCIPLES.md)** - design principles and `runEverything()`. +- **[`examples-map-payload-to-unified.md`](examples-map-payload-to-unified.md)** - `mapPayloadToUnified` and CSV-first flow. +- **[`examples/README.md`](examples/README.md)** - step-by-step examples. +- **[`ERROR_CATALOG.md`](ERROR_CATALOG.md)** - warning and error codes. +- **[`MONTE_CARLO_SIMULATION_IMPLEMENTATION.md`](MONTE_CARLO_SIMULATION_IMPLEMENTATION.md)** - path MC vs window bootstrap index. diff --git a/docs/LOCAL_ORCHESTRATOR_UI_ARCHITECTURE.md b/docs/LOCAL_ORCHESTRATOR_UI_ARCHITECTURE.md new file mode 100644 index 0000000..27aad37 --- /dev/null +++ b/docs/LOCAL_ORCHESTRATOR_UI_ARCHITECTURE.md @@ -0,0 +1,509 @@ +# Local Orchestrator + UI Shell Architecture + +This document describes a practical architecture where a local backend process orchestrates all heavy operations, and UI remains a control shell for non-technical users. + +## 1. Goal and scope + +Goal: +- Run a local service process from Engine-side tooling. +- Expose a simple UI for: + - CSV upload and analysis using Engine adapters. + - Integration bootstrap and execution for external bot repositories (`freqtrade`, `octobot`). + - Status tracking, logs, and result download. + +Scope: +- Local-first workflow for a single user machine. +- No direct browser access to arbitrary file system paths. +- All privileged operations (filesystem, process execution, env checks) go through the local orchestrator backend. + +Out of scope: +- Full cloud orchestration. +- Remote multi-tenant execution. +- Browser-only direct execution of Python integrations. + +## 2. Why this architecture is required + +Pure browser UI cannot reliably: +- access arbitrary local directories without strict user-mediated handles; +- spawn/manage long-running local Python or Docker jobs; +- enforce robust preflight checks against local dependencies; +- provide stable retry/recovery semantics for integration execution. + +Therefore, UI must be a shell, while a local backend process performs operational work. + +## 3. High-level architecture + +Components: +- `UI Shell` (desktop web UI): job creation, configuration editing, progress, errors, logs, results. +- `Local Orchestrator Backend` (new local process): authoritative execution layer. +- `Engine Runtime` (`@kiploks/engine-core`, `@kiploks/engine-adapters`): CSV parse + analysis. +- `Integration Runners` (`freqtrade`, `octobot` external folders): optional Python/Docker execution targets. + +Flow: +1. User opens UI and configures local paths. +2. UI sends commands to local orchestrator API. +3. Orchestrator validates environment and paths. +4. Orchestrator either: + - runs CSV analysis directly through Engine packages, or + - prepares and launches integration run in selected external repository. +5. Orchestrator streams status/logs and stores outputs. +6. UI renders report and actionable diagnostics. + +## 4. Backend responsibilities (must-have) + +The local orchestrator backend is responsible for: +- local filesystem access and path validation; +- installation/bootstrap of integration helper folder into external repo path; +- default config materialization (`kiploks.json` from template); +- process execution (`python`, `docker compose`, helper scripts); +- job queue and state machine (queued/running/succeeded/failed/cancelled); +- log capture and structured error classification; +- preflight checks (toolchain, env vars, permissions, directory layout); +- safe cancellation and cleanup. + +The UI must not bypass these responsibilities. + +## 5. Core API surface for UI + +Recommended API groups (local HTTP, loopback only): +- `POST /preflight/check` -> environment diagnostics. +- `GET /preflight/result` -> return last preflight result without re-running checks. +- `GET /paths` -> return saved integration paths and metadata. +- `POST /paths/register` -> save/validate `freqtrade` and `octobot` paths. +- `DELETE /paths/:integration` -> remove one saved integration path. +- `POST /csv/analyze` -> parse CSV and run engine analysis. +- `POST /integrations/bootstrap` -> install/update integration helper files. +- `POST /integrations/run` -> start integration job. +- `GET /jobs` -> paginated job history for UI. +- `GET /jobs/:id` -> status, step, progress. +- `GET /jobs/:id/logs` -> paginated logs (polling endpoint). +- `GET /jobs/:id/events` -> SSE stream for real-time step/progress/log updates. +- `GET /jobs/:id/result` -> analysis output / artifact metadata. +- `POST /jobs/:id/cancel` -> cooperative stop. + +Security baseline: +- bind only to `127.0.0.1`; +- ephemeral local token/session for UI calls; +- strict path allowlist rooted in user-approved directories. + +## 6. Data and config model + +Persist locally: +- user-selected repository paths; +- default integration config snapshots; +- last successful preflight result; +- job metadata and artifacts index; +- optional command history for troubleshooting. + +Configuration policy: +- never overwrite user integration config silently; +- use explicit "create default", "merge", or "replace" actions; +- keep generated config versioned with schema to support migrations. + +Minimum integration config lifecycle: +- include `schema_version`, `engine_version`, `integration_type`, `last_migrated_at`; +- store source-of-truth marker (`managed_by: "kiploks-orchestrator"`). +- expose explicit API actions: + - `POST /integrations/config/reset-defaults` + - `POST /integrations/config/merge` + - `POST /integrations/config/replace` + +Conflict resolution policy: +- if config file changed outside UI since last orchestrator write (mtime/hash mismatch), backend returns conflict state; +- UI must present 3-way decision: keep file, apply UI draft, or merge preview; +- default is no-write until user confirms. + +## 7. Discovery and startup UX (first run) + +Local startup contract: +- primary command: `kiploks ui` (alias) starting local orchestrator and UI shell. +- fallback command: `kiploks serve --ui` (non-interactive environments). + +Startup behavior: +- on launch, orchestrator probes preferred port (for example `:41731`); +- if occupied, tries bounded fallback range (for example `+1...+20`); +- if no free port found, exits with actionable error and occupied PID list (when available); +- optional auto-open browser on first run (`--open` default true for interactive terminals). + +Shutdown behavior: +- `Ctrl+C` triggers graceful stop: reject new jobs, cancel/terminate running subprocesses, flush state; +- hard timeout fallback kills orphan child processes after grace window; +- optional background mode (`kiploks ui --daemon`) with explicit `kiploks stop`. + +First-run flow: +1. Start local service and open UI. +2. Show preflight wizard (python/docker/path permissions/env). +3. Ask user to select integration repo paths. +4. Offer bootstrap dry-run before any write. +5. Allow first CSV analysis even if integrations are not configured. + +## 8. Integration execution contracts (Phase 3 design anchor) + +The orchestrator must treat integration execution as adapter contracts, not ad-hoc shell calls. + +Freqtrade runner contract: +- command strategy: + - default docker mode: invoke repository-provided wrapper (for example `./kiploks-freqtrade/run-in-docker.sh`); + - fallback host mode: `python kiploks-freqtrade/run.py` (if validated by preflight). +- expected machine-readable output: + - preferred: JSON artifact written to known output path; + - fallback: structured stdout markers (`KIPLOKS_JOB_STATUS`, `KIPLOKS_RESULT_PATH`). +- success criteria: + - process exit code `0`; + - required output artifact exists and passes schema validation. +- failure criteria: + - non-zero exit code, missing artifact, invalid artifact schema, timeout. + +OctoBot runner contract: +- command strategy: + - default host mode: `python kiploks-octobot/run.py`; + - optional wrapper mode: `./kiploks-octobot/run.sh`. +- expected output and success/failure criteria mirror Freqtrade contract. + +Mandatory backend output normalization: +- map runner-specific outputs into unified job result: + - `raw_artifact_path` + - `normalized_payload` + - `analysis_summary` + - `error_code` + `error_context` for failures + +## 9. CSV analysis response contract (`POST /csv/analyze`) + +The endpoint must return a stable shape for UI rendering, including partial/invalid-row handling. + +Response sections: +- `meta`: file name, delimiter, total rows, parsed rows, invalid rows. +- `validation`: list of row-level issues (row index, code, message, severity). +- `analysis_blocks`: keyed block results from engine (for example summary, risk, stability, verdict). +- `warnings`: non-fatal engine or adapter warnings. +- `artifacts`: normalized JSON output path/hash for reproducibility. + +Partial-result behavior: +- for large files, backend may stream progress via `GET /jobs/:id/events`; +- UI can render completed blocks incrementally when each block is marked `ready`. + +Invalid CSV behavior: +- if invalid row ratio under configured threshold, proceed with partial parse and return warnings; +- if ratio above threshold or mandatory columns missing, return hard validation error with fix hints. + +## 10. Integration versioning and upgrade policy + +Versioning policy for integration helper folders: +- each generated helper contains manifest (for example `.kiploks-integration-manifest.json`) with: + - `engine_version` + - `template_version` + - `generated_at` + - `files_checksum` + +Upgrade detection: +- on every `POST /integrations/bootstrap`, orchestrator compares manifest + checksums with current template set; +- if outdated, backend returns `upgrade_available` state and migration actions. + +Upgrade strategies: +- `safe-merge` (default): patch managed files only, preserve user custom files; +- `replace-managed`: replace only files declared as managed in manifest; +- `full-reinstall`: explicit destructive action requiring user confirmation. + +User visibility: +- UI shows version badge: `Installed vX / Latest vY`; +- stale integrations are flagged before run and can be blocked by policy. + +## 11. Engine/CLI compatibility checks + +Compatibility checks must run before bootstrap and run: +- detect local CLI version vs template compatibility matrix; +- detect engine package version expected by installed integration helper; +- warn or hard-stop on known incompatible combinations. + +Minimum enforcement: +- major version mismatch => hard stop; +- minor mismatch => warning with guided upgrade; +- patch mismatch => allow by default. + +## 12. Multiplatform path handling + +Path normalization rules: +- normalize separators (`\` and `/`) to platform-safe internal representation; +- preserve Windows drive letters and UNC paths; +- resolve symlinks and canonical real paths before allowlist checks; +- reject path traversal attempts after normalization. + +Runner portability: +- never build commands via raw string concatenation of user paths; +- pass paths as structured args to process spawn APIs; +- store both `display_path` (UI-friendly) and `canonical_path` (execution-safe). + +Windows-specific checks: +- path length and reserved filename constraints; +- executable resolution for `python`/`py` and shell wrappers; +- consistent quoting rules for spaces in repository paths. + +## 13. Stopper matrix (critical constraints) + +### A) Hard stop (cannot proceed automatically) +- Missing Python runtime required by integration. +- Missing Docker runtime when selected execution mode requires Docker. +- Invalid selected path (not bot repo root or missing required subfolders). +- Missing mandatory auth/env required for upload/remote API steps. +- OS permission denial for writing integration files. + +### B) Soft stop (can proceed with degraded mode) +- Optional Playwright/e2e checks unavailable. +- Self-signed local certificates not trusted (UI warning + fallback URL guidance). +- Partial metric payload where engine can still compute subset blocks. + +### C) Known architectural limits +- No fully reliable browser-only flow for integration bootstrap/run. +- Cross-machine reproducibility is constrained by local toolchain versions. +- Long-running job reliability requires explicit restart/recovery strategy in orchestrator. + +## 14. What must not be promised to users + +Do not promise: +- "one-click works everywhere" without environment preparation; +- identical results across all machines with different Python/Docker/tool versions; +- fully hands-off setup for arbitrary external repositories with unknown customizations. + +Must state clearly: +- first run includes local preflight and setup; +- integration execution depends on external repo health and local environment; +- UI simplifies operations but does not remove external dependency constraints. + +## 15. Implementation plan (phased) + +Phase 0 - Foundation: +- define job model and API contracts; +- implement preflight engine (python/docker/path/env checks); +- implement secure local server startup lifecycle. + +Phase 1 - CSV-first value: +- implement `csv/analyze` pipeline with adapters; +- return structured analysis and warnings; +- add UI for upload + report rendering + diagnostics. + +Phase 2 - Integration bootstrap: +- support selecting `freqtrade` / `octobot` repo paths; +- materialize integration folder and default config from templates; +- add dry-run mode to show intended filesystem changes. + +Phase 3 - Integration execution: +- run integration commands via orchestrator; +- stream logs and status; +- persist artifacts and provide rerun from previous config. + +Phase 4 - Hardening: +- robust cancellation/timeout policies; +- crash recovery and orphan process detection; +- reproducibility report (tool versions, command hash, config hash). + +## 16. Operational guardrails + +Required before each integration run: +- preflight freshness window (for example, last 24h) or force recheck; +- path integrity revalidation (repo markers); +- config schema validation; +- disk-space and write-permission checks for output directories. + +Observability: +- structured logs by job step (`preflight`, `bootstrap`, `run`, `collect`, `analyze`); +- stable error codes for UI mapping; +- local diagnostic bundle export for support. + +## 17. Decision summary + +Is this approach feasible? +- Yes. It is the technically correct path for "simple UI over complex local integrations". + +Primary risk concentration: +- local environment variability (python/docker/paths/permissions); +- external repository conventions drift (`freqtrade`, `octobot`); +- process orchestration reliability. + +Risk mitigation: +- strict preflight gating; +- explicit bootstrap semantics with dry-run; +- stable job orchestration and diagnostics-first UX. + +# Local Orchestrator + UI Shell Architecture + +This document describes a practical architecture where a local backend process orchestrates all heavy operations, and UI remains a control shell for non-technical users. + +## 1. Goal and scope + +Goal: +- Run a local service process from Engine-side tooling. +- Expose a simple UI for: + - CSV upload and analysis using Engine adapters. + - Integration bootstrap and execution for external bot repositories (`freqtrade`, `octobot`). + - Status tracking, logs, and result download. + +Scope: +- Local-first workflow for a single user machine. +- No direct browser access to arbitrary file system paths. +- All privileged operations (filesystem, process execution, env checks) go through the local orchestrator backend. + +Out of scope: +- Full cloud orchestration. +- Remote multi-tenant execution. +- Browser-only direct execution of Python integrations. + +## 2. Why this architecture is required + +Pure browser UI cannot reliably: +- access arbitrary local directories without strict user-mediated handles; +- spawn/manage long-running local Python or Docker jobs; +- enforce robust preflight checks against local dependencies; +- provide stable retry/recovery semantics for integration execution. + +Therefore, UI must be a shell, while a local backend process performs operational work. + +## 3. High-level architecture + +Components: +- `UI Shell` (desktop web UI): job creation, configuration editing, progress, errors, logs, results. +- `Local Orchestrator Backend` (new local process): authoritative execution layer. +- `Engine Runtime` (`@kiploks/engine-core`, `@kiploks/engine-adapters`): CSV parse + analysis. +- `Integration Runners` (`freqtrade`, `octobot` external folders): optional Python/Docker execution targets. + +Flow: +1. User opens UI and configures local paths. +2. UI sends commands to local orchestrator API. +3. Orchestrator validates environment and paths. +4. Orchestrator either: + - runs CSV analysis directly through Engine packages, or + - prepares and launches integration run in selected external repository. +5. Orchestrator streams status/logs and stores outputs. +6. UI renders report and actionable diagnostics. + +## 4. Backend responsibilities (must-have) + +The local orchestrator backend is responsible for: +- local filesystem access and path validation; +- installation/bootstrap of integration helper folder into external repo path; +- default config materialization (`kiploks.json` from template); +- process execution (`python`, `docker compose`, helper scripts); +- job queue and state machine (queued/running/succeeded/failed/cancelled); +- log capture and structured error classification; +- preflight checks (toolchain, env vars, permissions, directory layout); +- safe cancellation and cleanup. + +The UI must not bypass these responsibilities. + +## 5. Core API surface for UI + +Recommended API groups (local HTTP, loopback only): +- `POST /preflight/check` -> environment diagnostics. +- `POST /paths/register` -> save/validate `freqtrade` and `octobot` paths. +- `POST /csv/analyze` -> parse CSV and run engine analysis. +- `POST /integrations/bootstrap` -> install/update integration helper files. +- `POST /integrations/run` -> start integration job. +- `GET /jobs/:id` -> status, step, progress. +- `GET /jobs/:id/logs` -> structured logs/stream. +- `GET /jobs/:id/result` -> analysis output / artifact metadata. +- `POST /jobs/:id/cancel` -> cooperative stop. + +Security baseline: +- bind only to `127.0.0.1`; +- ephemeral local token/session for UI calls; +- strict path allowlist rooted in user-approved directories. + +## 6. Data and config model + +Persist locally: +- user-selected repository paths; +- default integration config snapshots; +- last successful preflight result; +- job metadata and artifacts index; +- optional command history for troubleshooting. + +Configuration policy: +- never overwrite user integration config silently; +- use explicit "create default", "merge", or "replace" actions; +- keep generated config versioned with schema to support migrations. + +## 7. Stopper matrix (critical constraints) + +### A) Hard stop (cannot proceed automatically) +- Missing Python runtime required by integration. +- Missing Docker runtime when selected execution mode requires Docker. +- Invalid selected path (not bot repo root or missing required subfolders). +- Missing mandatory auth/env required for upload/remote API steps. +- OS permission denial for writing integration files. + +### B) Soft stop (can proceed with degraded mode) +- Optional Playwright/e2e checks unavailable. +- Self-signed local certificates not trusted (UI warning + fallback URL guidance). +- Partial metric payload where engine can still compute subset blocks. + +### C) Known architectural limits +- No fully reliable browser-only flow for integration bootstrap/run. +- Cross-machine reproducibility is constrained by local toolchain versions. +- Long-running job reliability requires explicit restart/recovery strategy in orchestrator. + +## 8. What must not be promised to users + +Do not promise: +- "one-click works everywhere" without environment preparation; +- identical results across all machines with different Python/Docker/tool versions; +- fully hands-off setup for arbitrary external repositories with unknown customizations. + +Must state clearly: +- first run includes local preflight and setup; +- integration execution depends on external repo health and local environment; +- UI simplifies operations but does not remove external dependency constraints. + +## 9. Implementation plan (phased) + +Phase 0 - Foundation: +- define job model and API contracts; +- implement preflight engine (python/docker/path/env checks); +- implement secure local server startup lifecycle. + +Phase 1 - CSV-first value: +- implement `csv/analyze` pipeline with adapters; +- return structured analysis and warnings; +- add UI for upload + report rendering + diagnostics. + +Phase 2 - Integration bootstrap: +- support selecting `freqtrade` / `octobot` repo paths; +- materialize integration folder and default config from templates; +- add dry-run mode to show intended filesystem changes. + +Phase 3 - Integration execution: +- run integration commands via orchestrator; +- stream logs and status; +- persist artifacts and provide rerun from previous config. + +Phase 4 - Hardening: +- robust cancellation/timeout policies; +- crash recovery and orphan process detection; +- reproducibility report (tool versions, command hash, config hash). + +## 10. Operational guardrails + +Required before each integration run: +- preflight freshness window (for example, last 24h) or force recheck; +- path integrity revalidation (repo markers); +- config schema validation; +- disk-space and write-permission checks for output directories. + +Observability: +- structured logs by job step (`preflight`, `bootstrap`, `run`, `collect`, `analyze`); +- stable error codes for UI mapping; +- local diagnostic bundle export for support. + +## 11. Decision summary + +Is this approach feasible? +- Yes. It is the technically correct path for "simple UI over complex local integrations". + +Primary risk concentration: +- local environment variability (python/docker/paths/permissions); +- external repository conventions drift (`freqtrade`, `octobot`); +- process orchestration reliability. + +Risk mitigation: +- strict preflight gating; +- explicit bootstrap semantics with dry-run; +- stable job orchestration and diagnostics-first UX. + diff --git a/docs/LOCAL_UI_REPORT_CHIP_GRID.md b/docs/LOCAL_UI_REPORT_CHIP_GRID.md new file mode 100644 index 0000000..5b86899 --- /dev/null +++ b/docs/LOCAL_UI_REPORT_CHIP_GRID.md @@ -0,0 +1,73 @@ +# Local UI report: chip grid for key-value sections + +## Goal + +Show dense scalar blocks in the local orchestrator report UI as a readable 2-column grid of cards (label + pill-shaped value), instead of a flat bullet list. This matches the layout introduced for `Robustness modules` and is now reused for other report sections. + +## Where it lives + +- **Orchestrator + report client**: `engine/packages/cli/web/src/legacy-orchestrator.ts` (Vite bundle, ESM React). +- **Report / orchestrator scoped CSS**: `engine/packages/cli/web/src/legacy-global.css` (`@scope (#legacy-root)` so Tailwind shell classes stay separate). +- **HTTP**: `engine/packages/cli/src/commands/ui.ts` serves `dist/web/` for `GET /ui/` and assets. + +See also `engine/docs/LOCAL_UI_TAILWIND_VITE.md` for the step shell and build pipeline. + +## CSS building blocks + +| Class | Role | +| --- | --- | +| `robust-grid` | 2-column CSS grid for cards | +| `robust-item` | One field: label + value | +| `robust-chip` | Pill container for the formatted value | +| `robust-chip-good` / `robust-chip-warn` / `robust-chip-bad` / `robust-chip-neutral` | Semantic color for the pill | +| `robust-flag-chips` / `robust-flag-chip` | Wrapped row of neutral pills for long flag lists | +| `robust-item .robust-chip` | Allows long strings (for example DQG diagnosis) to wrap | + +## JavaScript helpers (inside `ReportDetails`) + +1. **`chipToneForKv(label, raw)`** + Picks a chip color from the raw value and the field label (lowercased path for nested keys). Heuristics cover: + + - Robustness blockers: `blockedByModule`, `blockedByModules` + - Booleans: negative-sounding keys treat `true` as bad (`blocked`, `failure`, …); deployment-style keys treat `true` as good + - Numbers in `[0, 1]`: green / amber / red thresholds (same idea as robustness module scores) + - Numbers in `(1, 100]`: scaled as `/100` for the same thresholds (useful for scores shown as `0-100`) + - Drawdown-style negatives: more negative than about `-20%` reads as bad (label matches `drawdown`, `mdd`, …) + - Short strings: common verdict tokens (`PASS`, `FAIL`, …) without regex word-boundary escapes (those would break inside the outer template literal that emits `app.js`) + +2. **`chipGridItems(pairs, keyPrefix)`** + Takes `[label, raw][]`, runs `compactRows`, formats with existing `formatCell`, renders `robust-item` nodes. Keys use `keyPrefix` to stay stable in lists. + +3. **`kvChipGridSection(title, pairs, keyPrefix)`** + One `report-section` with an `h3` and a single `robust-grid` of items. + +4. **`robustnessSection()`** + Still the only section with **two** titled sub-blocks (`Module gates` and `Components`); it now uses `chipGridItems` so tone logic stays centralized. + +## Sections using the chip grid + +| Section | Data source | +| --- | --- | +| Robustness modules | `rob.modules`, `rob.*`, `rob.components.*` | +| Risk (all scalars) | `collectPrimitives` on `riskAnalysis` (+ `risk.metrics`) | +| Pro benchmark metrics | `collectPrimitives` on `proBenchmarkMetrics` | +| Parameter sensitivity - diagnostics | `paramSensitivity.diagnostics` | +| Parameter sensitivity - top parameters | First parameters + `.sensitivity` | +| Data quality (DQG) | `dataQualityGuardResult` / `dataQualityGuard` scalar fields | +| DQG modules | `dqg.modules[]` - card per module: name, verdict chip, optional truncated `details` JSON | +| Decision summary | `decisionSummary` scalars in the same grid; positive/risk flag tiers use `robust-flag-chips` | +| Turnover & cost drag | `turnoverAndCostDrag` via `collectPrimitives` | + +Sections that stay as before (list or table) include `Strategy`, `Walk-forward`, `Benchmark`, canonical tables, WFA window table, and so on. + +## Edge cases + +- **Empty section**: `compactRows` removes null/empty values; if nothing remains, `kvChipGridSection` returns `null` and nothing is rendered. +- **Very long labels**: paths from `collectPrimitives` stay in the left column; values wrap inside the chip. +- **Template literal safety**: regexes inside `renderUiAppJs()` must not use escapes that the outer template eats (for example `\b`). Prefer explicit string equality or simple alternation regexes. + +## How to extend + +1. Add CSS only in `renderUiHtml()` if you need new visual variants (keep namespaced under `robust-` or a new prefix). +2. Add or adjust heuristics in `chipToneForKv` when new boolean or verdict fields need clearer coloring. +3. For new scalar dumps, prefer `kvChipGridSection("Title", rows, "uniquePrefix")` if rows are `[label, value]` pairs. diff --git a/docs/LOCAL_UI_SAAS_BLOCK_MIGRATION_PLAN.md b/docs/LOCAL_UI_SAAS_BLOCK_MIGRATION_PLAN.md new file mode 100644 index 0000000..eaf2d3d --- /dev/null +++ b/docs/LOCAL_UI_SAAS_BLOCK_MIGRATION_PLAN.md @@ -0,0 +1,242 @@ +# План миграции SaaS-блоков в Local UI + +## Цель + +Перенести в Step 7 (Reports) локального оркестратора блоки анализа в стиле SaaS, при этом сохранить текущий legacy-рендер отчета как безопасный fallback. + +Критерии успеха: +- Step 7 показывает структурированные блоки анализа с понятными заголовками и компактной визуализацией. +- Отсутствующие данные не ломают страницу - неподдерживаемые блоки пропускаются с понятной причиной. +- Legacy-рендер остается доступным до подтвержденного паритета. + +## Область работ + +Входит в работу: +- Поверхность отчета Step 7 в `engine/packages/cli/web`. +- Блочный рендер секций анализа. +- Адаптер из локального report payload в типизированный блоковый input. + +Не входит в работу (для этой ветки миграции): +- Переписывание UI шагов 1-6. +- Изменение логики генерации отчетов в engine-core. +- Полный SSR slot паритет с Next.js SaaS реализацией. + +## Текущий baseline + +- Список отчетов и выбор отчета в Step 7 находятся в `web/src/shell/ShellReportsStep.tsx`. +- Полные детали отчета сейчас рендерит legacy-монолит `web/src/legacy/report-view.tsx`. +- Backend routes обслуживаются из `packages/cli/src/commands/ui.ts`: + - `GET /api/reports` + - `GET /api/reports/:id` +- Shape детального ответа локального отчета - `LocalReport` с `report: unknown` и `rawPayload: unknown`. + +## Целевая архитектура + +### `ReportSurface` (новый компонент) + +Единая входная точка для Step 7: +- Input: `reportId`. +- Ответственности: + - загрузить данные один раз, + - смэппить payload в типизированную блоковую модель, + - отрендерить современный набор блоков, + - переключиться на legacy-рендер при необходимости. + +Планируемый файл: +- `web/src/shell/report/ReportSurface.tsx` + +### Data flow + +1. `ShellReportsStep` выбирает `reportId`. +2. `ReportSurface` запрашивает `/api/reports/:id`. +3. Mapper преобразует `LocalReport.report` -> `TestResultDataLite`. +4. Матрица доступности блоков решает, какие блоки рендерить. +5. Если критичный мэппинг не удался, показывается legacy-рендер с предупреждением. + +## Предлагаемые интерфейсы + +### `ReportSurfaceProps` + +```ts +export type ReportSurfaceProps = { + reportId: string; + mode?: "auto" | "blocks" | "legacy"; + showDebugMeta?: boolean; +}; +``` + +Правила: +- `auto` (по умолчанию): сначала пробуем blocks, затем fallback на legacy. +- `blocks`: рендерим только блоки, при нехватке данных показываем нефатальные placeholders. +- `legacy`: принудительно старый рендер. + +### `TestResultDataLite` + +Минимальный контракт, достаточный для первых волн миграции: + +```ts +export type TestResultDataLite = { + strategy?: { + name?: string; + symbol?: string; + timeframe?: string; + exchange?: string; + }; + decisionSummary?: { + verdict?: string; + confidence?: number; + riskLevel?: string; + deploymentReadiness?: boolean; + }; + verdictPayload?: Record | null; + robustnessScore?: { + overall?: number; + components?: { + parameterStability?: number; + timeRobustness?: number; + marketRegime?: number; + monteCarloStability?: number; + sensitivity?: number; + }; + modules?: Record; + } | null; + dataQualityGuardResult?: Record | null; + benchmarkComparison?: Record | null; + proBenchmarkMetrics?: Record | null; + walkForwardAnalysis?: Record | null; + parameterSensitivity?: Record | null; + turnoverAndCostDrag?: Record | null; + riskAnalysis?: Record | null; + strategyActionPlan?: Record | null; +}; +``` + +## Матрица блоков (обязательные и опциональные поля) + +| Блок | Обязательные поля | Опциональные поля | Поведение fallback | +| - | - | - | - | +| Final Verdict | `verdictPayload` OR `decisionSummary.verdict` | confidence, riskLevel | компактная verdict-карточка из `decisionSummary` | +| Robustness Score | `robustnessScore.overall` | components, modules | показываем только overall | +| Data Quality Guard | `dataQualityGuardResult` | module breakdown | badge + краткое сообщение | +| Benchmark Metrics | `proBenchmarkMetrics` OR `benchmarkComparison` | interpretive text | показываем доступный вариант | +| Walk-Forward | `walkForwardAnalysis` | professional/heavy части | скрываем недоступные subsections | +| Parameter Sensitivity | `parameterSensitivity` | diagnostics | только summary списка параметров | +| Turnover and Cost Drag | `turnoverAndCostDrag` | дополнительные подсказки | только key metrics | +| Risk Metrics | `riskAnalysis` | qualitative notes | только scalar metrics | +| Strategy Action Plan | `strategyActionPlan` OR контекст из decisionSummary | детальные действия | базовый план выводим из verdict | + +## Правила доступности + +Каждый блок получает machine-readable статус: + +```ts +type BlockStatus = + | { state: "ready" } + | { state: "partial"; reason: string } + | { state: "missing"; reason: string }; +``` + +Правила: +- `ready`: рендерим полный блок. +- `partial`: рендерим компактный блок с предупреждением о частичных данных. +- `missing`: скрываем блок в обычном режиме, показываем placeholder в debug режиме. + +## План миграции по PR + +## PR1 - Основание и безопасная обертка + +Deliverables: +- Добавить `ReportSurface` с переключением режима и fetch lifecycle. +- Добавить mapper `mapLocalReportToLite`. +- Сохранить legacy как output по умолчанию в `auto` режиме. +- Добавить telemetry logs по статусу мэппинга в dev console. + +Планируемые файлы: +- `web/src/shell/report/ReportSurface.tsx` +- `web/src/shell/report/useReportData.ts` +- `web/src/shell/report/mapLocalReportToLite.ts` +- `web/src/shell/ShellReportsStep.tsx` (переключение на `ReportSurface`) + +Acceptance: +- Нет регрессий в Step 7. +- Legacy output не меняется при провале мэппинга. + +## PR2 - Первый набор блоков (максимальная ценность, низкий риск) + +Deliverables: +- Реализовать рендер блоков: + - Final Verdict + - Robustness Score + - Data Quality Guard +- Добавить section container и spacing, согласованные с shell design. +- Показать legacy-рендер ниже блоков в collapsible секции "Full legacy report". + +Планируемые файлы: +- `web/src/shell/report/blocks/FinalVerdictBlock.tsx` +- `web/src/shell/report/blocks/RobustnessScoreBlock.tsx` +- `web/src/shell/report/blocks/DataQualityGuardBlock.tsx` +- `web/src/shell/report/ReportBlocksView.tsx` + +Acceptance: +- Выбранные отчеты (включая demo fixtures) корректно показывают эти блоки. +- Существующие deep links `#report=` продолжают работать. + +## PR3 - Расширенные блоки анализа + путь к deprecation + +Deliverables: +- Добавить оставшиеся блоки: + - Benchmark + - Walk-Forward + - Parameter Sensitivity + - Turnover and Cost Drag + - Risk + - Strategy Action Plan +- Добавить diagnostics-панель доступности блоков (опциональный debug toggle). +- Пометить legacy-рендер как fallback-only в code comments. + +Acceptance: +- Покрытие блоков достигает паритета для типовых report payloads. +- Legacy view остается доступным для edge payloads. + +## Риски и меры снижения + +- Риск: расхождение payload shape между локальными отчетами и ожиданиями SaaS. + - Мера: mapper со строгими guards + partial states. +- Риск: большой payload компонентов замедляет Step 7. + - Мера: memoized mapping и lazy mount для тяжелых блоков. +- Риск: визуальная несогласованность с shell design. + - Мера: использовать shell Tailwind tokens и не использовать legacy scoped styles для новых блоков. +- Риск: скрытые регрессии на редких shape отчетов. + - Мера: оставить legacy fallback и добавить проверки на fixtures. + +## Стратегия тестирования + +Функциональные проверки: +- Открыть Step 7 и быстро переключать несколько отчетов. +- Проверить все режимы: `auto`, `blocks`, `legacy`. +- Проверить deep link поведение с `#report=`. + +Проверки shape данных: +- Использовать существующие fixture отчеты: + - `packages/cli/fixtures/saas-mock-report.json` + - `packages/cli/fixtures/saas-mock-paper-tiger-report.json` +- Добавить один намеренно sparse fixture с отсутствующими полями. + +Regression checks: +- `npm run build:web` +- Manual smoke по навигации Steps 1-7. + +## Decision gates rollout + +Gate 1 (после PR1): +- Нет UX регрессий, fallback подтвержден. + +Gate 2 (после PR2): +- Первый набор блоков корректен на всех demo fixtures. + +Gate 3 (после PR3): +- Расширенные блоки стабильны; legacy можно визуально понизить в приоритете, но пока не удалять. + +## Опциональный follow-up + +После стабилизации PR3 оценить вынос shared block primitives из SaaS в переиспользуемые package(s), но только после стабилизации локального контракта и модели доступности. diff --git a/docs/LOCAL_UI_TAILWIND_VITE.md b/docs/LOCAL_UI_TAILWIND_VITE.md new file mode 100644 index 0000000..a807f2d --- /dev/null +++ b/docs/LOCAL_UI_TAILWIND_VITE.md @@ -0,0 +1,62 @@ +# Local UI: Vite + React + Tailwind + +## Goal + +Move the local orchestrator browser UI from an inline JavaScript string in `ui.ts` to a **bundled** React app with **Tailwind** (single design source), while keeping the existing orchestrator and report screens under a **step shell** so the first screens stay light. + +## Layout + +- **Shell** (`web/src/App.tsx`): Kiploks-style dark header, **left step sidebar** (active / done / pending), **right pane** with copy and actions. +- **Steps today** + 1. Overview - short copy. + 2. Preparation - optional probe `GET /preflight/result`; failures use `ApiErrorBanner` with HTTP status, parsed or raw body, and stack when available. + 3. Orchestrator - full legacy UI mounted into `#legacy-root` inside a scroll container (same behavior as before the migration). + +Back / Next behave as agreed: **Back** is allowed (clears the last preflight error when leaving step 2). **Next** on step 2 is disabled because the orchestrator is the final workspace. + +## Build + +From the engine repo: + +```bash +npm run build -w @kiploks/engine-cli +``` + +This runs `vite build --config web/vite.config.mjs` (output under `packages/cli/dist/web/`) then `tsc`. + +## Server routes + +- `GET /` redirects to `/ui/`. +- `GET /ui` redirects to `/ui/`. +- `GET /ui/` serves `dist/web/index.html`. +- `GET /ui/assets/*` serves hashed Vite assets. + +Implementation: `tryServeUiWebRequest` in `packages/cli/src/commands/ui.ts`. + +## Source layout + +| Path | Role | +| --- | --- | +| `packages/cli/web/vite.config.mjs` | Vite root `web/`, `base: "/ui/"`, `outDir` = `../dist/web` | +| `packages/cli/web/tailwind.config.cjs` | Theme tokens aligned with Kiploks (app-main, brand, surfaces; radius lg/md/sm как в SaaS) | +| `packages/cli/web/src/main.tsx` | React 18 root | +| `packages/cli/web/src/App.tsx` | Step shell, чекбокс на шаге 0, строгая проверка тела `GET /preflight/result` | +| `packages/cli/web/src/ApiErrorBanner.tsx` | Structured error UI | +| `packages/cli/web/src/legacy/mount.tsx` | `mountLegacyOrchestrator`, импорт `legacy-global.css` | +| `packages/cli/web/src/legacy/orchestrator-app.tsx` | Бывший `App()` - типизированные state и API | +| `packages/cli/web/src/legacy/report-view.tsx` | Бывший `ReportDetails` - пока `// @ts-nocheck` (следующий шаг - DTO отчёта) | +| `packages/cli/web/src/legacy/types.ts` | Общие типы UI (джобы, пути, preflight, kiploks UI) | +| `packages/cli/web/src/legacy/api.ts` / `json.ts` / `workflow.ts` | Мелкие утилиты | +| `packages/cli/web/src/legacy-orchestrator.ts` | Реэкспорт `mountLegacyOrchestrator` для совместимости | +| `packages/cli/web/src/legacy-global.css` | Бывший `