Commit Graph
116 Commits
Author SHA1 Message Date
stroblme 57c5e3233b Draw the colour disc inside its tile rather than past it
Docs / docs (push) Successful in 42s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m10s
Playwright Tests / test-playwright (2, 2) (push) Failing after 22s
pre-commit / pre-commit (push) Failing after 2m7s
Test Backend / test-backend (push) Failing after 48s
Compose Smoke Test / test-compose (push) Failing after 22s
Playwright Tests / merge-reports (push) Failing after 1m6s
The disc was sized against its parents in percentages, and every box
between it and the tile is sized by what is in it — so the chain never
resolved, the brightness slider grew, and the tile scrolled with half a
disc showing. Container units are the tile's own height whatever sits in
between, so the widget now fits exactly.

Two guards behind it, since both are things a picture can fail silently:
nothing on a panel of pictures may overflow its tile, and Touch has to
actually make a control bigger while leaving what it publishes alone.
2026-08-23 22:07:21 +02:00
stroblme 5b9628c02e Rework the dashboard into two looks over one behaviour
A dashboard is a wall panel somebody hangs in their own hallway, so it
now wears what they choose: a look, and a palette of their own colours.

Two complete component sets live under `Dashboard/ui/` — `glass`
(translucent panes over a slowly moving ground) and `material` (Material
3 tonal cards) — behind one prop contract. Every control's state,
keyboard and `aria-` live in `ui/core` and are shared, so the two sets
are the same dashboard drawn twice rather than two products: a set only
decides what a control looks like while doing it.

Four settings join the channel, each drivable by a flow like any other:
`look`, `palette`, `background` and `touch`. A palette is an ordered list
of hex colours — background, surface, primary, accent, text, then more
chart colours — pasted from a coolors.co link or typed, written onto the
canvas as the token variables everything already reads. Trailing roles
are derived, so three colours are a whole dashboard, and derived text is
held to AA rather than trusted (`theme.check.ts` measures it). A palette
also decides light or dark, since its first colour is the ground.

Widgets are measured against their own tile with container queries rather
than against the viewport, animate through `motion`, and can be drawn
without their title. The three reworks:

- a bar draws a row per reading, up to eight, each in the dashboard's own
  data colours and each able to carry its own scale — replacing readings
  nested in one fill, which could only ever share one colour and stop at
  three. Documents written the old way are read as rows.
- a chart's range picker moved to a column down its right-hand edge, which
  gives the plot back a whole row of a short tile.
- the colour wheel became a disc: hue is the angle and saturation the
  distance from the middle, so a colour is one gesture rather than three,
  with brightness on a slider beside it.

`index.css` and `lib/motion.ts` are untouched — the dashboard overrides
token *values* on its canvas, never the blocks the two repos share.
2026-08-23 21:52:14 +02:00
stroblmeandClaude Fable 5 6b77675167 Note the orphaned Postgres app database
Nothing reads it since the engine moved its state to SQLite, but it still
carries the pre-cutover history and an alembic stamp naming a revision that
commit deleted — which reads like a broken migration chain and is not one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ue1tkFWB1bcGy3aWhCKpU
2026-08-23 21:03:05 +02:00
stroblmeandClaude Fable 5 19bc2810cf Add a Python SDK: flows declared in your own repository
A data scientist keeps their code where it is and decorates it: `@node`
declares a function's ports beside the function, `Flow(name, nodes=[...])`
says which of them make a flow, and `use(fn, wire=..., **settings)` rebinds
one for a single flow. `fluksio sync` uploads the document plus a generated
import shim per node, so the store still holds a complete, runnable,
git-versioned definition while the code it imports stays theirs.

`fluksio login|run|runs` and `flow.submit().wait()` are the client half, over
the run endpoints that already existed. Runs record the user repository's
commit beside the store's, so "what code produced this number" is answerable
on the side that now holds the code.

- `fluksio/sdk/`: ports, decorators, the flow builder and its checks, the shim
  generator, an HTTP client and sync. Standard library only at import, so
  `from fluksio import node` in a training script pulls in no engine.
