feat(ui): TDS migration stage 3 — integration fixes, legacy removal, docs
Lint / JS (eslint) (pull_request) Successful in 10s
Lint / Notify on failure (pull_request) Skipped
Lint / Deploy (pull_request) Skipped
Security / JS Security (npm audit) (pull_request) Failing after 10s
Test / JS Tests (jest) (pull_request) Successful in 14s
Lint / JS (eslint) (push) Successful in 11s
Lint / Notify on failure (push) Skipped
Lint / Deploy (push) Skipped
Security / JS Security (npm audit) (push) Failing after 11s
Test / JS Tests (jest) (push) Successful in 10s

- Fix design-system class names that do not exist in base.css (.lt-alert--error,
  .lt-form-hint); add shared .lt-modal-lg, .lt-field-error and .is-invalid rules
- Remove public/index.html and the public/base.js symlink
- package.json: start script, correct main
- README: vendored design system, Web UI section, new env vars, read-only dev mode

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HamVMDrA8RqhyxmUHgiqRp
This commit is contained in:
2026-09-08 22:17:15 -04:00
co-authored by Claude Fable 5.1
parent 68d0a906a6
commit eca5cb45b1
11 changed files with 129 additions and 3195 deletions
+72 -4
View File
@@ -12,7 +12,23 @@ A distributed workflow orchestration platform for managing and executing complex
## Styling & Layout
PULSE uses the **LotusGuild Terminal Design System**. For all styling, component, and layout documentation see:
PULSE uses the **LotusGuild Terminal Design System**. The design system is **vendored** into this
repo at `public/web_template/` (`base.css`, `base.js`, `VERSION`) and served from `/web_template/*`,
so the app has no runtime dependency on a sibling checkout. `public/web_template/VERSION` records
the design-system version, upstream short SHA, and sync date; asset URLs are cache-busted with it.
Update the vendored copy with:
```bash
scripts/sync-web-template.sh <path-to-web_template-checkout>
```
The script copies `base.css`/`base.js` as regular files (never symlinks) and rewrites `VERSION`.
Never hand-edit `public/web_template/` — it is excluded from ESLint and overwritten on every sync.
Pulse-local gaps in the design system live in `public/assets/app.css` (currently `.lt-modal-lg`,
`.lt-field-error`, `.is-invalid`) and are candidates for upstreaming.
Reference documentation:
- [`web_template/README.md`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/README.md) — full component reference, CSS variables, JS API
- [`web_template/base.css`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.css) — unified CSS (`.lt-*` classes)
@@ -20,9 +36,39 @@ PULSE uses the **LotusGuild Terminal Design System**. For all styling, component
- [`web_template/aesthetic_diff.md`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/aesthetic_diff.md) — cross-app divergence analysis and convergence guide
- [`web_template/node/middleware.js`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/node/middleware.js) — Express auth, CSRF, CSP nonce middleware
**Pending convergence items (see aesthetic_diff.md):**
- Extract inline `<style>` from `public/index.html` into `public/style.css` and extend `base.css`
- Use `lt.autoRefresh.start(refreshData, 30000)` instead of raw `setInterval`
## Web UI
The UI is server-rendered with EJS. Every route renders `views/pages/<page>.ejs` into the shared
chrome in `views/layout.ejs` (nav, header, WebSocket status dot, theme toggle, command palette).
| Route | Page | View | Page module |
|---|---|---|---|
| `/` | Dashboard | `views/pages/dashboard.ejs` | `public/assets/pages/dashboard.js` |
| `/workers` | Workers | `views/pages/workers.ejs` | `public/assets/pages/workers.js` |
| `/workflows` | Workflows | `views/pages/workflows.ejs` | `public/assets/pages/workflows.js` |
| `/executions` | Executions | `views/pages/executions.ejs` | `public/assets/pages/executions.js` |
| `/quick` | Quick Command | `views/pages/quick.ejs` | `public/assets/pages/quick.js` |
| `/scheduler` | Scheduler | `views/pages/scheduler.ejs` | `public/assets/pages/scheduler.js` |
Scripts load in a fixed order: `/web_template/base.js` → `/assets/app.js` → `/assets/pages/<page>.js`.
`app.js` owns the shell (`window.Pulse`: action registry, event bus, `Pulse.confirm`, formatters,
WebSocket, the single 30 s auto-refresh) and each page module registers itself with:
```js
Pulse.registerPage({ name, init(), refresh(), onEvent(type, data) /* return true if handled */ });
```
Page modules never attach their own listeners for UI actions — they register handlers under their
own action prefix (`dash:`, `wk:`, `wf:`, `ex:`, `qc:`, `sc:`) and the markup wires them up with
`data-action` / `data-change-action` / `data-input-action` / `data-submit-action` attributes that
`app.js` delegates. DOM ids are likewise prefixed per page. All dynamic strings go through
`Pulse.esc`, and destructive actions use the themed `Pulse.confirm` (no native `confirm()`/`alert()`).
**Content Security Policy:** pages are served under a strict nonce-based CSP (helmet), including
`script-src-attr 'none'`. There are **no inline `<script>` blocks without a nonce and no inline
event handler attributes** anywhere in `views/` or `public/assets/`; anything added there must
follow the same rule or the browser will refuse to run it. Set `PULSE_CSP_REPORT_ONLY=1` to switch
the policy to report-only (violations are reported to `/csp-report` and logged) while debugging.
## Overview
@@ -311,8 +357,30 @@ DB_USER=pulse_user # Database user
DB_PASSWORD=<password> # Database password
WORKER_API_KEY=<api-key> # Worker authentication key
EXECUTION_RETENTION_DAYS=30 # Auto-cleanup retention (default: 30)
# Web UI (all optional)
APP_NAME=PULSE # Header/boot/title app name (default: PULSE)
APP_SUBTITLE=<text> # Header subtitle (default: "Worker Orchestration // LotusGuild")
PULSE_CSP_REPORT_ONLY=1 # Serve the CSP report-only instead of enforcing it
PULSE_DEV_READONLY=1 # Local dev guard: disable all background writes (see below)
DISABLE_BACKGROUND_JOBS=1 # Alias for PULSE_DEV_READONLY
```
### Local development against a real database
Running a local instance against the production MariaDB is safe only with the read-only guard on:
```bash
PULSE_DEV_READONLY=1 PORT=8099 HOST=127.0.0.1 npm start
```
`PULSE_DEV_READONLY=1` (alias `DISABLE_BACKGROUND_JOBS=1`) disables every background writer, so a
dev instance can never mutate shared state behind your back: stale-execution recovery at startup,
the old-execution cleanup job (startup call and interval), the scheduled-command processor
(startup call and interval), and the stale-worker offline sweep. Each skip is logged at startup
alongside a `PULSE_DEV_READONLY IS ON` banner. The guard covers background jobs only — API routes
still write, so avoid destructive actions in the UI when you are pointed at production data.
**Worker (.env):**
```bash
WORKER_NAME=pulse-worker-01 # Unique worker name