Advisory issues read as advice rather than failure
Docs / docs (push) Successful in 21s
Playwright Tests / test-playwright (1, 2) (push) Failing after 2m37s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 35s
Playwright Tests / merge-reports (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
Docs / docs (push) Successful in 21s
Playwright Tests / test-playwright (1, 2) (push) Failing after 2m37s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 35s
Playwright Tests / merge-reports (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
`ADVISORY_ISSUES` moves next to `ValidationIssue` in pipeline.py, and the model derives an `advisory` flag from its own code, so the distinction the engine already made ships to the client instead of being re-guessed there. The dock keeps its summary in `--destructive` only when a real fault is among the issues and paints an advisory row `--muted-foreground`; the canvas leaves advisories off a node's dot and border entirely, since node status has three colours and no warning tier. biome checks the generated `openapi.json`, which nothing formats since the SDK script dropped its format pass — ignore it like the other generated files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013Gf7WaExcJ9bs3kfJXB3nK
This commit is contained in:
@@ -20,7 +20,8 @@ from sqlmodel import col, select
|
||||
from fluksio.api.deps import FlowControllerDep, SessionDep, get_current_user
|
||||
from fluksio.api.routes.runs import elapsed_ms
|
||||
from fluksio.core.config import settings
|
||||
from fluksio.flow.controller import ADVISORY_ISSUES, NodeStatus
|
||||
from fluksio.flow.controller import NodeStatus
|
||||
from fluksio.flow.pipeline import ADVISORY_ISSUES
|
||||
from fluksio.models import EngineEvent, FlowRun, MetricBucket
|
||||
|
||||
router = APIRouter(
|
||||
|
||||
@@ -54,6 +54,7 @@ from fluksio.flow.nodes import (
|
||||
TriggerNode,
|
||||
)
|
||||
from fluksio.flow.pipeline import (
|
||||
ADVISORY_ISSUES,
|
||||
NodeOutcome,
|
||||
Pipeline,
|
||||
RunCacheLookup,
|
||||
@@ -81,10 +82,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
HOOK_PREFIX = "/hooks"
|
||||
|
||||
# Validation codes that are worth saying but do not stop a flow running, so
|
||||
# neither the brain graph nor the health summary treats them as a fault.
|
||||
ADVISORY_ISSUES = frozenset({"unauthenticated_hook"})
|
||||
|
||||
# How long a node gets to close what it opened before the rebuild moves on.
|
||||
# A node's `stop` talks to whatever it connected to, and a broker that has gone
|
||||
# away can leave it waiting for an acknowledgement that never arrives — which
|
||||
|
||||
@@ -25,7 +25,7 @@ from concurrent.futures import Future, ThreadPoolExecutor, wait
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Literal, Protocol
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, computed_field
|
||||
|
||||
from fluksio.flow import logs
|
||||
from fluksio.flow.artifacts import is_reference
|
||||
@@ -41,9 +41,15 @@ logger = logging.getLogger(__name__)
|
||||
#: manual run shows up in the history — but it is no one's idempotency key.
|
||||
MANUAL_RUN_PREFIX = "manual-"
|
||||
|
||||
# Validation codes that are worth saying but do not stop a flow running, so
|
||||
# neither the brain graph nor the health summary treats them as a fault.
|
||||
# Lives here rather than beside the controller so `ValidationIssue` can carry
|
||||
# the distinction itself, and every reader gets it for free.
|
||||
ADVISORY_ISSUES = frozenset({"unauthenticated_hook"})
|
||||
|
||||
|
||||
class ValidationIssue(BaseModel):
|
||||
"""A problem that keeps a flow from running correctly."""
|
||||
"""Something wrong with a flow — a fault, or merely advisory."""
|
||||
|
||||
code: Literal[
|
||||
"cycle",
|
||||
@@ -60,6 +66,12 @@ class ValidationIssue(BaseModel):
|
||||
port: str | None = None
|
||||
message_name: str | None = None
|
||||
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def advisory(self) -> bool:
|
||||
"""Worth saying, but not a fault — the UI says so in a softer tone."""
|
||||
return self.code in ADVISORY_ISSUES
|
||||
|
||||
|
||||
class ValueSource(BaseModel):
|
||||
"""Who caused a message to take its current value.
|
||||
|
||||
@@ -4,7 +4,7 @@ from fluksio.flow.controller import _declared_inputs
|
||||
from fluksio.flow.events import EventBus
|
||||
from fluksio.flow.messages import DType, MessageSpec
|
||||
from fluksio.flow.nodes import Node
|
||||
from fluksio.flow.pipeline import Pipeline
|
||||
from fluksio.flow.pipeline import Pipeline, ValidationIssue
|
||||
from fluksio.flow.schemas import FlowDef, FlowInput
|
||||
|
||||
|
||||
@@ -197,3 +197,12 @@ def test_values_carry_timestamps():
|
||||
values = pipeline.values("f")
|
||||
assert values["f.out"]["value"] == 1.0
|
||||
assert values["f.out"]["ts"] > 0
|
||||
|
||||
|
||||
def test_only_advisory_codes_are_flagged_advisory():
|
||||
"""The UI reads this to keep an advisory out of the fault tone."""
|
||||
hook = ValidationIssue(code="unauthenticated_hook", message="open")
|
||||
cycle = ValidationIssue(code="cycle", message="loop")
|
||||
|
||||
assert hook.advisory is True
|
||||
assert cycle.advisory is False
|
||||
|
||||
Reference in New Issue
Block a user