NOTEPAD: drop what is done, correct what was never true
A sweep against the code. Removed as resolved: the worker pool's `_running` is keyed by (run, node) now, `WorkItem.kind` documents only the three kinds that exist, `routeTree.gen.ts` matches its generator, and the host's Node is well past the version the Vite 7 note was about. Removed as disproved: the collector's redelivery branch is reachable — Redis reclaims an entry this process is still running. Corrected: the demo places six of the fifteen node types, not thirteen — the count was the widget types. Narrowed to what is left: the header/config sync now covers settings but only on an untouched scaffold and only one way; a declared flow input is what the dashboard-input node asked for; the flow boundary already refits the canvas where node ports do not. The Playwright hazard is not resolved — both names still resolve to production — and the fix as written is incomplete: `VITE_API_URL` has to be overridden to `http://` or every setup call fails the TLS handshake. Also: one `asText` instead of two, and the queue docstring no longer refers to a node item that never existed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUb8YpL2s3gmN9WTACTt4q
This commit is contained in:
+12
-15
@@ -18,16 +18,16 @@ Deferring because out of scope is fine, but don't mention deferring than.
|
||||
- CHORE/UI: the edge popover shows the same value twice — `MessageSparkline` falls through to a collapsed `ValuePreview` for a non-numeric value, and `EdgeInspector` then renders its own `ValuePreview defaultOpen` below it. Cosmetic; one of the two is redundant.
|
||||
- FEAT/UI: a settings-and-inputs overview page, so what every node of an installation is configured with can be read and searched in one place rather than one panel at a time.
|
||||
- FEAT/UI: an input endpoint opens the flow panel, which is right for editing but not for reading one value. A panel of its own — the declaration, the current value, its history — is what clicking a label wants to give.
|
||||
- FEAT/UI we should introduce a sync between the header of the python function and the node configuration; i.e. adding an input/output or static paramter would change the header of the python function and changing the python function header and vice versa
|
||||
- FEAT/UI: sync between the header of the python function and the node configuration. The config→header half exists for ports *and* settings — `scaffoldFor` writes `def process(<ports>, <settings>)` and `editNode` keeps it in step — but only while the source is still exactly the generated scaffold (`SCAFFOLD_SHAPE`), and never for shared code. What is missing is the same for code someone has edited, and the reverse direction: nothing parses a `def process(...)` header back into ports and settings.
|
||||
- BUG/UI on flows like "House history" where the widget sets the range for the "draw the window" node to generate some data, the edges overlap the nodes. We should adjust the flow visualization to account for these cyclic behaviors
|
||||
- BUG/UI when enlarging the code editor of a node, the code editor should enlarge to the left (node settings remain on the right) so that the code editor fills the center of the screen with the node properties available next to it
|
||||
- BUG/UX the console/log panel does not show print output of nodes
|
||||
- BUG/UX the console/log panel should be moved up slightly (to separate from the toolbar) and kept open (console button becomes stateful)
|
||||
- CHORE/UI: loop lag on Home reads a real number with no flows, and that is right — `LoopWatchdog` times how late `asyncio.sleep(1.0)` wakes on the API's event loop and is started unconditionally, so it measures the engine process rather than any flow, and it is what turns the health badge `degraded`. Nothing to fix; recorded so it is not reopened.
|
||||
- BUG/UI auto node placement on flows should be improved in regards to least crossing edges and a more vertical layout on mobile devices
|
||||
- FEAT/UI we should highlight failing nodes accordingly in the flow view to facilitate easier tracking of mis-configurations
|
||||
- INFRA: ensure that all the packages/ dependencies needed to run fluksio are available on arm to make this software runnable on e.g. raspbian
|
||||
- INFRA: merge the philosophy statement at the beginning of vision.md into the rest of the document. Dissolve the decision dates and fold the decisions into a clean structure
|
||||
- CHORE/INFRA: `bunx playwright test` from the host is unsafe against this stack — `app.fluksio.com` and `api.fluksio.com` resolve to production, and `--host-resolver-rules` steers only Chromium while `page.request.*` resolves through Node, so setup and teardown write to the live instance. Run the specs in a container on the `proxy` network with `--add-host` pointing both names at Traefik.
|
||||
- CHORE/INFRA: `bunx playwright test` from the host is unsafe against this stack — `app.fluksio.com` and `api.fluksio.com` resolve to production, and `--host-resolver-rules` steers only Chromium while `page.request.*` resolves through Node, so setup and teardown write to the live instance. Run the specs in a container on the `proxy` network with `--add-host` pointing both names at Traefik, and override `VITE_API_URL` to the `http://` form: `tests/utils/api.ts` takes it from `app/.env`, where it is `https://`, and Traefik has no cert the container trusts, so every `api()` call fails the TLS handshake before it reaches a request. The lasting fix is for the suite to name its own origin rather than inherit the deployment's.
|
||||
- CHORE/INFRA: `make test-backend` reads `app/.env`, so a checkout configured for a deployment fails two tests that assume the development defaults — `test_private.py::test_create_user` (the `/private` router is off unless `ENVIRONMENT=local`) and `test_mcp_http.py::test_an_agent_can_list_and_call_tools` (the MCP host allow-list is built from `DOMAIN` while the test speaks to `api.localhost`). Both pass under `ENVIRONMENT=local DOMAIN=localhost`; the suite should pin its own environment rather than inherit the deployment's.
|
||||
- CHORE/DOCS: `app/development.md` is still the upstream template's text — `compose.override.yml`, `localhost.tiangolo.com`, `docker compose watch` as the dev flow — so it disagrees with the Makefile about how the stack is started and says nothing about the Playwright suite. The containerised run above is what it should carry.
|
||||
- FEAT/UI add a loading animation for the initial app load and when loading individual pages; make sure that elements e.g. in the home dashboard load independently to ensure a fast loading of the initial site but figures charts, tables, graph etc. follow after that
|
||||
@@ -66,12 +66,10 @@ is what M4 still waits on, together with porting the flows.
|
||||
|
||||
- CHORE/API: revoking an OAuth client does not invalidate access tokens already issued; they are stateless JWTs valid up to `MCP_TOKEN_EXPIRE_MINUTES`. Immediate revocation means `app/mcp/http.py` checking the client row still exists.
|
||||
- CHORE/FLOW: `Pipeline.trigger`'s docstring says a paused flow still publishes so the value shows on the canvas. True only without a queue; with one the item parks before `apply_outputs` and nothing shows. Docstring and behaviour disagree.
|
||||
- CHORE/FLOW: `WorkItem.kind == "node"` ("executes exactly one node") was documented but never implemented. If a run-one-node item is wanted, it still needs writing.
|
||||
- CHORE/API: `GET /observability/runs` caps at 200 rows, so pinning a minute busier than that on the Home chart shows its newest 200 with nothing saying more exist. Fine at ~60 runs/min; a count alongside the rows, or paging, is what a busier instance needs.
|
||||
|
||||
### Out-of-process nodes and modules
|
||||
|
||||
- CHORE/FLOW: `PythonWorkerPool._running` is keyed by node id and last-wins, so two concurrent runs of one node mean cancel kills the newest. Key by run id once M5's run records exist.
|
||||
- CHORE/FLOW: `compile_check` sends the *draft* source under the running node's cache key, so the worker recompiles the published source on its next call. Correct, but one wasted compile per save on a busy node.
|
||||
- FEAT/API: `POST /modules/apply` rebuilds the whole pipeline so a node that could not import its package stops being red. That resubscribes every MQTT node in the deployment; a targeted rebuild of the flows that actually failed to load would be gentler.
|
||||
- CHORE/FLOW: a node's return value now round-trips through JSON, so tuples arrive downstream as lists and anything non-JSON is an explicit error. That is the message contract, but flows written before this may notice.
|
||||
@@ -91,7 +89,6 @@ is what M4 still waits on, together with porting the flows.
|
||||
- CHORE/UI: the Home block's "Changes" list is the newest 15 audit rows whatever range is selected. Deliberate — an audit trail is worth reading past the window — but it sits under a control that governs everything else on the screen.
|
||||
- CHORE/FLOW: run records for a deleted flow stay until the retention window passes, so a flow that no longer exists keeps appearing in the history. Deliberate — it is a record of what ran — but `forget_flow` could offer to clear it.
|
||||
- CHORE/API: nothing can ask the collector to flush now, so anything needing the tables to be current has to wait out `FLUSH_INTERVAL_S` — which is what the soak harness does before clearing its own rows.
|
||||
- CHORE/API: `MetricsCollector._start_run`'s `existing is not None` branch is unreachable: a redelivery only arrives after the record it would update has been dropped.
|
||||
- CHORE/FLOW: `RedisWorkQueue.clear_flow` deletes only `pipeline:__parked__:{flow}`, so a deleted or renamed flow's `__queue__` stream entries, `__delayed__` zset members and `__done__:*` markers stay behind. The stream is capped and the entries are dropped when they reach a node that no longer exists, so it costs work rather than correctness.
|
||||
- CHORE/FLOW: `MemoryWorkQueue`'s in-flight count is a counter around claim/ack, and claiming already removed the item — so an item a handler leaves unacknowledged (no pipeline bound) counts as in flight until the process ends. Nothing can hand it back either way, which is what the memory queue is.
|
||||
- CHORE/API: audit rows ride the same drop-oldest bus as telemetry, so a storm can lose one. Writing a node's source is not audited either; publishing is.
|
||||
@@ -138,9 +135,11 @@ Decisions taken up front, because most items below depend on them:
|
||||
schedule. View mode inherits localStorage and the OS preference today, which
|
||||
a panel in a room has no way to set. NOTE: to solve this, we could introduce a general message sending to the overall dashboard (so far we only treat widgets in a dashboard as a receiver). We could e.g. have a toggle in the dashboard settings which says "propagate theme" which enables a field for defining a consume input (identical to a standard node input) and then a node can connect to this property by producing a corresponding message. This would nicely generalize to other dashboard settings later. This could later also serve as a security mechanism, i.e. the possibility to lock down dashboards remotely
|
||||
- CHORE/FLOW: porting the controls needs a declared writable message per control,
|
||||
since an input widget can only target what a flow declares. Consider a
|
||||
dashboard-input node so a flow states plainly that a value arrives from a
|
||||
panel, rather than each control borrowing some node's port.
|
||||
since an input widget can only target what a flow declares. Declaring one is no
|
||||
longer API-only — the flow panel edits a flow's inputs and the canvas draws each
|
||||
as a label — so the dashboard-input node this asked for has largely been
|
||||
answered by flow inputs. What is left is the naming: an input a panel writes
|
||||
looks the same as one a run passes in.
|
||||
|
||||
Deliberately not ported: the local-state/timestamp reconciliation the old
|
||||
dashboard does per widget — publishing on release and reading the value back
|
||||
@@ -150,7 +149,7 @@ as an em dash.
|
||||
### Dashboard follow-ups
|
||||
|
||||
- BUG/UI: ensure dashboard wallpanel (read-only) links hot reload automatically on dashboard changes
|
||||
- FEAT/NODE: the hosted demo covers thirteen of the fifteen built-in node types. `switch` and `delay` are unplaced — a `switch` branch needs either a dead-end port or trivial nodes to turn a branch back into a label, and neither read as something a person would hang. Worth revisiting when the demo grows a second page.
|
||||
- FEAT/NODE: the hosted demo places six of the fifteen built-in node types (`python`, `inject`, `change`, `join`, `rbe`, `trigger`); it does cover all fifteen dashboard widget types. `switch` and `delay` are the awkward ones — a `switch` branch needs either a dead-end port or trivial nodes to turn a branch back into a label, and neither reads as something a person would hang — while the I/O types (`mqtt`, `http`, `influxdb`, `exec`, `file`, `ntfy`, `mlp`) are unplaced because the demo has nothing real to talk to. Worth revisiting when the demo grows a second page.
|
||||
- CHORE/UI: an icon rule stores a `label` — the widget draws it under the glyph and uses it as the glyph's accessible name — but the mapping editor offers no field for it, so it can only be set through the API.
|
||||
- BUG/UI: shrinking the canvas silently clips whatever now falls past its bottom edge. `maxRows` only constrains a new drag, not a stored placement, so nothing warns and nothing offers to reflow.
|
||||
- CHORE/UX: dropping a widget also selects it, which opens its panel — which rescales the canvas the instant you let go. Correct, but it lurches; either leave the panel closed on a drag-release or animate the scale.
|
||||
@@ -169,9 +168,9 @@ as an em dash.
|
||||
|
||||
### Flow editor follow-ups
|
||||
|
||||
- BUG/UI: the payload of the inject node should adapt to the type set in the "provides" arguments and the number of fields should grow with the number of "provides" arguments. So if e.g. one argument is boolean, the payload should be a dropdown for true/false. If it is int, the value entered should be parsed as int etc. Check for other notes where this applies as well
|
||||
- BUG/UI: the payload of the inject node should adapt to the type set in the "provides" arguments and the number of fields should grow with the number of "provides" arguments. So if e.g. one argument is boolean, the payload should be a dropdown for true/false. If it is int, the value entered should be parsed as int etc. `payload` is typed `Any`, so `ParamsForm` renders one plain text field and never sees the node's `provides` at all. The dtype→widget pieces to reuse are `parseByDtype` and the bool dropdown in `FlowBoundary.tsx`, written for flow inputs and the run dialog.
|
||||
- CHORE/UI: a node's error status clears as soon as it runs again, so a failure that genuinely fired an alert can leave no trace on the canvas by the time anyone looks. The logs panel keeps the traceback; the node itself reads as healthy.
|
||||
- FEAT/UI: the flow graph should auto zoom-to fit when a panel is open and edges are created through assigning inputs/outputs
|
||||
- FEAT/UI: the flow graph should auto zoom-to fit when a panel is open and edges are created through assigning inputs/outputs. Declaring a *flow-level* input or output already refits, since it adds an endpoint and the effect watches `external.nodes.length`; editing a node's own ports does not, and neither does opening a panel.
|
||||
- PERF/FLOW: every save rebuilds the whole pipeline. Fine at the current flow count; rebuild only the touched flow when it starts to show.
|
||||
- CHORE/API: `POST /flows/{name}/rename` is no longer reachable from the UI. A flow's title is what the panel edits, matching how nodes work; the canonical name is fixed at creation, so either the endpoint goes or renaming comes back deliberately.
|
||||
- BUG/UI: `renderedNodes` overwrites xyflow's own `selected` flag with `id === selectedId`, so a box-selection of several nodes is invisible even though delete and copy act on all of them.
|
||||
@@ -187,9 +186,8 @@ as an em dash.
|
||||
|
||||
### Infrastructure
|
||||
|
||||
- CHORE/INFRA: the `playwright` compose service cannot reach `api.localhost`, so `make verify-docker` is the only containerised route. (Native Playwright now works: the headless-shell libs are installed. Only the headless shell is downloaded — `--headed` still needs `bunx playwright install chromium`, and there is no emoji font, so 👋 renders as tofu in screenshots.)
|
||||
- CHORE/INFRA: the `playwright` compose service cannot reach `api.localhost` and is not in the `dev-local` set, so `make verify-docker` is the only containerised route. Running natively is no longer dependable either: the workspace pins browser revision 1208 and `~/.cache/ms-playwright/chromium-1208` has no binary, so a host run either fails or silently borrows the 1234 build a `bunx playwright@latest` left behind. `bunx playwright install` against the pinned version is what would settle it. (The emoji font is installed now — 👋 no longer renders as tofu.)
|
||||
- CHORE/UI: `make lint-frontend` is `biome check --write --unsafe ./` — a lint target that rewrites the whole tree rather than checking it. A checking target plus a separate `format` would be safer.
|
||||
- CHORE/UI: `routeTree.gen.ts` was generated by an older router version than the installed one; the next build reorders ~130 lines regardless of who touched it.
|
||||
- CHORE/UI: the alerts screen duplicates the backend's `ALERTING_EVENTS`; the chooser drifts if the backend set grows. A rule with nothing ticked covers everything, so it fails soft.
|
||||
- CHORE/UI: `tests/runtime.spec.ts` still calls the home page "the dashboard" (`dashboard-flow-row`), which now collides with the dashboards feature.
|
||||
- CHORE/INFRA: `make soak`'s redis scenario stops the container the whole stack shares, so every flow briefly fails to journal, not just the soak fixtures. They recover on their own — nothing was dead-lettered or quarantined in the run this note comes from — but it is not a thing to run against a stack someone is relying on.
|
||||
@@ -204,7 +202,6 @@ Open on purpose. Each names what should bring it back.
|
||||
- CHORE/API: node source saves carry no version precondition, so two clients editing the same node's code are last-writer-wins. The flow document is what the optimistic lock protects; code files would need their own, and an exact-match one produces false conflicts against a single client's own interleaved flow and source saves. Revisit with the M5 multi-user work.
|
||||
- CHORE/FLOW: shared node sources bypass the draft/publish split. Editing one writes the library copy and reloads immediately, since the code is not any single flow's to hold back. Deliberate, but it means a shared node is the one thing publish does not gate.
|
||||
- CHORE/INFRA: `requires-python` is capped below 3.14 because the MCP SDK wants a newer starlette there than the pinned `sentry-sdk<2` allows. Lift the cap when sentry-sdk moves to 2.x.
|
||||
- CHORE/INFRA: `bun run --filter frontend build` fails on this workspace with `crypto.hash is not a function` — Vite 7 wants Node 20.12+ and the host has 18. The Docker image builds fine, so it only bites local bundling; `bunx tsc` still type-checks.
|
||||
- FEAT/UI: the node-panel and edge trend curves take no range, unlike the health block. They are drawn from a Redis ring of the last 120 values per message, which has no window to ask for — a hover caption names what the curve covers instead of a picker promising a span nothing can serve. Reopen if per-message history ever gains a time window.
|
||||
- FEAT/UI: an e-ink rendering profile for a dashboard — motion off, hover-only affordances resolved to something visible, high-contrast palette, thick strokes, and a repaint cadence low enough for a display that takes a second to settle. Reopen when a panel with such a display is actually hung.
|
||||
- CHORE/INFRA: Postgres stays. The 2026-08 review rejected YugabyteDB/CockroachDB (multi-node cluster systems, ~4 GB+ RAM per node, against the small-server target — the scaling story is remote workers, not a distributed DB) and found merging Postgres into Redis or vice versa buys little: the stores hold disjoint data and both sit behind abstractions. SQLite would fit the single-instance design and drop a container; reopen if the home-install footprint becomes a product concern.
|
||||
|
||||
Reference in New Issue
Block a user