Style the engine's own logs, notice enrolment while serving, say more in status
Docs / docs (push) Successful in 21s
Playwright Tests / test-playwright (1, 2) (push) Failing after 4m24s
Playwright Tests / test-playwright (2, 2) (push) Failing after 1m37s
pre-commit / pre-commit (push) Failing after 3m14s
Test Backend / test-backend (push) Successful in 2m15s
Compose Smoke Test / test-compose (push) Successful in 34s
Playwright Tests / merge-reports (push) Failing after 1m3s

Four things from a testing pass.

`fluksio serve` printed its own lines through the root logger, which has no
handler and falls back to `INFO:fluksio.cloud.connector:...` — beside uvicorn's
aligned output it reads like something went wrong. The engine's loggers and
alembic's now use uvicorn's own handler. Named rather than configuring the
root: httpx logs every portal call at INFO and none of that is printed today.

`fluksio enroll` writes its config from another process, so an engine already
serving never learned it had been paired. It now looks for one every few
seconds and dials when it appears. `load()` rather than `exists()`, or a file
that does not parse would be restarted forever.

`fluksio status` says where the installation stands with its portal — never
paired, linked, or paired and unreachable, which is the one worth acting on.

`--seed` and `--timeout` had no help text at all. Both say what they are for
now, and the docs say what a seed is actually for: recorded on the run, part of
its input digest, and passed to an input named `seed` when the flow declares
one, so the number a run is labelled with is the one the code drew from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019V5bsYGNxcgPs4xXmTPx69
This commit is contained in:
2026-08-25 09:29:22 +02:00
co-authored by Claude Opus 5
parent 7c5b212f43
commit 60757fa7fa
8 changed files with 231 additions and 29 deletions
+11 -8
View File
@@ -174,15 +174,17 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
cloud_task: asyncio.Task[None] | None = None
app.state.cloud_connector = None
app.state.cloud_task = None
if cloud_config.exists():
from fluksio.cloud.connector import CloudConnector
from fluksio.cloud import connector as cloud_connector
connector = CloudConnector(app)
app.state.cloud_connector = connector
cloud_task = asyncio.create_task(
connector.serve_forever(), name="cloud-connector"
)
app.state.cloud_task = cloud_task
if cloud_config.exists():
cloud_connector.start(app)
cloud_task = app.state.cloud_task
# Watched whether or not one exists now: enrolling from the CLI writes the
# config from another process entirely, and an engine already serving
# should pick it up rather than need restarting.
enrol_task = asyncio.create_task(
cloud_connector.watch_enrolment(app), name="cloud-enrolment-watch"
)
try:
# A mounted sub-app gets no lifespan of its own, so the MCP session
# manager is entered here; without it every /mcp request fails.
@@ -192,6 +194,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
watchdog_task.cancel()
alerts_task.cancel()
metrics_task.cancel()
enrol_task.cancel()
# Re-read from app.state: enrolling at runtime replaces this.
running_cloud = getattr(app.state, "cloud_task", None) or cloud_task
if running_cloud is not None: