Report a published node that has no code of its own

The store answers the new-node template when nothing was ever written for a
node, so such a node ran — returning {} on every call, reporting active and
ok, and saying nothing anywhere. Unreachable through `fluksio sync`, which
writes every body before it publishes; the editor end was open.

A run of a flow holding one is now refused, and the flow carries a
missing_source issue so it is visible before anybody runs it. A draft is
exempt: a node being written legitimately has no published body yet.

The generated client is regenerated for the new issue code, which also
catches up the drift left by earlier backend work (resources, code_digest,
idempotency_key).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 22:55:11 +02:00
co-authored by Claude Opus 5
parent 69fc0ceeb5
commit 4f3eaf950c
8 changed files with 275 additions and 8 deletions
+27
View File
@@ -1,9 +1,11 @@
"""Editing writes drafts; only publishing changes what the engine reads."""
import asyncio
from pathlib import Path
import pytest
from fluksio.flow.controller import FlowController
from fluksio.flow.messages import MessageSpec
from fluksio.flow.schemas import FlowDef, NodeDef
from fluksio.flow.store import FlowStore, StaleVersion
@@ -106,6 +108,31 @@ def test_an_edited_source_alone_counts_as_a_draft(store: FlowStore):
assert store.read_node_source("heating", "sensor") == EDITED
def test_a_published_node_with_no_body_is_reported(store: FlowStore):
"""It would run the new-node template, which returns nothing and says so."""
store.write_flow(a_flow())
controller = FlowController(store)
asyncio.run(controller.reload())
(issue,) = [i for i in controller.issues if i.code == "missing_source"]
assert issue.node == "heating.sensor"
assert not issue.advisory
# It still loads: one node with no body does not take the flow down.
assert controller.get_node("heating.sensor") is not None
store.write_node_source("heating", "sensor", SOURCE)
asyncio.run(controller.reload())
assert [i for i in controller.issues if i.code == "missing_source"] == []
def test_a_node_being_written_in_the_editor_is_not_reported(store: FlowStore):
"""A draft legitimately has no published body yet — that is what a draft is."""
store.write_draft(a_flow(), 0)
controller = FlowController(store)
assert controller.preview("heating").issues == []
def test_resaving_the_published_source_creates_no_draft(store: FlowStore):
store.write_flow(a_flow())
store.write_node_source("heating", "sensor", SOURCE)