- `FlowDef.origin` marks a flow code-defined; `Run.origin_commit` carries the
  repository's commit; `POST /modules/refresh` retires the workers without an
  install, which every sync calls — a worker holds the imported package in
  memory, so an edit to it is invisible until the process goes.
- The canvas shows a generated body read-only and names the repository to edit
  instead; a body edited there stops the next sync rather than being discarded.
- The worker's reporter carries inert `Port`, `node`, `use` and `Flow`, since
  the shim imports a module whose first line declares them.
- `examples/myresearch` is the worked example, `make sync-example` uploads it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ue1tkFWB1bcGy3aWhCKpU
2026-08-23 20:16:08 +02:00
stroblmeandClaude Opus 5 0a49b6e947 Mount the worker source in dev so it cannot fall behind the engine
Docs / docs (push) Successful in 47s
Playwright Tests / test-playwright (1, 2) (push) Failing after 29s
Playwright Tests / test-playwright (2, 2) (push) Failing after 21s
pre-commit / pre-commit (push) Failing after 2m9s
Test Backend / test-backend (push) Failing after 51s
Compose Smoke Test / test-compose (push) Failing after 22s
Playwright Tests / merge-reports (push) Failing after 1m13s
The engine and the worker speak a version-matched protocol, but compose.dev.yml
mounted only backend/fluksio: a reloaded engine talked to the worker baked into
the image. Adding the request id to that protocol therefore left every python
node failing with "the worker answered request None", because the old worker
echoed no id at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
2026-08-23 19:15:06 +02:00
stroblmeandClaude Opus 5 8d49a424f8 Say a rebuild has two scopes, and close the notepad items it fixes
The module docstrings and the notepad still described one rebuild that
touches everything. Closes the toggle cost, the seeding cost, the
per-save rebuild, the modules/apply rebuild and the Playwright spec that
could not fit a rebuild into its five seconds; files the follow-ups the
refactor leaves behind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
2026-08-23 19:05:23 +02:00
stroblmeandClaude Opus 5 0a018fb698 Stop and start a flow without rebuilding anything
A stopped flow's nodes are built like any other flow's — being stopped
means having no subscriptions, schedules or webhooks, not being absent —
so a toggle only ever needed the lifecycle call and the gate that goes
with it. It was doing a whole-pipeline rebuild instead, which on a
populated installation is every node in every flow reconnecting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
2026-08-23 18:35:28 +02:00
stroblmeandClaude Opus 5 cba8ca2c54 Publish, delete and module installs rebuild only what changed
Publishing or deleting a flow now splices that one flow into the running
graph instead of reconnecting every node in the installation, saving a
shared node's source rebuilds the flows using it, and installing modules
rebuilds only the flows holding a node that would not load.

Renaming stays on the full rebuild — it rewrites message references in
every other flow's document — and so does startup, which has no graph to
splice into.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
2026-08-23 18:32:51 +02:00
stroblmeandClaude Opus 5 715e9a9e31 Warm a worker before the node's clock starts, and refuse to delete a live flow
A node's timeout now covers its body only: the pool loads the source into the
worker it picked, off the node's budget, so imports that outlast the timeout no
longer make a node impossible to run. Draft checks compile without caching, so
saving does not evict what a busy node is serving calls from. Requests carry an
id the worker echoes and the pool checks, a reply is encoded once, and the
remote-exception cache is bounded.

DELETE /flows/{name} answers 409 while the flow has a running or queued run,
which is what was letting run_node rows outlive their run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
2026-08-23 17:11:55 +02:00
stroblmeandClaude Opus 5 cea0b26ed9 Stop the flow store leaking git zombies, honour VITE_API_URL from .env
`FlowStore._git` now passes `-c gc.auto=0`, so `git commit` no longer forks a
background `gc --auto` that reparents onto PID 1 and stays there unreaped. With
auto-gc off nothing packs on its own, so `_commit` runs a foreground `git gc`
every 500 commits — the trigger a long-lived seeding session actually reaches.

`docker/compose.yml` takes the frontend's `VITE_API_URL` build arg from the
environment, keeping `https://api.${DOMAIN}` only as the fallback. `setup.sh`
already derives the scheme from `ENVIRONMENT`, so an `up --build` that does not
layer `compose.local.yml` stops shipping a bundle that calls `https://api.localhost`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
2026-08-23 16:49:46 +02:00
stroblmeandClaude Opus 5 423968d9a0 Bound the pipeline teardown so a stuck node cannot wedge the controller
A node's stop() and a supervised task's cancellation are both waited on
inside the rebuild lock, and neither had a deadline: an MQTT client whose
broker never acknowledges the disconnect leaves aiomqtt's __aexit__
waiting forever, so reload() never returned and every start, stop or
publish behind it hung until the container was restarted.

Each node now gets five seconds to close and is abandoned after that, and
cancel_all reports what is still running rather than waiting on it — it
also no longer swallows a cancellation aimed at the caller, which used to
make the lock holder unkillable. A rebuild asked for by a request gives up
on the lock after fifteen seconds with RebuildBusy, answered as a 503.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
2026-08-23 16:36:48 +02:00
stroblme ab4bfa22bb Merge branch 'main' of git.stroblme.de:Fluksio/app
Docs / docs (push) Successful in 48s
pre-commit / pre-commit (push) Failing after 2m7s
Test Backend / test-backend (push) Failing after 53s
2026-08-23 15:35:17 +02:00
stroblme 487fb4439e up
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-23 15:35:15 +02:00
stroblme 972cc5e40a Notepad: a flow toggle rebuilds the whole pipeline
Timed at 8-10s on the tinyhouse installation, which is why the runtime
spec's 5s assertion fails there and passes on a small instance.
2026-08-23 15:05:11 +02:00
stroblme 60ebce56b0 Notepad: the https api origin is a compose default, not a stale image
Reopened with the actual trigger: the base compose builds the frontend
against https://api.${DOMAIN} and only compose.local.yml overrides it,
so any build without that layer breaks login locally. I closed this
earlier on the mistaken belief a rebuild had settled it.
2026-08-23 14:25:00 +02:00
stroblme afa9983aa1 Merge branch 'main' of git.stroblme.de:Fluksio/app
Docs / docs (push) Successful in 20s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m3s
Playwright Tests / test-playwright (2, 2) (push) Failing after 18s
pre-commit / pre-commit (push) Failing after 1m58s
Test Backend / test-backend (push) Failing after 43s
Compose Smoke Test / test-compose (push) Failing after 17s
Playwright Tests / merge-reports (push) Failing after 1m14s
2026-08-23 13:01:26 +02:00
stroblmeandClaude Opus 5 2731f65eea NOTEPAD: close the CubeLoader bug, note the loader the portal now draws
Docs / docs (push) Successful in 20s
pre-commit / pre-commit (push) Failing after 3m26s
Test Backend / test-backend (push) Failing after 42s
The invisible loader lived in the index repo only and is fixed there. What is
left for this repo is whether its lucide spinners should become the same brand
mark.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CB8uwHkH52qxLxAkGN4gTo
2026-08-23 12:51:15 +02:00
stroblme 4ebcd48069 Notepad: close the wave-1 items, record what they left behind
Eleven "to be sorted" entries are done. Three that survive are amended
rather than removed: the house panel's chart budget (the range picker
gave its row back), layoutGraph (height is derived now, width is still
guessed), and the dark:-variant scope, which turned out to share a root
with the React Flow colorMode bug. Adds the portal's missing
not-reporting state, and drops the frontend-origin entry the rebuild
settled.
2026-08-23 11:16:50 +02:00
stroblme 97162e3a59 up
Docs / docs (push) Successful in 20s
pre-commit / pre-commit (push) Failing after 2m0s
Test Backend / test-backend (push) Failing after 43s
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-23 11:02:08 +02:00
stroblme 700506e222 up
Docs / docs (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-23 00:11:08 +02:00
stroblmeandClaude Opus 5 4bdc0ea04a Panels for a ten-inch screen, and a motor button that says where it is
Docs / docs (push) Canceled after 0s
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Both screens the house is looked at on are 1280x800, so that is what the three
dashboards are laid out for: twelve columns of 96px, twelve rows of 51px, and
nothing past the bottom, because a panel does not scroll.

The motors are one control each instead of three buttons. A button could only
publish; a segmented control reads back as well — so the motor writes what it
is doing to the same message the control sets, and the segment that is held is
the direction it actually went. Up, Stop, Down for the shutters; Close/Open for
the window and In/Out for the awning, which is what those two are for.

A run stopped part way now leaves the position unknown rather than claiming the
target it never reached, so the next command in either direction moves it.

The preflight gained the two checks this needed. One runs each sample shape
past the port that would receive it. The other is arithmetic: every tile inside
the panel and none on top of another — both silent failures on a screen with no
scrollbar, and both caught before anything is written.

Sizes were settled by looking. A slider needs three rows or its tick labels
fall off; a status icon needs three or it loses the word under the glyph; a
gauge in two rows has no arc worth reading, so the battery is a bar on Home and
a gauge on Energy where there is height for one. A chart spends eighty pixels
on its chrome whatever it is given, so two of them read on this panel and three
did not — the temperature history is the one that went, and `history` still
answers for it.

`capture-panels.mjs` is how that was checked: the three panels at the screen's
own pixels, in both themes, reporting whether anything spilled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 18:50:03 +02:00
stroblmeandClaude Opus 5 a102a2f57a NOTEPAD: the house-control rig is deleted, not running
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 16:38:25 +02:00
stroblmeandClaude Opus 5 1b455b5e55 history: one Flux script per chart, and rows are json
Two things the installation found that the checks did not.

A script with three `from()` statements in it produces three results all
called `_result`, and InfluxDB refuses that outright — so the measurements go
into one filter and the rows come back tagged with which one they are.

And the answer a database node hands back holds a *list* of rows, which a
record may not: a record is flat scalars. It was declared one, so every chart
failed on the type check the moment a real answer arrived.

The second one is now caught before anything is pushed: the preflight runs
each sample shape past the port that would receive it, which is what turns
"expected record, got dict" from a runtime surprise into a line of output.

Also records the two engine faults this seeding session surfaced.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 16:24:38 +02:00
stroblmeandClaude Opus 5 0f1d2cc483 NOTEPAD: what the house port settled, and what it left
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 14:46:08 +02:00
stroblme 95da0c2457 NOTEPAD: clear what wave 4 closed, record what it left 2026-08-22 13:19:41 +02:00
stroblme 1ccfbacca2 NOTEPAD: clear what wave 3 closed, record what it left 2026-08-22 12:52:31 +02:00
stroblme 3cc2d353cd NOTEPAD: clear what wave 2 closed, record what it left
Docs / docs (push) Canceled after 0s
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
2026-08-22 12:24:56 +02:00
stroblme 3a20306342 NOTEPAD: clear what wave 1 closed, record what it left 2026-08-22 12:05:08 +02:00
stroblmeandClaude Opus 5 42a792b6de NOTEPAD: CubeLoader references a CSS rule that exists in neither repo
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TN5vRcscompqixxbYuGMah
2026-08-22 11:44:54 +02:00
stroblme f6a98051da up
Docs / docs (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-22 10:22:51 +02:00
stroblme ef329e9350 Merge branch 'main' of git.stroblme.de:Fluksio/app
Docs / docs (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
2026-08-22 09:43:12 +02:00
stroblme 6aa355cef3 up
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-22 09:43:09 +02:00
stroblme 0921cbddf6 Add make dev-lan: the app on a host port, API proxied on the same origin
Docs / docs (push) Canceled after 0s
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
2026-08-22 09:25:02 +02:00
stroblme fa3a981fa7 Note that make update removes the local Traefik proxy
Docs / docs (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
2026-08-22 08:55:49 +02:00
stroblmeandClaude Opus 5 f3b9eb1bd3 Note what the docs site left open
Docs / docs (push) Canceled after 0s
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M7Xv3cJEW5c8AXxn2hoojV
2026-08-22 05:57:29 +02:00
stroblmeandClaude Opus 5 c3cbbbc962 Record the packaging work and what it left open
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 23:01:25 +02:00
stroblmeandClaude Opus 5 9bdced74fb NOTEPAD: drop what wave two closed, file what it turned up
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Twenty-three items are done: the dashboard draft/publish split and panel
freshness, the canvas clipping warning, the inject node's per-port payloads,
the sticky failure and the brain click-through, and the lint and DOMAIN
tooling fixes.

The ChartWidget entry is rewritten rather than closed. Its ring buffer would
only remove the cheap half of the cost, and it is the shape most likely to
tempt someone into re-adding the dependency array that made a full window of
chart data go stale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uq8mtNb97A7praJLyeEYgs
2026-08-21 14:35:24 +02:00
stroblmeandClaude Opus 5 4e253f41ae NOTEPAD: drop what this wave closed, file what it turned up
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Twenty-one items are done: the canvas viewport and logs panel, the widget
overflow and stacking work, the backup and queue-namespace changes, and the
observability fold.

What replaced them is mostly the residue of doing them — a legend styled in the
wrong CSS chunk, two overflow floors that nothing would notice regressing, and
artifact blobs that outlive the rows pointing at them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uq8mtNb97A7praJLyeEYgs
2026-08-21 10:14:06 +02:00
stroblmeandClaude Opus 5 fe7a98f292 Bound a panel credential where the route check cannot reach
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Three things the security pass on the portal pairing turned up. The first two
were already true of a screen on the local network; what changed is that a
panel credential is now presentable from the internet, which is what makes
them worth closing rather than recording.

The artifact endpoint authenticates for itself, because a worker's credential
has to open it and that token is no use anywhere else. It resolved the caller
without handing over the request, so the one credential that is scoped by
route was judged by no route at all — a panel could read and write the store
as whoever approved it. It passes the request it already holds now.

The websocket has no route to judge either, and there the bound has to be on
what is sent: a panel is given the values its own dashboards draw and nothing
else — no node status, no logs, no shape of the graph. The keys stay in the
message, emptied, because a screen on a wall runs the bundle it was paired
with. `messages_for` reads that set off the published dashboards, and is the
walk the `/messages/` allowlist has wanted for a while.

And locality is no longer a header anyone can type. The marker the connector
stamps is a value minted per process, so reaching this API directly cannot buy
a device the credential meant for one that cannot reach it at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017F9RnYCJgASuBTcAjxmnsp
2026-08-21 00:09:18 +02:00
stroblmeandClaude Opus 5 1d6918d8df Pair a wall panel through the portal
A screen somewhere this installation is not reachable from asks the portal for
a code instead, and the portal mints its credential — because a token signed
here is one such a device could never present.

Where it was minted changes nothing about what it may do. The panel gate moved
off the branch that decodes a local panel token and onto whatever claims name
a panel, so the portal's and this installation's are bounded by the same check
against the same panel's dashboards. A token of that scope naming no panel is
refused rather than left holding the account it borrows.

The connector marks what arrives on its socket, since that is the only thing
that makes it true, and the approval screen now names what is holding a code —
approving adopts whatever answers, so it is worth a look first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017F9RnYCJgASuBTcAjxmnsp
2026-08-20 23:42:58 +02:00
stroblme dcd6716749 Record what the aircon actually did
pre-commit / pre-commit (push) Canceled after 0s
The mode-reads-unknown-while-off note is narrower than it looked: a unit switched off by a command keeps its last mode in those bits.
2026-08-20 23:02:04 +02:00
stroblme 4b6b9348c6 Seed the aircon write-path rig
Four controls and the unit's own answer beside them. Two catches, both on:
the flow is seeded stopped and the node's commands setting is off. The initial
values are read off the unit when the script runs, so starting the flow asks
for what it was already doing rather than commanding it to something else.
2026-08-20 22:43:03 +02:00
stroblme f4a55845ec Merge branch 'main' of git.stroblme.de:Fluksio/app
pre-commit / pre-commit (push) Canceled after 0s
2026-08-20 22:05:21 +02:00
stroblme 33bde3678d up
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-20 22:05:18 +02:00
stroblme f88dcf81c0 Let a connector write, and publish strings bare
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Two things stopped the engine commanding this house. ConnectorNode hardwired
its node function to a no-op, so an input message reaching a connector was
discarded and Art-Net's packet builder was unreachable; write() now carries
the input ports, which is additive so the contract version holds. And the MQTT
publisher JSON-encoded every payload, so "ON" went on the wire quoted and the
devices on a shared broker, which speak bare values, ignored it.

seed_house_control.py is the rig: a flow that drives the washing machine plug,
a dimmer and a colour fixture over MQTT, carries the same two as DMX on an
Art-Net node with transmit still off, and a dashboard to drive it by hand.
2026-08-20 21:57:27 +02:00
stroblme 06875a77ac up
pre-commit / pre-commit (push) Canceled after 0s
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-20 21:04:35 +02:00
stroblmeandClaude Opus 5 a2e11b61cf The e2e suite names its own origins, and refuses a live instance
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
`tests/utils/api.ts` took the API origin from `VITE_API_URL`, which
`tests/config.ts` loads out of `app/.env`. In a checkout configured for a
deployment that names the deployment — so the browser went to the local stack
while every setup and teardown call, `deleteAll` included, went to the live
one. `privateApi.ts` had the same reading, and it creates users.

Both origins now come from one place: `PLAYWRIGHT_BASE_URL`, with the API
derived from it (`app.<domain>` → `api.<domain>`) or named outright by
`PLAYWRIGHT_API_URL`, which is what CI and the compose service set. Nothing in
the suite reads `VITE_API_URL` any more.

Belt and braces, since a stack served under a real domain answers to the same
names its production instance does: a global setup resolves both origins and
refuses anything that is not loopback or a private range, before a test runs.
`PLAYWRIGHT_ALLOW_PUBLIC=1` says you meant it.

`make test-frontend` is now that safe run — the Playwright image on the proxy
network with both names mapped onto Traefik by address, as the host user so it
does not leave root-owned results behind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUb8YpL2s3gmN9WTACTt4q
2026-08-20 20:46:04 +02:00
stroblmeandClaude Opus 5 032c2e3ae6 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
2026-08-20 19:06:48 +02:00
stroblmeandClaude Opus 5 f1e13cdcd4 Flow inputs and outputs are visible and editable in the UI
A flow's inputs are the messages it takes from outside — a dashboard control,
a run, the API — and its outputs are what a batch run reports. Both existed in
the document and in the engine, and neither had any UI: the values looked
hard-coded on the canvas and the Run button always used the declared defaults.

The canvas now draws each as a labelled endpoint, the way it already draws a
dashboard tile or another flow, skipping an input something else already
accounts for. The flow panel edits them — mode, name, type, starting value,
and for a live flow the value it currently holds with a way to put a new one
in. Pressing Run on a batch flow asks for its parameters first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUb8YpL2s3gmN9WTACTt4q
2026-08-20 18:10:16 +02:00
stroblmeandClaude Opus 5 4355c917f8 Node settings arrive as keyword arguments, not a params dict
A python node's settings are constants of its own function, so they are passed
the way its ports are: by name. The controller binds them to the compiled
function, the `params` field is gone from the worker and remote protocols, and
a setting sharing a port's name is reported as a node error rather than
shadowing it. The panel's scaffold follows suit and keeps the header in step
with both ports and settings.

The demo's `pace` moves from a flow input to a setting of the training node,
which is what it always was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUb8YpL2s3gmN9WTACTt4q
2026-08-20 17:47:45 +02:00