A run says whether the dashboard, the CLI or a script asked for it

`POST /runs/flows/{name}` hardcoded `cause: "api"`, so every row in the
history claimed the same origin. The body now carries an optional `cause`,
closed to the values the column knows — the dashboard sends nothing and stays
"api", `fluksio run` says "cli", and the SDK client says "sdk".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Gf7WaExcJ9bs3kfJXB3nK
This commit is contained in:
2026-08-25 22:06:29 +02:00
co-authored by Claude Opus 5
parent b194071ca5
commit 1148e54c9e
9 changed files with 83 additions and 9 deletions
+10 -2
View File
@@ -6,7 +6,7 @@ or to listen on the flow socket, which carries its start and finish.
"""
from datetime import UTC, datetime
from typing import Any
from typing import Any, Literal
from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.concurrency import run_in_threadpool
@@ -35,6 +35,12 @@ def elapsed_ms(since: datetime) -> float:
return round((datetime.now(UTC) - start).total_seconds() * 1000, 2)
#: Where a caller may say a run came from. "sweep" is not here because the
#: sweep route writes it itself, and neither is a value a client made up: the
#: column is only worth a table row if it means the same thing every time.
RunCause = Literal["api", "cli", "sdk"]
class RunCreate(BaseModel):
params: dict[str, Any] = Field(default_factory=dict)
seed: int | None = None
@@ -42,6 +48,8 @@ class RunCreate(BaseModel):
draft: bool = False
#: Execute every node, whatever an earlier run already worked out.
no_cache: bool = False
#: Who is asking. The dashboard leaves it, and is the "api" default.
cause: RunCause = "api"
class SweepEntry(BaseModel):
@@ -172,7 +180,7 @@ async def create_run(
name,
params=body.params,
seed=body.seed,
cause="api",
cause=body.cause,
actor=user.email,
draft=body.draft,
no_cache=body.no_cache,