feat(coordinator): the nudge bodies state observed facts, never hedges

The idle-children header drops its opening idleness claim: a queued
entry delivers at whichever seam arrives next, and the drain
predicate re-verifies that children are active — never that the
coordinator is still idle — so the body now opens with the one fact
the delivery just verified.

The tasks body replaces its hedged children sentence with one
observed-fact line per child. The old sentence hedged states the
producer's read had just returned and invented activity for an idle
coordinator; the producer now threads (ws_id, state) pairs through,
and the formatter renders a running child as running (check before
redoing what it owns) and a stopped one as stopped, with the
tool-behaviour fact that wait_for_workstream returns immediately for
it. The line asserts nothing about results: no read observes whether
a child produced anything, and the immediate wait is the whole
protection — checking is cheap and finds whatever is there. Fact
lines are formatter-built beside the counts opener, so no tail
override can reach them; the formatter's old indeterminate-read hedge
branch is deleted (a failed read renders no body at all), and the
open-row status takes the same alteration check as the id.

Both bodies hand the model full workstream ids: the resolver refuses
truncated ids by design, so the roster's 8-char prefixes were not
handles — a model copying a bullet issued a call the resolver
rejects. Display prefixing stays on the operator card, derived from
the full id in the metadata.

Eval alignment: fixture child ids become production-shaped 32-hex (a
prefix looked like a different id entirely and the old shape only
resolved through the legacy branch); a body-override sweep refuses
cells without a live child at config time, keyed on the formatter's
own childless condition, so candidate text can never be measured over
a world production cannot produce.
This commit is contained in:
Patrick Buckley
2026-07-29 17:50:22 -07:00
parent 8874dcaa69
commit 48d6b2f84b
10 changed files with 1197 additions and 758 deletions
+105 -62
View File
@@ -25,8 +25,8 @@ from turnstone.console.coordinator_idle_observer import (
CoordinatorIdleObserver,
)
from turnstone.core.metacognition import (
NUDGE_CHILD_STOPPED_STATES,
NUDGE_IDLE_TASKS_CHILD_SLOT,
NUDGE_IDLE_TASKS_CHILDREN_CAVEAT,
NUDGE_IDLE_TASKS_ID_SLOT,
NUDGE_IDLE_TASKS_WAIT_SLOT,
NUDGE_REQUIRED_TOOL,
@@ -350,14 +350,14 @@ class TestEnqueueOnIdle:
# Same set, same order, same count — one list underneath.
assert [r["ws_id"] for r in rows] == ["child-a", "child-b", "child-c-longer-than-8"]
# The model-facing body: id-prefix + state bullets, byte-exact,
# The model-facing body: FULL id + state bullets, byte-exact,
# so no fragment of any name — hostile or benign — can ride
# along, and no ``(unnamed)`` fallback exists to stand in.
bullets = [ln for ln in text.split(chr(10)) if ln.startswith(" - ")]
assert bullets == [
" - child-a (running)",
" - child-b (thinking)",
" - child-c- (running)",
" - child-c-longer-than-8 (running)",
]
assert "thinking>" not in text # the tag, never the state word
assert "200ms" not in text
@@ -370,13 +370,15 @@ class TestEnqueueOnIdle:
for row in rows:
assert set(row) == {"ws_id", "state"}
# IDENTITY. The meta keeps ``ws_id`` raw on every row, and the
# FE derives its ident column from it (``String(c.ws_id).slice(0,
# 8)`` in ``appendIdleChildren``, pinned in test_coordinator_page),
# matching the 8-char prefix the body's own bullet renders.
# IDENTITY. The meta keeps ``ws_id`` raw on every row; the body
# bullet carries the same full id (a HANDLE — the resolver
# refuses prefixes), and the FE derives its 8-char display ident
# from the meta (``String(c.ws_id).slice(0, 8)`` in
# ``appendIdleChildren``, pinned in test_coordinator_page) —
# formatting over one value, not a second value.
assert all(r["ws_id"] for r in rows)
assert rows[2]["ws_id"][:8] == "child-c-"
assert "child-c-" in bullets[2]
assert rows[2]["ws_id"] == "child-c-longer-than-8"
assert bullets[2] == " - child-c-longer-than-8 (running)"
def test_idle_with_no_active_children_no_enqueue(self, coord_setup):
mgr, storage, ws = coord_setup
@@ -773,12 +775,11 @@ class TestIdleTasks:
The load-bearing case is ``test_active_children_co_deliver_with_
idle_tasks``: the classes are independent conditions, so when both
hold the pair CO-DELIVERS in one drain, tasks first. Each body
asserts only its own domain — the tasks body carries the "children
may still be running" caveat whenever a live child row exists, and
says nothing about children when none does — so a consistent pair
is two true statements. The thing that must never happen is a
STALE pair, which the per-path reads and per-entry predicates
prevent.
asserts only its own domain — the tasks body renders one observed
fact line per live child row, and says nothing about children when
none exists — so a consistent pair is two true statements. The
thing that must never happen is a STALE pair, which the per-path
reads and per-entry predicates prevent.
"""
def test_open_tasks_and_no_children_enqueues(self, coord_setup):
@@ -1376,14 +1377,18 @@ class TestIdleTasksMetadata:
assert meta["counts"] == {"open": 1, "in_progress": 0, "pending": 1}
class TestTasksBodyChildrenCaveat:
class TestTasksBodyChildrenFacts:
"""Which BODY the tasks path enqueues, by observed children state.
The caveat is the one sentence in the tasks body that speaks about
the other domain. It is conditioned on EXISTENCE — any child row in
a live state — and not on the liveness path's ACTIVE set, because an
idle child holding results nobody collected is exactly the "may have
finished while you worked" disjunct the sentence carries.
The per-child fact lines are the tasks body's one statement about
the other domain, and they render the OBSERVED ``(ws_id, state)``
the read returned — never a hedge about it (the retired caveat's
"may still be running or may have finished while you worked" was
manufactured uncertainty plus manufactured context, ruled out
2026-07-29). They are conditioned on EXISTENCE — any child row in
a live state — and not on the liveness path's ACTIVE set, because
an idle child holding results nobody collected is exactly the row
the stopped-child line protects.
Everything here is about CONTENT. That the fire itself is
untouched by any ANSWER these states return is
@@ -1406,7 +1411,7 @@ class TestTasksBodyChildrenCaveat:
pending = ws.session._nudge_queue.pending_with_metadata("any")
return next(entry for entry in pending if entry[0] == "idle_tasks")
def test_childless_body_omits_caveat_card_meta_unchanged(self, coord_setup):
def test_childless_body_omits_children_facts_card_meta_unchanged(self, coord_setup):
"""The measured harm, removed: a coordinator with no children is
no longer told to go and check on children it does not have.
@@ -1418,28 +1423,38 @@ class TestTasksBodyChildrenCaveat:
mgr, storage, ws = coord_setup
_type, text, meta = self._fire(mgr, storage, ws)
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in text
assert "may still be running" not in text
# The rest of the body is untouched — this removes a sentence,
# not the opener it rides or the branches after it.
assert "Child " not in text
assert "child" not in text
# The rest of the body is untouched — this omits the fact lines,
# not the counts line they ride under or the branches after it.
assert text.startswith("You still have 1 open task: 1 in_progress, 0 pending.")
assert "needs_user" in text
assert meta == {"counts": {"open": 1, "in_progress": 1, "pending": 0}}
def test_idle_child_keeps_the_caveat(self, coord_setup):
"""The stranded-children case: the load-bearing detector for
a probe wired to ``_ACTIVE_CHILD_STATES`` instead of
``_LIVE_CHILD_STATES``, which no gate-matrix or metadata test
can see (they all seed running children or none). Its siblings
are ``test_every_live_state_keeps_the_caveat[idle]`` and the
"""The stranded-children case, and the C6b protection: the
stopped-child fact line must state the stop and the immediate
wait — the retired hedge's "may have finished" disjunct, now
attached to the one child it is true of, with the id the model
needs to check. It asserts NOTHING about results ("may hold
uncollected results" was cut as a fabrication — no read ever
observed results existing); the immediate wait is the whole
protection, because checking is cheap and finds whatever is
there. Load-bearing detector for a probe wired to
``_ACTIVE_CHILD_STATES`` instead of ``_LIVE_CHILD_STATES``,
which no gate-matrix or metadata test can see (they all seed
running children or none). Its siblings are
``test_every_live_state_renders_its_fact_line[idle]`` and the
eval-parity guard's ``idle_child`` scenario; this one carries
the reason.
the reason. (The name predates the fact lines — "the caveat"
here IS the stopped-child line — and stays so the C6b pointer
in the fixture notes keeps resolving.)
An idle child is not actionable — the liveness path will not
nudge about it, which is why no co-delivered entry appears here
— but it exists, and it may hold results nobody has collected.
Conditioning on the ACTIVE set would strip the hedge from the
one state whose protective disjunct is live.
Conditioning on the ACTIVE set would drop the line from the one
state whose protection is live.
"""
mgr, storage, ws = coord_setup
# ``_add_active_child`` seeds ANY state despite its name.
@@ -1447,36 +1462,53 @@ class TestTasksBodyChildrenCaveat:
_type, text, _meta = self._fire(mgr, storage, ws)
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in text
assert (
"Child child-a has stopped — wait_for_workstream returns immediately for it."
) in text
# The observed state renders as itself — never the hedge, never
# the other state's line.
assert "Child child-a is still running" not in text
assert "may still be running" not in text
# The liveness path stays out of it: an idle child is not
# actionable, so this really is the advice nudge alone hedging
# actionable, so this really is the advice nudge alone speaking
# about a child nobody was woken for.
assert [t for t, _ in ws.session._nudge_queue.pending("any")] == ["idle_tasks"]
@pytest.mark.parametrize("state", sorted(_LIVE_CHILD_STATES))
def test_every_live_state_keeps_the_caveat(self, coord_setup, state):
def test_every_live_state_renders_its_fact_line(self, coord_setup, state):
"""The membership is the enum, not a list someone maintains: a
state added to ``WorkstreamState`` joins the live set with no
edit, and this parametrization grows with it."""
edit, and this parametrization grows with it. Each state gets
the line that is TRUE of it: wait-terminal states (idle/error)
the stopped line with its immediate-wait point, everything else
the still-running line."""
mgr, storage, ws = coord_setup
_add_active_child(storage, ws_id="child-a", state=state)
_type, text, _meta = self._fire(mgr, storage, ws)
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in text
if state in NUDGE_CHILD_STOPPED_STATES:
assert (
"Child child-a has stopped — wait_for_workstream returns immediately for it."
in text
)
else:
assert "Child child-a is still running; check before redoing anything it owns." in text
@pytest.mark.parametrize("state", ["closed", "deleted"])
def test_terminal_child_rows_read_as_childless(self, coord_setup, state):
"""``closed`` and ``deleted`` are strings the close and reap
paths write, and they are NOT ``WorkstreamState`` members — a
coordinator whose every child row is terminal has no children to
hedge about, however many rows storage still holds."""
coordinator whose every child row is terminal has no children
for the body to speak about, however many rows storage still
holds."""
mgr, storage, ws = coord_setup
_add_active_child(storage, ws_id="child-a", state=state)
_type, text, _meta = self._fire(mgr, storage, ws)
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in text
assert "Child " not in text
assert "child" not in text
def test_the_advice_fire_is_identical_across_every_answer_the_read_returns(self):
"""The ruling, restated to the width it actually holds: what the
@@ -1773,6 +1805,17 @@ class TestExecutableCalls:
assert "child_ws_id='child-a'" in text
assert "child_ws_id='child-b'" not in text
assert NUDGE_IDLE_TASKS_CHILD_SLOT not in text
# ...and each child gets its OWN observed fact line, in read
# order — the running one the redo protection, the idle one the
# stop and the immediate wait — so the mixed state renders as
# two facts, never one hedge covering both.
lines = text.split(chr(10))
assert lines[1] == (
"Child child-a is still running; check before redoing anything it owns."
)
assert lines[2] == (
"Child child-b has stopped — wait_for_workstream returns immediately for it."
)
def test_the_blocked_branch_is_a_call_not_prose(self, coord_setup):
"""MUTATION CONTROL: reverting the wait to prose fails here.
@@ -1830,9 +1873,8 @@ class TestExecutableCalls:
text = self._body(mgr, storage, ws)
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in text
assert NUDGE_IDLE_TASKS_CHILD_SLOT not in text
for fragment in ("child", "wait_for_workstream", "list_workstreams"):
for fragment in ("child", "Child", "wait_for_workstream", "list_workstreams"):
assert fragment not in text, fragment
# Two calls, not three — and the ones that remain are populated.
assert self._task_id_args(text) == ["tsk_a"] * 2
@@ -1997,7 +2039,7 @@ class TestIndeterminateChildrenRead:
pending = ws.session._nudge_queue.pending("any")
assert [t for t, _ in pending] == ["idle_tasks"]
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in pending[0][1]
assert "Child " not in pending[0][1]
def test_liveness_does_not_fire_when_children_read_fails(self, coord_setup):
mgr, storage, ws = coord_setup
@@ -3068,7 +3110,7 @@ class TestEvalParity:
``_open_task_ids``) and calls the real formatter — the eval carries
no copy of any comprehension. Sharing the derivation removes one
drift class; this guard covers the one that remains: the OBSERVER's
plumbing (envelope read → counts + ids → ``child_ws_ids`` argument →
plumbing (envelope read → counts + ids → ``children`` argument →
formatter → card meta) can still diverge from the eval's
re-derivation over the same envelope, and every sweep would then
score a body that is not the one shipping, with the merge decision
@@ -3079,13 +3121,13 @@ class TestEvalParity:
re-derivations agreeing proves nothing about what production emits.
Run on TWO coordinator states, because the body is not one string:
the caveat sentence AND the two child slots are conditioned on which
child rows exist, so "the eval renders what production renders" is
two claims, one per cell class, and a guard that only ever saw the
childless state would go green against an eval wired to the wrong
branch on every cell that has children.
the per-child fact lines AND the two child slots are conditioned on
which child rows exist, so "the eval renders what production
renders" is two claims, one per cell class, and a guard that only
ever saw the childless state would go green against an eval wired
to the wrong branch on every cell that has children.
``expect_child_ws_ids`` is declared per scenario rather than derived,
``expect_children`` is declared per scenario rather than derived,
so each branch is a written-down claim about what production emits
in that state rather than the same derivation run twice. It is what
makes a PARTIAL update fail: while the observer passed a literal
@@ -3093,29 +3135,30 @@ class TestEvalParity:
flipped in the same commit that replaced that literal with a real
read. Either half of a pair like that landing alone trips here — an
observer re-wired to a literal fails ``no_children``, and an eval
that stopped deriving the value fails it too. Declaring the ID
rather than a bool is what extends that property to the populated
slots: an observer that read the right ROWS but projected the wrong
field would still have satisfied a bool.
that stopped deriving the value fails it too. Declaring the
``(ws_id, state)`` PAIR rather than a bool is what extends that
property to the fact lines and the populated slots: an observer that
read the right ROWS but projected the wrong field — or the id
without the state — would still have satisfied a bool.
"""
@pytest.mark.parametrize(
("children", "expect_child_ws_ids"),
("children", "expect_children"),
[
pytest.param([], [], id="no_children"),
# An IDLE child: a row that exists (so the caveat speaks
# An IDLE child: a row that exists (so the fact lines speak
# about something real) but that the liveness path will not
# nudge about, so this scenario stays a single-entry
# comparison rather than turning into a co-delivery test.
# It is also the second detector for a read miswired to the
# ACTIVE set — that bug renders the childless body here,
# against an eval that (correctly) derives the id.
# against an eval that (correctly) derives the pair.
# ``child-1`` is ``_add_active_child``'s default ws_id.
pytest.param([{"state": "idle"}], ["child-1"], id="idle_child"),
pytest.param([{"state": "idle"}], [("child-1", "idle")], id="idle_child"),
],
)
def test_eval_stimulus_matches_production_formatter(
self, coord_setup, children, expect_child_ws_ids
self, coord_setup, children, expect_children
):
mgr, storage, ws = coord_setup
# ``_add_active_child`` seeds ANY state despite its name — it
@@ -3186,7 +3229,7 @@ class TestEvalParity:
# 1. The model-facing body is byte-identical to the eval's, on
# the branch this coordinator state selects.
assert text == render_tasks_body(envelope, child_ws_ids=expect_child_ws_ids)
assert text == render_tasks_body(envelope, children=expect_children)
assert text.startswith("You still have 6 open tasks: 2 in_progress, 4 pending.")
# 2. The operator card records the counts the body told the
+235 -141
View File
@@ -21,8 +21,9 @@ import pytest
from turnstone.console.coordinator_idle_observer import _ACTIVE_CHILD_STATES
from turnstone.core.metacognition import (
NUDGE_IDLE_TASKS_CHILD_DOOR,
NUDGE_IDLE_TASKS_CHILD_SLOT,
NUDGE_IDLE_TASKS_CHILDREN_CAVEAT,
NUDGE_IDLE_TASKS_ID_SLOT,
)
from turnstone.core.session import COORDINATOR_TOOLS
from turnstone.core.storage._registry import (
@@ -644,7 +645,7 @@ class TestSweepValidation:
def test_no_caveat_arm_with_only_terminal_children_is_refused(self):
"""``closed`` / ``deleted`` are the strings the close and reap
paths write, and they are NOT ``WorkstreamState`` members — a row
in one of them is gone, so the cell is childless for the caveat's
in one of them is gone, so the cell is childless for the body's
purposes however many rows it declares."""
for state in ("closed", "deleted"):
cell = {
@@ -669,8 +670,8 @@ class TestSweepValidation:
def test_no_caveat_arm_passes_on_any_live_child_including_idle(self):
"""EXISTENCE-in-a-live-state, deliberately broader than the pair
arms' ACTIVE predicate: C6b's idle child — a child that finished
with results nobody collected — is precisely the state the
caveat's second disjunct covers, and it fails the pair-arm check
with results nobody collected — is precisely the row the
stopped-child fact line protects, and it fails the pair-arm check
(``test_pair_arm_with_only_inactive_children_is_refused``) while
passing this one. Both are correct."""
for state in sorted(_LIVE_CHILD_STATES):
@@ -696,6 +697,89 @@ class TestSweepValidation:
}
_validate_cells([cell])
def test_a_childless_cell_is_refused_under_an_override(self):
"""Item: an override sweep can no longer force children content
into a childless world, STRUCTURALLY. A childless cell renders
the formatter's childless branch, whose literal door cut would
silently strip a candidate that quotes the shipped
blocked-on-a-child branch — so the sweep refuses the cell at
config validation, naming the cell and the reason, before any
model round-trip. Without an override the same cell passes:
the shipped tail is exactly what the cut is defined against.
"""
cell = {
"id": "X_override_childless",
"arms": [ARM_NUDGE],
"children": [],
"tasks": [_OPEN_TASK],
}
_validate_cells([cell]) # fine against the shipped body
with pytest.raises(SystemExit) as excinfo:
_validate_cells([cell], override_active=True)
message = str(excinfo.value)
assert "X_override_childless" in message, message
assert "body-override" in message, message
assert "live state" in message, message
def test_a_terminal_only_children_cell_is_refused_under_an_override(self):
"""The refusal keys on the LIVE derivation, not the raw list: a
cell whose every child row is terminal is a childless world to
the formatter, so a raw-list predicate would wave it through and
the door cut would maul the candidate anyway."""
cell = {
"id": "X_override_terminal",
"arms": [ARM_NUDGE],
"children": [
{
"ws_id": "ws-c1",
"name": "auditor",
"state": "closed",
"transcript": [_ASSIGNMENT_ROW, _FINDINGS_ROW],
}
],
"tasks": [_OPEN_TASK],
}
_validate_cells([cell])
with pytest.raises(SystemExit, match="X_override_terminal"):
_validate_cells([cell], override_active=True)
def test_a_live_child_cell_passes_under_an_override(self):
"""The refusal is exactly as wide as the maul: any live-state
child row keeps the door in play, so the cell measures the
candidate as authored (idle included — the C6b class is a legal
override cell)."""
for state in ("running", "idle"):
cell = {
"id": f"X_override_{state}",
"arms": [ARM_NUDGE],
"children": [
{
"ws_id": "ws-c1",
"name": "auditor",
"state": state,
"transcript": [_ASSIGNMENT_ROW]
+ ([_FINDINGS_ROW] if state == "idle" else []),
}
],
"tasks": [_OPEN_TASK],
}
_validate_cells([cell], override_active=True)
def test_the_shipped_cells_that_survive_an_override_are_the_children_ones(self):
"""The shipped grid under ``--body-override``: exactly the two
children-bearing cells validate; every childless cell is refused
by name. (An override sweep therefore runs with ``--cells
C6_co_delivery,C6b_stranded_children`` or a fixture edit — never
with a silently mauled childless body.)"""
with_children = [c for c in NUDGE_CELLS if _live_children(c.get("children") or [])]
assert {c["id"] for c in with_children} == {"C6_co_delivery", "C6b_stranded_children"}
_validate_cells(with_children, override_active=True)
for cell in NUDGE_CELLS:
if cell in with_children:
continue
with pytest.raises(SystemExit, match=cell["id"]):
_validate_cells([cell], override_active=True)
def test_the_cells_that_declare_no_caveat_are_the_ones_with_children(self):
"""The shipped grid, named rather than assumed.
@@ -951,7 +1035,9 @@ class TestRefusalsPrecedeTheCanary:
_SENTINEL = "canary-probe-entered"
@classmethod
def _sweep(cls, monkeypatch, cells: list[dict[str, Any]]) -> None:
def _sweep(
cls, monkeypatch, cells: list[dict[str, Any]], *, override: str | None = None
) -> None:
from turnstone.eval import nudges as nudges_module
def _probe_sentinel(*a: Any, **k: Any) -> bool:
@@ -968,6 +1054,7 @@ class TestRefusalsPrecedeTheCanary:
model="eval-model",
cells=cells,
n_runs=1,
body_override_text=override,
)
def test_a_good_cell_reaches_the_probe(self, monkeypatch):
@@ -1001,6 +1088,20 @@ class TestRefusalsPrecedeTheCanary:
self._sweep(monkeypatch, [cell])
assert self._SENTINEL not in str(excinfo.value), cell
def test_the_override_refusal_fires_before_the_probe(self, monkeypatch):
"""The override-only class binds at the same point as every
other refusal: config time, zero model round-trips. Both
directions, like the class docstring demands — the childless
cell refuses WITHOUT the sentinel, and the same cell without an
override reaches the probe (so the refusal really is
override-conditional)."""
cell = {"id": "X_t", "arms": [ARM_NUDGE], "children": [], "tasks": [_OPEN_TASK]}
with pytest.raises(SystemExit) as excinfo:
self._sweep(monkeypatch, [cell], override="Candidate wording.")
assert self._SENTINEL not in str(excinfo.value)
with pytest.raises(RuntimeError, match=self._SENTINEL):
self._sweep(monkeypatch, [cell])
def test_a_duplicate_cell_id_fires_before_the_probe(self, monkeypatch):
"""The one refusal that reads the whole list rather than a
single cell, so it needs its own two-cell fixture."""
@@ -1067,13 +1168,14 @@ class TestStimulus:
def test_bodies_are_production_rendered(self):
"""The eval carries no copy of the body: the real formatter over
the really-seeded envelope — the counts opener, the id block, the
co-delivery honesty line and the escape branch, populated with
the seeded id and the seeded child, and NO task text."""
the really-seeded envelope — the counts opener, the per-child
observed fact line, the id block and the escape branch,
populated with the seeded id and the seeded child, and NO task
text."""
env = _envelope({"id": "tsk_1", "title": "audit auth.py", "status": "pending"})
body = render_tasks_body(env, child_ws_ids=["ws-c1"])
body = render_tasks_body(env, children=[("ws-c1", "running")])
assert body.startswith("You still have 1 open task: 0 in_progress, 1 pending.")
assert "may still be running" in body
assert "Child ws-c1 is still running; check before redoing anything it owns." in body
assert "needs_user" in body
# The seeded id reaches the block AND the branch calls; the
# seeded title reaches neither.
@@ -1088,32 +1190,32 @@ class TestStimulus:
ablation cannot drift from the body that ships.
THE ARM ABLATES THE BODY'S WHOLE CHILDREN AWARENESS, by design.
``child_ws_ids`` governs the caveat sentence, the
The ``children`` pairs govern the per-child fact lines, the
blocked-on-a-child branch and that branch's slots, because in
production all of them answer ONE storage read and a bool beside
a list is two derivations of one question. So the arm asks a
single clean question — does this body need to mention children
at all? — rather than the narrower "what does the sentence buy?"
it was named for. That is a better question, which is why the
arm is worth keeping; the name stays because archived sweeps
report under it.
production all of them answer ONE storage read and a second
derivation of it is a state where two answers can disagree. So
the arm asks a single clean question — does this body need to
mention children at all? — rather than the narrower "what does
the sentence buy?" it was named for. That is a better question,
which is why the arm is worth keeping; the name stays because
archived sweeps report under it.
This scope is not drift. It is what "the formatter's other
branch" MEANS now that one condition governs every
children-bearing element, and the alternative — a knob that
stripped the sentence while keeping the branch — would render a
body no coordinator receives, which is the one thing an eval arm
may never do.
branch" MEANS now that one read governs every children-bearing
element, and the alternative — a knob that stripped the fact
lines while keeping the branch — would render a body no
coordinator receives, which is the one thing an eval arm may
never do.
"""
env = _envelope({"id": "tsk_1", "title": "audit auth.py", "status": "pending"})
kept = render_tasks_body(env, child_ws_ids=["ws-c1"])
ablated = render_tasks_body(env, child_ws_ids=[])
kept = render_tasks_body(env, children=[("ws-c1", "running")])
ablated = render_tasks_body(env, children=[])
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in kept
assert "Child ws-c1 is still running" in kept
assert "child_ws_id='ws-c1'" in kept
# NOTHING about children survives the ablation — asserted as
# absence of the topic, so a reworded branch cannot pass.
for absent in ("child", "wait_for_workstream", "list_workstreams", "may still be running"):
for absent in ("child", "Child", "wait_for_workstream", "list_workstreams"):
assert absent not in ablated, absent
# ...and nothing else moves. The counts opener, the id block and
# the remaining branches survive: this arm ablates an
@@ -1126,16 +1228,20 @@ class TestStimulus:
turns = build_stimulus(ARM_NO_CAVEAT, envelope=env, children=[_LIVE_CHILD])
assert turns[1]["content"] == ablated
def test_the_nudge_arm_derives_the_caveat_from_the_cells_children(self):
def test_the_nudge_arm_derives_the_children_facts_from_the_cells_children(self):
"""The ``nudge`` arm renders what a coordinator in that cell's
state would really receive, which since the observer's probe
landed is two different bodies by cell class.
landed is two different bodies by cell class — and for the
children-bearing class, the OBSERVED per-state fact lines.
The predicate is EXISTENCE in a live state, the observer's own:
an idle child keeps the caveat (it may hold results nobody
collected) while a terminal row does not count as a child at
all. Deriving through ``_live_children`` rather than the pair
arms' active filter is what those two rows detect.
an idle child keeps its stopped-with-immediate-wait line (it
may hold results nobody collected) while a terminal row does not
count as a child at all. Deriving through ``_live_children``
rather than the pair arms' active filter is what those two rows
detect — and the STATE riding beside the id is what makes the
idle row render as the fact it is instead of a running claim or
a hedge.
"""
env = _envelope({"id": "tsk_1", "title": "audit auth.py", "status": "pending"})
@@ -1143,50 +1249,28 @@ class TestStimulus:
turns = build_stimulus(ARM_NUDGE, envelope=env, children=children)
return str(turns[1]["content"])
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in _body([])
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in _body([_LIVE_CHILD])
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in _body(
[{"ws_id": "ws-c1", "name": "auditor", "state": "idle"}]
assert "Child " not in _body([])
assert "Child ws-c1 is still running; check before redoing anything it owns." in _body(
[_LIVE_CHILD]
)
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in _body(
[{"ws_id": "ws-c1", "name": "auditor", "state": "closed"}]
)
def test_an_indeterminate_read_renders_the_children_aware_body(self):
"""``None`` reaches the formatter unchanged, and the formatter
hedges — the eval must not launder a failed read into a
measurement of the childless body.
It is the case a naive "drop when empty" gets wrong, and the one
that keeps the blocked-on-a-child branch alive on a failed read.
The branch is kept and its slots stay placeholders, which is the
honest state: the read that failed is the HARNESS's, so the
model's own ``list_workstreams()`` may well answer. A formatter
that instead invented a ws_id from an unknown read would put a
call the model cannot run in front of it, which is the exact
failure the invented ``a1b2c3d4`` constant used to cause.
"""
env = _envelope({"id": "tsk_1", "title": "audit auth.py", "status": "pending"})
hedged = render_tasks_body(env, child_ws_ids=None)
childless = render_tasks_body(env, child_ws_ids=[])
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in hedged
assert "If an item is waiting on a child workstream" in hedged
# No fabricated child anywhere: an unknown read leaves both child
# slots as their honest placeholders.
assert "ws-c1" not in hedged
assert hedged.count(NUDGE_IDLE_TASKS_CHILD_SLOT) == 2
# The childless body is the same one minus every children-bearing
# element, so the hedged/childless pair is the whole conditional.
for absent in ("child", "wait_for_workstream", "list_workstreams"):
assert absent not in childless, absent
idle_body = _body([{"ws_id": "ws-c1", "name": "auditor", "state": "idle"}])
assert (
"Child ws-c1 has stopped — "
"wait_for_workstream returns immediately for it."
) in idle_body
assert "Child " not in _body([{"ws_id": "ws-c1", "name": "auditor", "state": "closed"}])
# No hedge about an observed state, in any cell class.
for children in ([], [_LIVE_CHILD], [{"ws_id": "ws-c1", "state": "idle"}]):
body = _body(children)
assert "may still be running" not in body, children
assert "while you worked" not in body, children
def test_ragged_envelope_rows_do_not_raise(self):
env = _envelope(
{"id": "tsk_1", "title": None, "status": "pending", "note": 42},
"not-a-dict-row",
)
body = render_tasks_body(env, child_ws_ids=["ws-c1"])
body = render_tasks_body(env, children=[("ws-c1", "running")])
assert body.startswith("You still have 1 open task")
def test_seed_transcript_pairs_calls_with_results(self):
@@ -1301,78 +1385,53 @@ class TestBodyOverrideSkip:
assert "skipped" not in cell[arm]
assert cell[arm]["n"] == 2
def test_override_never_applies_the_caveat_conditional(self):
"""A candidate that keeps the caveat sentence verbatim while
rewording another paragraph is the LIKELIEST tuning shape there
is, and it is the one the literal-anchored cut would silently
maul: the sentence is present, so the removal lands, and a
childless cell would file a caveat-stripped candidate under the
heading of the wording the operator actually wrote.
def test_a_children_cell_measures_a_door_quoting_candidate_as_authored(self):
"""The LIKELIEST candidate shape there is: one that keeps the
shipped blocked-on-a-child branch verbatim while rewording
another paragraph. On a children cell — the only cell class an
override sweep still admits — the formatter's door cut never
runs (children are present), so the candidate's quoted branch
survives byte-exact and its slots are populated exactly as
production would populate the shipped text. The childless cell
that would have mauled this candidate is refused at config time
(``test_a_childless_cell_is_refused_under_an_override``), which
is the structural half of the same protection.
So the conditional is off whenever an override is installed —
every arm, every cell, including the arm whose whole definition
is the other branch. Asserted through the REAL override context
manager, because the mechanism under test is the one that swaps
the module constant.
Asserted through the REAL override context manager, because the
mechanism under test is the one that swaps the module constant.
"""
from turnstone.eval import nudges as nudges_module
env = _envelope({"id": "tsk_1", "title": "audit auth.py", "status": "pending"})
candidate = (
NUDGE_IDLE_TASKS_CHILDREN_CAVEAT
+ chr(10) * 2
+ "Reworded branch paragraph: use tasks(action='update', "
+ "task_id='tsk_...', status='needs_user')."
)
candidate = "Reworded opening paragraph: reconcile your list." + NUDGE_IDLE_TASKS_CHILD_DOOR
with nudges_module._body_override(candidate):
forced = render_tasks_body(env, child_ws_ids=[], override_active=True)
through_the_arm = build_stimulus(
ARM_NO_CAVEAT, envelope=env, children=[_LIVE_CHILD], override_active=True
)
# The control: without the override rule this is what the
# candidate would have been reported as.
unprotected = render_tasks_body(env, child_ws_ids=[])
rendered = render_tasks_body(env, children=[("ws-c1", "running")])
through_the_arm = build_stimulus(ARM_NUDGE, envelope=env, children=[_LIVE_CHILD])
# The control: the childless branch really would cut the
# quoted door out of this candidate — the maul the refusal
# and the skip exist to make unreachable.
mauled = render_tasks_body(env, children=[])
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in forced
# The candidate tail rides byte-exact after the formatter-built
# counts opener — the opener is seeded state, not tuned text, so
# an override never touches it.
assert forced == "You still have 1 open task: 0 in_progress, 1 pending." + candidate
assert through_the_arm[1]["content"] == forced
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in unprotected, (
# Formatter-built facts lead; the candidate rides after them
# with its quoted door substituted, never stripped.
assert rendered.startswith(
"You still have 1 open task: 0 in_progress, 1 pending."
+ chr(10)
+ "Child ws-c1 is still running; check before redoing anything it owns."
)
assert "record the link and wait instead of redoing its work" in rendered
assert "child_ws_id='ws-c1'" in rendered
assert NUDGE_IDLE_TASKS_CHILD_SLOT not in rendered
# The candidate's own task-id slot has no open-list machinery in
# this candidate, so it substitutes from the seeded set like the
# shipped tail's would.
assert "task_id='tsk_1'" in rendered
assert NUDGE_IDLE_TASKS_ID_SLOT not in rendered
assert through_the_arm[1]["content"] == rendered
assert "record the link and wait instead of redoing its work" not in mauled, (
"the control must show the cut really lands on candidate text"
)
def test_the_runner_threads_the_override_flag_into_every_run(self, monkeypatch):
"""The skip covers the two ablation arms; ``override_active`` is
what protects the REST of the grid, whose bodies are rendered
from the candidate text. Pinned on the argument the runs receive
— a runner that computed the flag and dropped it would leave the
conditional live against unknown text, which is the whole class
the skip exists to close."""
from turnstone.eval import nudges as nudges_module
seen: list[bool] = []
def _fake_run(**kw: Any) -> dict[str, Any]:
seen.append(kw["override_active"])
return {"pass": True, "failures": [], "forbidden": [], "actions": ["tasks"]}
monkeypatch.setattr(nudges_module, "tool_call_canary", lambda *a, **k: True)
monkeypatch.setattr(nudges_module, "_run_single_nudge", _fake_run)
cells = [{"id": "X_tuning", "arms": [ARM_NUDGE], "tasks": [_OPEN_TASK]}]
for override, expected in (("Candidate wording.", True), (None, False)):
seen.clear()
run_nudge_response(
base_url="http://eval.invalid/v1",
api_key="x",
model="eval-model",
cells=cells,
n_runs=2,
body_override_text=override,
)
assert seen == [expected, expected], override
class TestBodyFingerprint:
"""A result file must say WHICH body produced its numbers.
@@ -1385,7 +1444,9 @@ class TestBodyFingerprint:
"""
@staticmethod
def _sweep(monkeypatch, *, override: str | None) -> dict[str, Any]:
def _sweep(
monkeypatch, *, override: str | None, cells: list[dict[str, Any]] | None = None
) -> dict[str, Any]:
from turnstone.eval import nudges as nudges_module
monkeypatch.setattr(nudges_module, "tool_call_canary", lambda *a, **k: True)
@@ -1398,7 +1459,9 @@ class TestBodyFingerprint:
base_url="http://eval.invalid/v1",
api_key="x",
model="eval-model",
cells=[
cells=cells
if cells is not None
else [
{"id": "X_a", "arms": [ARM_NUDGE], "tasks": [_OPEN_TASK]},
{
"id": "X_b",
@@ -1436,21 +1499,52 @@ class TestBodyFingerprint:
def test_the_fingerprint_is_of_the_effective_tail_under_an_override(self, monkeypatch):
"""Taken INSIDE the override context, so the hash is of the text
the runs really saw — a fingerprint of the shipped tail under a
tuning sweep would be a false provenance stamp, which is worse
than none."""
candidate sweep would be a false provenance stamp, which is
worse than none.
An override sweep admits only children-bearing cells (the
childless class is refused at config time), so the default
two-cell fixture cannot serve here — both cells carry a live
child, and both stamp the ``children_present`` fact their runs
really rendered, DERIVED rather than forced by a flag.
"""
from turnstone.core import metacognition as metacog
candidate = "Candidate wording under test."
out = self._sweep(monkeypatch, override=candidate)
out = self._sweep(
monkeypatch,
override=candidate,
cells=[
{
"id": "X_b",
"arms": [ARM_NUDGE],
"children": [_LIVE_CHILD],
"tasks": [_OPEN_TASK],
},
{
"id": "X_c",
"arms": [ARM_NUDGE],
"children": [
{
"ws_id": "ws-c9",
"name": "researcher",
"state": "idle",
"transcript": [
_ASSIGNMENT_ROW,
_FINDINGS_ROW,
],
}
],
"tasks": [_OPEN_TASK],
},
],
)
assert out["body"]["override"] is True
assert out["body"]["tail_sha256"] == hashlib.sha256(candidate.encode()).hexdigest()
# The conditional is off under an override, on EVERY cell — so
# the childless cell stamps ``True`` here, matching the body its
# runs really received rather than the fact its fixture holds.
assert out["body"]["cells"] == {
"X_a": {"children_present": True},
"X_b": {"children_present": True},
"X_c": {"children_present": True},
}
# ...and the constant is restored afterwards, so the stamp is
# not evidence of a leaked override.
+21 -13
View File
@@ -32,7 +32,7 @@ from tests._helpers import wait_until as _wait_until
from tests.test_session_manager import FakeStorage
from turnstone.core import session_worker
from turnstone.core.idle_nudge_watcher import IdleNudgeWatcher, wake_workstream_if_pending
from turnstone.core.metacognition import NUDGE_IDLE_TASKS_CHILDREN_CAVEAT
from turnstone.core.metacognition import NUDGE_IDLE_TASKS_CHILD_DOOR
from turnstone.core.session import ChatSession
from turnstone.core.session_manager import SessionManager
from turnstone.core.trajectory import dicts_from_turns, turn_from_dict
@@ -563,10 +563,15 @@ def test_coord_idle_with_children_and_open_tasks_delivers_both(coord_mgr, tmp_db
assert chr(10) + " - tsk_a (in_progress)" in tasks_text
assert "task_id='tsk_a'" in tasks_text
assert "audit auth.py" not in tasks_text
# The caveat branch the read selects when a live child row is
# really in storage — the populated half of the pair whose empty
# half is the test below.
assert "may still be running" in tasks_text
# The children-aware branch the read selects when a live child
# row is really in storage — the populated half of the pair
# whose empty half is the test below. The fact line renders the
# OBSERVED state (registered running) with the full id, end to
# end through the real storage round-trip.
assert (
"Child child-a is still running; check before redoing anything it owns." in tasks_text
)
assert "may still be running" not in tasks_text
# CO-DELIVERY COHERENCE, end to end: both bodies in one drain now
# name the same child, from two independent storage reads. The
# tasks body populates its blocked-on-a-child branch with the
@@ -591,15 +596,16 @@ def test_coord_idle_with_children_and_open_tasks_delivers_both(coord_mgr, tmp_db
observer.shutdown()
def test_coord_idle_with_open_tasks_and_no_children_omits_the_caveat(coord_mgr, tmp_db):
def test_coord_idle_with_open_tasks_and_no_children_omits_children_content(coord_mgr, tmp_db):
"""The childless sibling of the test above, over the same chain:
no child rows registered, so the enqueue-time existence aggregate
answers "none" and the DELIVERED body says nothing about children.
no child rows registered, so the enqueue-time live-children read
answers "none" and the DELIVERED body says nothing about children
no fact lines, no blocked-on-a-child branch.
Asserted on the transcript rather than on the formatter's return,
because the claim is about what the coordinator is actually told:
the probe queries the storage backend through the real
``count_workstreams_by_state``, and every boundary between that read
the body's children read queries the storage backend through the
real ``list_workstreams``, and every boundary between that read
and the system turn — observer, queue, watcher, wake worker,
``deliver_wake_nudge_from_queue`` — is production code.
"""
@@ -659,10 +665,12 @@ def test_coord_idle_with_open_tasks_and_no_children_omits_the_caveat(coord_mgr,
m["content"] for m in msgs if m.get("role") == "system" and m["_source"] == "idle_tasks"
)
assert tasks_text.startswith("You still have 1 open task: 1 in_progress, 0 pending.")
assert "may still be running" not in tasks_text
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT not in tasks_text
assert "Child " not in tasks_text
assert "child" not in tasks_text
assert NUDGE_IDLE_TASKS_CHILD_DOOR not in tasks_text
# The nudge is otherwise the shipped one: the conditional drops
# a sentence, not the opener, the id block or the instructions.
# the fact lines and the blocked-on-a-child branch, not the
# opener, the id block or the other instructions.
assert "needs_user" in tasks_text
# End to end, through the REAL storage round-trip: the id that
# went into ``workstream_config`` comes back out populated into
+224 -118
View File
@@ -1,15 +1,18 @@
"""Tests for turnstone.core.metacognition — detection, nudging, formatting."""
import pytest
from turnstone.core.metacognition import (
MEMORY_NUDGE_TYPES,
NUDGE_CHILD_STOPPED_STATES,
NUDGE_COMPLETION,
NUDGE_CORRECTION,
NUDGE_DENIAL,
NUDGE_IDLE_CHILDREN_DISPLAY_CAP,
NUDGE_IDLE_CHILDREN_HEADER,
NUDGE_IDLE_CHILDREN_WAIT_CAP,
NUDGE_IDLE_TASKS_CHILD_DOOR,
NUDGE_IDLE_TASKS_CHILD_SLOT,
NUDGE_IDLE_TASKS_CHILDREN_CAVEAT,
NUDGE_IDLE_TASKS_ID_SLOT,
NUDGE_IDLE_TASKS_OPEN_LIST_SLOT,
NUDGE_IDLE_TASKS_TAIL,
@@ -412,9 +415,42 @@ class TestFormatIdleChildrenNudge:
def test_single_child_renders_id_and_state(self):
children = [{"ws_id": "ws-abc12345", "state": "running"}]
text = format_idle_children_nudge(children)
assert "ws-abc12 (running)" in text # short-id form (8 chars) + state
assert " - ws-abc12345 (running)" in text # FULL id + state
assert "wait_for_workstream" in text
assert "ws-abc12345" in text # full id appears in the suggestion's ws_ids list
assert wait_call(["ws-abc12345"]) in text
def test_header_opens_with_the_drain_verified_fact_only(self):
"""The body's first sentence is the one claim the drain predicate
re-verifies children are still active. "You are idle." led it
until 2026-07-29 and was dropped: a queued entry delivers at
whichever seam arrives next, and the predicate re-checks the
CHILDREN, never the coordinator's idleness, so that sentence
could be false at delivery. The harness must not render a state
it does not hold.
"""
text = format_idle_children_nudge([{"ws_id": "ws-abc12345", "state": "running"}])
assert text.startswith("These child workstreams are still active:")
assert text.startswith(NUDGE_IDLE_CHILDREN_HEADER)
assert "You are idle" not in text
assert "idle" not in NUDGE_IDLE_CHILDREN_HEADER
def test_bullets_carry_the_full_ws_id_as_a_handle(self):
"""The roster exists to hand the model HANDLES for inspect /
message / wait, and ``_resolve_ws_ref`` refuses truncated ids by
design (near-miss ids are NEVER auto-resolved) so an 8-char
prefix bullet was not a handle, it was a value the resolver
rejects sitting one line above the full id that works. The
bullet and the wait line carry the SAME full id; prefixing for
readability is the FE's display concern, not this body's.
"""
full = "a0b1c2d3e4f5061728394a5b6c7d8e9f"
text = format_idle_children_nudge([{"ws_id": full, "state": "running"}])
bullets = [ln for ln in text.splitlines() if ln.startswith(" - ")]
assert bullets == [f" - {full} (running)"]
assert f"To block on them: {wait_call([full])}." in text
# MUTATION CONTROL for the truncation returning: no bullet
# carries the 8-char prefix form.
assert f" - {full[:8]} (" not in text
def test_model_authored_names_never_reach_the_body(self):
"""THE SAFE-HARNESS PIN. A ``name`` key on the row — benign or
@@ -439,9 +475,9 @@ class TestFormatIdleChildrenNudge:
text = format_idle_children_nudge(children)
bullet_rows = [ln for ln in text.splitlines() if ln.startswith(" - ")]
assert bullet_rows == [
" - ws-real0 (running)",
" - ws-evil0 (thinking)",
" - ws-real0 (attention)",
" - ws-real0001 (running)",
" - ws-evil0002 (thinking)",
" - ws-real0003 (attention)",
]
# No name text, no fallback, no steering content anywhere.
assert "benign-research" not in text
@@ -451,13 +487,11 @@ class TestFormatIdleChildrenNudge:
assert "(unnamed)" not in text
def test_under_display_cap_no_overflow_line(self):
# The id's first 8 chars must differ per row, or the prefix
# assertions cannot tell the bullets apart.
children = [{"ws_id": f"ws{i:02d}-aaaaaa", "state": "running"} for i in range(3)]
text = format_idle_children_nudge(children)
assert "...and" not in text
for i in range(3):
assert f"ws{i:02d}-aaa" in text # the row's 8-char prefix
assert f" - ws{i:02d}-aaaaaa (running)" in text # the row's FULL id
def test_over_display_cap_renders_overflow_line(self):
n = NUDGE_IDLE_CHILDREN_DISPLAY_CAP + 4
@@ -470,7 +504,7 @@ class TestFormatIdleChildrenNudge:
bullets = [ln for ln in text.splitlines() if ln.startswith(" - ")]
assert len(bullets) == NUDGE_IDLE_CHILDREN_DISPLAY_CAP
for i in range(NUDGE_IDLE_CHILDREN_DISPLAY_CAP):
assert f"ws{i:02d}-aaa" in bullets[i]
assert bullets[i] == f" - ws{i:02d}-aaaaaa (thinking)"
def test_over_wait_cap_truncates_suggestion_ws_ids(self):
n = NUDGE_IDLE_CHILDREN_WAIT_CAP + 5
@@ -673,8 +707,9 @@ class TestSanitizePayload:
class TestFormatIdleTasksNudge:
"""The ``idle_tasks`` body — counts opener, caveat, the open-id
block, typed branches with populated calls, and NOTHING else.
"""The ``idle_tasks`` body — counts opener, per-child observed fact
lines, the open-id block, typed branches with populated calls, and
NOTHING else.
Adopted off the round-8 numbers: the counts candidate matched or
beat the roster body on every childless cell with zero forbidden
@@ -687,20 +722,24 @@ class TestFormatIdleTasksNudge:
and the controls here are the other half of that pair: ids and
statuses appear, titles and notes cannot (the formatter is not given
any), and every emitted call is populated from what the caller
observed rather than from an invented constant.
observed rather than from an invented constant. Children content is
the same discipline one step further (the 2026-07-29 ruling): the
caller observed ``(ws_id, state)`` per child, so the body renders
that fact per child and never a hedge about it.
"""
_OPEN = [("tsk_1", "in_progress"), ("tsk_2", "pending")]
@staticmethod
def _fmt(counts=None, *, open_task_ids=None, child_ws_ids=None):
"""Render the default two-open fixture with one live child.
def _fmt(counts=None, *, open_task_ids=None, children=None):
"""Render the default two-open fixture with one running child.
``child_ws_ids`` defaults to ``None`` INDETERMINATE, which
keeps the caveat and populates no child slot because that is
the value most assertions below want: the caveat-bearing body
with nothing about children fabricated into it. Tests that care
about population pass a list.
``children`` defaults to one RUNNING child the children-aware
body, which is the fuller of the two forms because that is
what most assertions below want. Tests about the childless form
pass ``[]`` explicitly; there is no third state to default to
(the formatter takes a required list, and a failed production
read renders no body at all).
The defaults live HERE and not on the formatter deliberately: a
default on the production function would let a caller lose a
@@ -712,7 +751,7 @@ class TestFormatIdleTasksNudge:
open_task_ids=(
TestFormatIdleTasksNudge._OPEN if open_task_ids is None else open_task_ids
),
child_ws_ids=child_ws_ids,
children=[("child-a", "running")] if children is None else children,
)
def test_empty_counts_return_empty_string(self):
@@ -776,26 +815,25 @@ class TestFormatIdleTasksNudge:
" - tsk_2 (pending)",
]
def test_the_entire_body_is_counts_line_plus_tail(self):
def test_the_entire_body_is_counts_line_facts_plus_tail(self):
"""The strongest control: byte-equality against the one constant
plus the formatter-built opener, modulo exactly the slot
operations the formatter declares.
plus the formatter-built opening fact block, modulo exactly the
slot operations the formatter declares.
Spelling the operations out here is the point anything smuggled
into the body (a provenance sentence, an interpolated title, a
second wait call) lands outside this equality and fails it, and
any slot operation that is NOT one of these has to be added to
this test before it can ship.
second wait call, a hedge sentence riding back in beside the
fact lines) lands outside this equality and fails it, and any
slot operation that is NOT one of these has to be added to this
test before it can ship.
Both branches of the children conditional are byte-checked, and
the childless one is TWO literal cuts against the same constant:
the caveat sentence and the whole blocked-on-a-child branch.
Deriving the childless body from the childful one by a single
caveat cut is what the shipped code did for one release, and it
is exactly the bug so this test can no longer be written that
way either.
Both branches of the children conditional are byte-checked: the
children-bearing form is opener + one fact line per child + the
populated tail, and the childless one is the door's literal cut
against the same constant with NO fact lines.
"""
opener = "You still have 2 open tasks: 1 in_progress, 1 pending."
facts = chr(10) + "Child child-a is still running; check before redoing anything it owns."
block = chr(10) * 2 + " - tsk_1 (in_progress)" + chr(10) + " - tsk_2 (pending)"
childful = (
NUDGE_IDLE_TASKS_TAIL.replace(NUDGE_IDLE_TASKS_OPEN_LIST_SLOT, block, 1)
@@ -803,15 +841,14 @@ class TestFormatIdleTasksNudge:
.replace(NUDGE_IDLE_TASKS_ID_SLOT, "tsk_1")
.replace(NUDGE_IDLE_TASKS_CHILD_SLOT, "child-a")
)
assert self._fmt(child_ws_ids=["child-a"]) == opener + childful
assert self._fmt(children=[("child-a", "running")]) == opener + facts + childful
childless = (
NUDGE_IDLE_TASKS_TAIL.replace(NUDGE_IDLE_TASKS_CHILDREN_CAVEAT, "", 1)
.replace(NUDGE_IDLE_TASKS_CHILD_DOOR, "", 1)
NUDGE_IDLE_TASKS_TAIL.replace(NUDGE_IDLE_TASKS_CHILD_DOOR, "", 1)
.replace(NUDGE_IDLE_TASKS_OPEN_LIST_SLOT, block, 1)
.replace(NUDGE_IDLE_TASKS_ID_SLOT, "tsk_1")
)
assert self._fmt(child_ws_ids=[]) == opener + childless
assert self._fmt(children=[]) == opener + childless
def test_escape_branch_precedes_resume_branch(self):
"""Branch order follows harm: guessing on an operator decision is
@@ -833,7 +870,7 @@ class TestFormatIdleTasksNudge:
redoing a running child's work > a stale list > redone finished
work. The blocked-on-child branch is the second escape hatch
after the operator one, before "take it"."""
out = self._fmt(child_ws_ids=["a1b2c3d4e5f6"])
out = self._fmt(children=[("a1b2c3d4e5f6", "running")])
# The id in this slot must be a BARE HEX string, matching
# uuid4().hex ids and the FE link regex /^[a-f0-9]{8,64}$/i — a
# "ws_"-prefixed example taught the model an id shape
@@ -851,53 +888,113 @@ class TestFormatIdleTasksNudge:
assert out.index("needs_user") < out.index("child_ws_id=")
assert out.index("child_ws_id=") < out.index("If the next step is yours")
def test_never_asserts_children_are_done(self):
def test_a_running_child_renders_the_running_fact_line(self):
"""This nudge can fire ALONE while children run (the liveness
nudge can be blocked by its own cap or wait gate), so the
CHILDREN-PRESENT body must carry the may-still-be-running line
deleting it reopens the resume-over-live-children hazard the old
cross-domain fire gate existed for. The childless body carries
no children claim at all, which asserts nothing false
(``test_childless_body_says_nothing_about_children_at_all``)."""
assert "may still be running" in self._fmt()
CHILDREN-PRESENT body must carry the check-before-redoing
protection deleting it reopens the resume-over-live-children
hazard the old cross-domain fire gate existed for. It rides an
OBSERVED fact line, full id: the caller read the state this same
event, so the body states it rather than hedging about it."""
out = self._fmt(children=[("child-a", "running")])
assert (
chr(10) + "Child child-a is still running; check before redoing anything it owns."
) in out
@pytest.mark.parametrize("state", ["thinking", "running", "attention"])
def test_every_non_stopped_live_state_reads_as_still_running(self, state):
out = self._fmt(children=[("child-a", state)])
assert "Child child-a is still running" in out
assert "has stopped" not in out
@pytest.mark.parametrize("state", sorted(NUDGE_CHILD_STOPPED_STATES))
def test_a_stopped_child_states_the_stop_and_the_immediate_wait(self, state):
"""The stranded-child protection, per stopped state (idle AND
error both are states ``wait_for_workstream`` treats as
terminal, so the line's wait-returns-immediately claim is true
for exactly this set). The line asserts the stop and the cheap
check, NOTHING about results: "may hold uncollected results"
was cut as a fabrication no read observed results existing.
This line is the C6b protection the old caveat's "may have
finished" disjunct, now attached to the child it is true of,
and the check it invites finds whatever is actually there."""
out = self._fmt(children=[("child-a", state)])
assert (
chr(10) + "Child child-a has stopped — "
"wait_for_workstream returns immediately for it."
) in out
assert "is still running" not in out.split(chr(10))[1]
def test_mixed_children_render_one_fact_line_each_in_read_order(self):
out = self._fmt(children=[("child-a", "running"), ("child-b", "idle")])
lines = out.split(chr(10))
assert lines[1] == (
"Child child-a is still running; check before redoing anything it owns."
)
assert lines[2] == (
"Child child-b has stopped — "
"wait_for_workstream returns immediately for it."
)
def test_no_hedge_survives_about_an_observed_state(self):
"""MUTATION CONTROL for the retired caveat (ruled 2026-07-29):
the read returned each child's state, so "may still be running
or may have finished" was manufactured uncertainty, and "while
you worked" was manufactured context (the coordinator was IDLE).
Neither fabrication may ride back in under any children state.
"""
for children in (
[],
[("child-a", "running")],
[("child-a", "idle")],
[("child-a", "running"), ("child-b", "idle")],
):
out = self._fmt(children=children)
assert "may still be running" not in out, children
assert "may have finished" not in out, children
assert "while you worked" not in out, children
assert "Children of yours" not in out, children
def test_childless_body_says_nothing_about_children_at_all(self):
"""An affirmative empty list — the caller observed no child in a
live state removes EVERY children-bearing element: the caveat
sentence and the whole blocked-on-a-child branch.
live state renders EVERY children-bearing element absent: no
fact lines and no blocked-on-a-child branch.
Removing only the sentence was the shipped behaviour for one
release, and it left the body omitting the claim about children
while keeping the instruction about children two calls a
childless coordinator cannot make, pointing at a lookup that
returns nothing. Both cuts are literal-anchored against their own
constants, so a reworded neighbour cannot shift either.
Omitting the facts while keeping the instruction was the shipped
behaviour for one release, and it left a childless coordinator
reading two calls it could not make, pointing at a lookup that
returns nothing. The door cut is literal-anchored against its
own constant, so a reworded neighbour cannot shift it.
Asserted first as ABSENCE OF THE TOPIC no reworded branch could
satisfy it and then as exact string differences, because a cut
that also took a neighbouring sentence, or that landed one
character off, would satisfy "the lines are gone" while shipping a
mangled paragraph.
satisfy it and then as an exact string difference against the
children-bearing form, because a cut that also took a
neighbouring sentence, or that landed one character off, would
satisfy "the lines are gone" while shipping a mangled paragraph.
"""
dropped = self._fmt(child_ws_ids=[])
dropped = self._fmt(children=[])
for absent in ("child", "wait_for_workstream", "list_workstreams", "may still be running"):
for absent in ("child", "Child", "wait_for_workstream", "list_workstreams"):
assert absent not in dropped, absent
# The INDETERMINATE body minus exactly those two literals. The
# door's own ``task_id`` slot is already populated by the time it
# reaches a rendered body, so the literal cut against it has to be
# too — matching the constant raw here would silently no-op and
# this equality would then be comparing the wrong pair.
rendered_door = NUDGE_IDLE_TASKS_CHILD_DOOR.replace(NUDGE_IDLE_TASKS_ID_SLOT, "tsk_1")
hedged = self._fmt(child_ws_ids=None)
assert rendered_door in hedged
assert dropped == hedged.replace(NUDGE_IDLE_TASKS_CHILDREN_CAVEAT, "", 1).replace(
rendered_door, "", 1
# The children-bearing body minus exactly the fact line and the
# rendered door. The door's own ``task_id`` slot is already
# populated by the time it reaches a rendered body, so the
# literal cut against it has to be too — matching the constant
# raw here would silently no-op and this equality would then be
# comparing the wrong pair.
rendered_door = (
NUDGE_IDLE_TASKS_CHILD_DOOR.replace(NUDGE_IDLE_TASKS_ID_SLOT, "tsk_1")
.replace(NUDGE_IDLE_TASKS_WAIT_SLOT, wait_call(["child-a"]), 1)
.replace(NUDGE_IDLE_TASKS_CHILD_SLOT, "child-a")
)
# The opening paragraph the caveat cut leaves behind ends at the
# counts line it was appended to — the anchor the literal-replace
# rests on, and the whole reason the constant carries its own
# leading separator.
fact_line = (
chr(10) + "Child child-a is still running; check before redoing anything it owns."
)
childful = self._fmt(children=[("child-a", "running")])
assert rendered_door in childful
assert dropped == childful.replace(fact_line, "", 1).replace(rendered_door, "", 1)
# The opening paragraph the fact-line cut leaves behind ends at
# the counts line the lines rode under.
assert dropped.startswith(
"You still have 2 open tasks: 1 in_progress, 1 pending." + chr(10)
)
@@ -909,67 +1006,52 @@ class TestFormatIdleTasksNudge:
assert kept_fragment in dropped, kept_fragment
def test_populated_child_slots_are_the_other_half_of_the_read(self):
"""One value carries the caveat, the branch AND the branch's two
slots, so a non-empty list moves all of them.
"""One value carries the fact lines, the branch AND the branch's
two slots, so a non-empty list moves all of them.
This is the deliberate consequence of not splitting the read into
a bool beside a list: in production every one of those answers one
two derivations: in production every one of those answers one
storage question, and two derivations of one question are two
things that can disagree which is precisely how the sentence
came to be conditional while the branch below it was not.
things that can disagree which is precisely how the children
sentence once came to be conditional while the branch below it
was not.
"""
populated = self._fmt(child_ws_ids=["child-a", "child-b"])
populated = self._fmt(children=[("child-a", "running"), ("child-b", "idle")])
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in populated
assert "Child child-a is still running" in populated
assert "Child child-b has stopped" in populated
assert NUDGE_IDLE_TASKS_CHILD_SLOT not in populated
# The SCALAR slot takes one id; the LIST slot takes them all,
# because a list has no wrong element to pick.
assert "child_ws_id='child-a'" in populated
assert f" {wait_call(['child-a', 'child-b'])}" in populated
def test_caveat_rides_the_counts_line_as_its_second_sentence(self):
"""The caveat's leading two-space separator makes it the second
sentence of the opening paragraph, exactly as it was the closing
sentence of the paragraph it used to ride."""
kept = self._fmt(child_ws_ids=["child-a"])
def test_fact_lines_ride_directly_under_the_counts_line(self):
"""The opening fact block is one paragraph — the counts line
with the per-child lines directly beneath it, then the blank
line the tail's first element carries."""
kept = self._fmt(children=[("child-a", "running")])
assert kept.startswith(
"You still have 2 open tasks: 1 in_progress, 1 pending. Children of yours"
"You still have 2 open tasks: 1 in_progress, 1 pending." + chr(10) + "Child child-a"
)
def test_indeterminate_children_keeps_the_caveat(self):
"""``None`` is "the read failed", not "no children".
The hedge is unconditionally true, so keeping it can only ever be
uninformative; omitting it turns a failed read into an implied
all-clear. Only an affirmative negative may select omission.
A failed read also populates NOTHING: an id invented from an
unknown answer would put a call the model cannot run in front of
it, which is precisely the failure the ``a1b2c3d4`` constant used
to cause.
"""
hedged = self._fmt(child_ws_ids=None)
assert "may still be running" in hedged
assert hedged.count(NUDGE_IDLE_TASKS_CHILD_SLOT) == 2
assert f" {NUDGE_IDLE_TASKS_WAIT_SLOT}" in hedged
def test_caveat_constant_is_the_tails_own_sentence(self):
"""The constant and the tail are ONE sentence, not two copies.
def test_the_door_is_the_tails_own_literal(self):
"""The constant and the tail are ONE text, not two copies.
If the tail were reworded without the constant (or vice versa),
the literal-anchored removal would silently no-op and every
childless body would ship the caveat again a failure with no
symptom at the call site.
childless body would ship the children branch again a failure
with no symptom at the call site. With the caveat sentence
retired for formatter-built fact lines, the door is the tail's
ONLY children-bearing element, so this is the whole conditional.
"""
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT in NUDGE_IDLE_TASKS_TAIL
assert NUDGE_IDLE_TASKS_TAIL.count(NUDGE_IDLE_TASKS_CHILDREN_CAVEAT) == 1
# It carries its own leading sentence separator, so removing it
# cannot leave a double space or a hanging clause — the tail
# then opens directly on the branch paragraphs.
assert NUDGE_IDLE_TASKS_CHILDREN_CAVEAT.startswith(" ")
stripped = NUDGE_IDLE_TASKS_TAIL.replace(NUDGE_IDLE_TASKS_CHILDREN_CAVEAT, "", 1)
assert stripped.startswith(NUDGE_IDLE_TASKS_OPEN_LIST_SLOT)
assert NUDGE_IDLE_TASKS_CHILD_DOOR in NUDGE_IDLE_TASKS_TAIL
assert NUDGE_IDLE_TASKS_TAIL.count(NUDGE_IDLE_TASKS_CHILD_DOOR) == 1
# The door carries its own leading blank line, so removing it
# cannot leave doubled spacing; and with no caveat in front of
# it the tail opens directly on the open-list slot.
assert NUDGE_IDLE_TASKS_CHILD_DOOR.startswith(chr(10) * 2)
assert NUDGE_IDLE_TASKS_TAIL.startswith(NUDGE_IDLE_TASKS_OPEN_LIST_SLOT)
def test_every_slot_is_present_exactly_where_the_formatter_expects_it(self):
"""The slots and the tail are ONE text, not two copies — the same
@@ -1023,7 +1105,7 @@ class TestFormatIdleTasksNudge:
call = wait_call(ids)
assert call in format_idle_children_nudge([{"ws_id": i, "state": "running"} for i in ids])
assert call in self._fmt(child_ws_ids=ids)
assert call in self._fmt(children=[(ws_id, "running") for ws_id in ids])
def test_ids_populate_every_call_and_nothing_else_does(self):
"""The open ids ride in two places and only two: the block, and
@@ -1067,6 +1149,30 @@ class TestFormatIdleTasksNudge:
]
assert mixed.count("task_id='tsk_good'") == 3
def test_a_hostile_status_drops_its_row_like_a_hostile_id(self):
"""BOTH row fields take the alteration check, symmetrically: the
bullet interpolates the status beside the id, so a status the
strict sanitiser would alter is dropped with its row rather than
mangled into the body. Unreachable through the production
producer ``_open_tasks`` vocabulary-filters statuses so this
pins the formatter's PUBLIC surface, where ``open_task_ids`` is
caller-supplied. The counts stay the producer's mapping and are
untouched by the drop.
"""
newline = chr(10)
out = self._fmt(
open_task_ids=[
("tsk_bad", "pending" + newline + " - tsk_forged (done)"),
("tsk_good", "pending"),
]
)
assert [ln for ln in out.split(newline) if ln.startswith(" - ")] == [
" - tsk_good (pending)"
]
assert "tsk_forged" not in out
assert out.count("task_id='tsk_good'") == 3
assert out.startswith("You still have 2 open tasks: 1 in_progress, 1 pending.")
def test_no_system_reminder_envelope(self):
"""The body is raw text; the wire boundary folds it, not this."""
assert "system-reminder" not in self._fmt()
+100 -74
View File
@@ -23,16 +23,16 @@ event, and both nudges then fire and CO-DELIVER in one drain, tasks
first the grooming instruction is instant, the park instruction is
open-ended, so the batch ends on the wait. Each nudge asserts only its
own domain: the tasks body never claims the children are gone it
carries the "children may still be running" caveat whenever any child
row exists in a live state, because the liveness nudge can be blocked
by its own cap or wait gate while advice fires alone, and on a
coordinator with no children it says nothing about children at all.
Either way a consistent pair is two true statements, not a
contradiction. One ordering caveat, accepted: a cross-bracket pair
(an older queued ``idle_children`` surviving into a bracket that
enqueues ``idle_tasks``) delivers children-first by seq; both entries
are still predicate-valid, so that is a tuning miss, not a correctness
one. If evals show small models fumbling even the ordered pair, the
renders one OBSERVED fact line per child row in a live state (still
running, or stopped with the wait's immediate return noted), because
the liveness nudge can be blocked by its own cap or wait gate while
advice fires alone, and on a coordinator with no children it says
nothing about children at all. Either way a consistent pair is two
true statements, not a contradiction. One ordering caveat, accepted:
a cross-bracket pair (an older queued ``idle_children`` surviving into
a bracket that enqueues ``idle_tasks``) delivers children-first by
seq; both entries are still predicate-valid, so that is a tuning miss,
not a correctness one. If evals show small models fumbling even the ordered pair, the
named upgrade path is a single combined checkpoint type selected at
produce time do NOT reintroduce a cross-domain fire gate.
@@ -78,10 +78,10 @@ Gate order, ``idle_tasks``: coordinator-kind → operator-Stop gate
EVENT closed) cap + cooldown peek asked-operator skip
wait-tool skip envelope read (corrupt no fire; a FAILED read
fails the EVENT closed)
live-child id read (``[]``/ids BODY CONTENT. It runs after every
gate it could otherwise perturb, and what it FINDS never changes
whether this path fires only a read that FAILS does, and that
silences the event, not just this path) permission check
live-child ``(ws_id, state)`` read (``[]``/rows BODY CONTENT. It
runs after every gate it could otherwise perturb, and what it FINDS
never changes whether this path fires only a read that FAILS does,
and that silences the event, not just this path) permission check
(``nudge_allowed``) atomic charge enqueue record.
ONE rule spans both paths, from the owner and unqualified: these
@@ -158,11 +158,12 @@ _ACTIVE_CHILD_STATES: frozenset[str] = frozenset(
# LIVE = the row still describes a child this coordinator has. A
# different question from the one above — "can the model act on it?" —
# and the one the tasks body's children caveat speaks about, so it is a
# strict superset: ``idle`` is in it precisely because an idle child is
# the "may have finished while you worked" disjunct (a child that
# finished with results nobody collected), and ``error`` is in it
# because an errored child still owns the work it was given.
# and the one the tasks body's children fact lines speak about, so it
# is a strict superset: ``idle`` is in it precisely because an idle
# child may hold results nobody collected — the stopped-child fact
# line exists for exactly that row — and ``error`` is in it because an
# errored child still owns the work it was given (and is stopped in
# the same wait-terminal sense).
#
# Enum-derived rather than typed out. ``WorkstreamState`` is exactly
# {idle, thinking, running, attention, error}, and the terminal strings
@@ -446,11 +447,12 @@ class CoordinatorIdleObserver:
put a ``list_workstreams`` round-trip on every coord state
transition in the cluster. The tasks path never GATES on what
children are FOUND each nudge asserts only its own domain;
once its own gates have passed it reads one live-child id list
of its own, to fill in the body it plans, never to decide
whether to fire (:meth:`_live_child_ids_for_body`). Two paths,
two reads, and neither ANSWER is shared: the shape that would
tempt a shared read is the shape that re-couples the domains.
once its own gates have passed it reads one live-child
``(ws_id, state)`` list of its own, to fill in the body it
plans, never to decide whether to fire
(:meth:`_live_children_for_body`). Two paths, two reads, and
neither ANSWER is shared: the shape that would tempt a shared
read is the shape that re-couples the domains.
What the event does share is the failure signal one failed
read anywhere silences both classes which keys on a read
FAILING, never on what it found.
@@ -729,8 +731,10 @@ class CoordinatorIdleObserver:
# enum-derived state, neither of them user-authored text, so there
# is no projection split here any more — the tasks path below keeps
# its two-projection split because its fields ARE user-authored.
# The FE derives its ``ident`` column from ``ws_id`` (the same
# 8-char prefix the body's bullet carries).
# The FE derives its 8-char ``ident`` column from ``ws_id``
# display formatting over the same full id the body's bullet now
# carries whole (a bullet is a handle for the resolver, which
# refuses prefixes; an ident is a label for the operator's eye).
children_meta = [
{
"ws_id": c.get("ws_id", ""),
@@ -792,13 +796,14 @@ class CoordinatorIdleObserver:
same-event ``idle_children`` (co-delivery, tasks first) or alone
while children run and the liveness nudge is blocked by its own
cap or wait gate. The body never presumes the children are done
(its "children may still be running" caveat plus the
blocked-on-a-child branch are what make advice-alone honest
beside running children), and its drain predicate reads no
children at all. The one children read this path makes
(:meth:`_live_child_ids_for_body`) is the post-gate live-child id
list that selects between the children-aware body and the
childless one and populates the former's slots.
(its per-child observed fact lines plus the blocked-on-a-child
branch are what make advice-alone honest beside running
children), and its drain predicate reads no children at all.
The one children read this path makes
(:meth:`_live_children_for_body`) is the post-gate live-child
``(ws_id, state)`` list that selects between the children-aware
body and the childless one, feeds the fact lines, and populates
the branch's slots.
It is NOT true that no outcome of that read can suppress the
fire, and the earlier absolute saying so was wrong. A read that
@@ -1005,9 +1010,12 @@ class CoordinatorIdleObserver:
# which buys more storage traffic, aimed at the thing that just
# failed. Firing there adds load exactly where load is the
# problem. The hedge's own justification also dissolves under
# the prior question: the caveat exists to keep the BODY safe
# when children are unknown, but not sending a body is safe
# too — strictly safer, and free.
# the prior question: a hedge exists to keep the BODY safe when
# children are unknown, but not sending a body is safe too —
# strictly safer, and free. (The same ruling later removed the
# hedge from the SUCCESSFUL-read body: the states are observed,
# so the body renders one fact line per child instead of "may
# still be running or may have finished".)
#
# BEFORE ANY CHARGE, structurally. Plans carry no side effects,
# and ``_on_idle`` reaches :meth:`_commit_plan` — where
@@ -1029,8 +1037,13 @@ class CoordinatorIdleObserver:
# coord that gained a child inside that window — narrow, and
# one-directional: a body with no children content asserts
# NOTHING about children, while carrying it costs a measured
# ``list_workstreams`` round-trip on every childless bracket. A
# NEW out-of-band creator class (task-agent lanes) widens the
# ``list_workstreams`` round-trip on every childless bracket.
# A fact line can likewise outlive its state (a running child
# idles before delivery), and that lands safe in the same
# direction: the running line's instruction is check-before-
# redoing, which is exactly right for a child that has just
# finished, and an idle child does not restart. A NEW
# out-of-band creator class (task-agent lanes) widens the
# window and must re-visit this condition.
#
# Cost: one ``list_workstreams`` fetch per IDLE event that clears
@@ -1039,14 +1052,14 @@ class CoordinatorIdleObserver:
# needed ids to populate its calls with; the rate is unchanged and
# the per-read cost is now the liveness path's, which is the trade
# the discovery round-trip it removes is worth (see
# :meth:`_live_child_ids_for_body`).
child_ws_ids = self._live_child_ids_for_body(ws)
if child_ws_ids is None:
# :meth:`_live_children_for_body`).
children = self._live_children_for_body(ws)
if children is None:
raise _StorageReadError("live_children")
text = format_idle_tasks_nudge(
open_counts,
open_task_ids=open_task_ids,
child_ws_ids=child_ws_ids,
children=children,
)
# Bind identity by closure (never the live ``ws``). The
@@ -1080,10 +1093,13 @@ class CoordinatorIdleObserver:
# No children read, restated here because the temptation
# recurs: an entry that outlives a bracket, survives a Stop
# demotion, or is resurrected by a failed wake and then
# delivers beside live children is two true statements, not
# a contradiction — a caveat-bearing body covers that state
# explicitly, and a caveat-less one (enqueued when no live
# child row existed) asserts nothing about children at all.
# delivers beside live children stays two true-enough
# statements, not a contradiction — a children-bearing
# body's fact lines state what was observed at enqueue and
# their protections land safe under drift (a running child
# that idled still deserves check-before-redoing; an idle
# one does not restart), and a childless body asserts
# nothing about children at all.
#
# Corrupt envelope → drop, and a FAILED read → drop: a
# failed storage read never delivers a nudge, at drain
@@ -1156,7 +1172,7 @@ class CoordinatorIdleObserver:
This read serves the LIVENESS path only, and nothing here may be
made to license or block the ADVICE fire on what it FINDS
advice makes its own read (:meth:`_live_child_ids_for_body`),
advice makes its own read (:meth:`_live_children_for_body`),
after its own gates, and a found-children outcome that skipped an
advice fire would be the re-coupling this warning forbids. A
FAILED read is the one outcome that leaves this path's scope,
@@ -1252,10 +1268,12 @@ class CoordinatorIdleObserver:
param into the aggregate (protocol change) or accept that the
divergence lands in the safe direction liveness over-delivers,
and the body's live-child read that shares this asymmetry
(:meth:`_live_child_ids_for_body`) over-reports children, which
keeps a hedge that is unconditionally true and adds a ws_id to a
``mode="any"`` wait that returns on the first finisher anyway.
The advice DRAIN predicate reads no children at all.
(:meth:`_live_children_for_body`) over-includes rows, which adds
a fact line that is still TRUE of the row it names (the line
renders the row's own observed state; only its kind diverged)
and a ws_id to a ``mode="any"`` wait that returns on the first
finisher anyway. The advice DRAIN predicate reads no children
at all.
"""
try:
counts = self._storage.count_workstreams_by_state(
@@ -1271,19 +1289,22 @@ class CoordinatorIdleObserver:
return None
return any(counts.get(s, 0) > 0 for s in _ACTIVE_CHILD_STATES)
def _live_child_ids_for_body(self, ws: Workstream) -> list[str] | None:
def _live_children_for_body(self, ws: Workstream) -> list[tuple[str, str]] | None:
"""Enqueue-time answer to "which child rows of this coord are in
a live state?", for the ``idle_tasks`` BODY only.
a live state, and in which?", for the ``idle_tasks`` BODY only.
A (possibly empty) list of ``ws_id``\\ s on a successful read;
``None`` when the read was INDETERMINATE (the query raised, or a
row was too ragged to classify). The THREE-WAY return is the
point, and each value has a different consumer:
A (possibly empty) list of ``(ws_id, state)`` pairs on a
successful read; ``None`` when the read was INDETERMINATE (the
query raised, or a row was too ragged to classify). The
THREE-WAY return is the point, and each value has a different
consumer:
* ``[]`` and a NON-EMPTY list both reach
:func:`~turnstone.core.metacognition.format_idle_tasks_nudge`
and choose between the childless body and the children-aware
one. These are the only two values production ever hands it.
one. These are the only two values production ever hands it,
and since the formatter's children parameter became a required
list they are the only two it can express.
* ``None`` never reaches the formatter at all: the caller raises
it to the event level, before any charge, and NEITHER nudge is
sent a failed storage read fails the whole event closed
@@ -1296,20 +1317,24 @@ class CoordinatorIdleObserver:
both falsy and mean opposite things: one is an answer, the other
is the absence of one.
It returns IDS rather than the bool it once did because the body
now populates two child slots from them (the blocked-on-a-child
branch's ``child_ws_id`` and its ``wait_for_workstream`` call).
One value serves the caveat and the slots deliberately: a bool
beside a list is two derivations of one storage read, and the
state where they disagree hedge kept, slots empty, or worse the
reverse is unreachable when there is only one value.
It returns ``(ws_id, state)`` PAIRS rather than the bare ids it
once did because the body renders the observed STATE per child
(the fact lines) as well as populating the blocked-on-a-child
branch's two slots. Bare ids forced the body to hedge about
states this very query had just read "may still be running or
may have finished" — which is manufactured uncertainty, ruled
out 2026-07-29: the harness renders the fact it holds. One
value serves the fact lines and the slots deliberately: a
second derivation of the same storage read is a state where the
two can disagree, which is unreachable when there is only one
value.
Deliberately :data:`_LIVE_CHILD_STATES`, not
:data:`_ACTIVE_CHILD_STATES`: the caveat speaks about children
that EXIST, and an idle child holding results nobody collected
is exactly its "may have finished while you worked" disjunct.
Conditioning on the active set would strip the hedge from the
one state whose protective disjunct is live.
:data:`_ACTIVE_CHILD_STATES`: the fact lines speak about
children that EXIST, and an idle child holding results nobody
collected is exactly what the stopped-child line protects.
Conditioning on the active set would drop the line from the one
state whose protection is live.
COST, changed and stated: this was a ``count_workstreams_by_state``
aggregate while a bool was enough. Ids need rows, so it is now a
@@ -1355,15 +1380,16 @@ class CoordinatorIdleObserver:
parent_ws_id=ws.id,
user_id=ws.user_id,
)
out: list[str] = []
out: list[tuple[str, str]] = []
for row in rows:
mapping = getattr(row, "_mapping", row)
if mapping["state"] not in _LIVE_CHILD_STATES:
state = mapping["state"]
if state not in _LIVE_CHILD_STATES:
continue
out.append(mapping["ws_id"])
out.append((mapping["ws_id"], state))
except Exception:
log.debug(
"coord_idle_observer.caveat_list_failed ws=%s",
"coord_idle_observer.body_children_list_failed ws=%s",
ws.id[:8],
exc_info=True,
)
@@ -1191,9 +1191,12 @@ function createCoordinatorPane(root, wsId, opts) {
// vocabulary per surface-kind, not one rule for both cards.
function appendIdleChildren(meta) {
const children = Array.isArray(meta.children) ? meta.children : [];
// The ws_id prefix rides as `ident` — the same 8 chars the
// model-facing body's bullet carries — and is the row's identity
// column; a fresh row renders ident + state and nothing else.
// The ws_id's first 8 chars ride as `ident` — display formatting
// over the same full id the model-facing body's bullet carries
// whole (the bullet is a handle for the resolver, which refuses
// prefixes; this column is a label for the operator's eye) — and
// `ident` is the row's identity column; a fresh row renders
// ident + state and nothing else.
const rows = children.map((c) => ({
ident: c && c.ws_id ? String(c.ws_id).slice(0, 8) : "",
name: c && c.name ? String(c.name) : "",
+302 -206
View File
@@ -15,6 +15,9 @@ from __future__ import annotations
import re
import time
from typing import Any
from turnstone.core.workstream import WorkstreamState
# Default cooldown (s) between nudges of the same type. Production
# paths pass ``cooldown_secs`` explicitly from
@@ -243,8 +246,8 @@ def wait_call(ws_ids: list[str]) -> str:
``prompts/tools_coordinator.md`` uses) while ``mode`` is spelled with
double quotes. Both are valid in the call syntax the model emits,
and this exact byte sequence is what the roster has shipped and been
tuned against; re-quoting it is a body change and belongs in a sweep,
not in a refactor.
measured with; re-quoting it is a body change and belongs in a
sweep, not in a refactor.
Callers cap *ws_ids* at :data:`NUDGE_IDLE_CHILDREN_WAIT_CAP` before
calling the cap is the executor's ``WAIT_MAX_WS_IDS``, so an
@@ -253,7 +256,8 @@ def wait_call(ws_ids: list[str]) -> str:
return f'wait_for_workstream(ws_ids={ws_ids!r}, mode="any", timeout=120)'
# The ``idle_children`` header states FACTS ONLY — no imperative.
# The ``idle_children`` header states FACTS ONLY — no imperative, and
# only the fact the drain predicate re-verifies.
#
# It carried "Either continue the user's work or block on the listed
# children explicitly:" for most of this feature's life. Compressing
@@ -263,20 +267,28 @@ def wait_call(ws_ids: list[str]) -> str:
# advice body's first paragraph exists to deny. A liveness wake must
# never be the thing that authorises continuing.
#
# So the header now only reports the situation and the roster; the
# formatter's trailing line supplies the one actionable call
# It then opened with "You are idle." until 2026-07-29, and that
# sentence went for the honesty half of the same rule: a queued
# ``idle_children`` entry delivers at whichever seam arrives next, and
# its drain predicate re-verifies that CHILDREN are still active —
# never that the coordinator is still idle — so the harness was
# asserting a state it does not hold at delivery time. The body now
# opens with the drain-verified fact and nothing else.
#
# So the header only reports the roster's claim; the formatter's
# trailing line supplies the one actionable call
# (``To block on them: wait_for_workstream(...)``). Deciding what to
# do with a live child is the model's call on the evidence, not this
# message's to grant.
NUDGE_IDLE_CHILDREN_HEADER = "You are idle. These child workstreams are still active:"
NUDGE_IDLE_CHILDREN_HEADER = "These child workstreams are still active:"
# The ``idle_tasks`` body. TUNED AGAINST THE BEHAVIORAL EVAL
# (``turnstone-eval --nudges``) — deepseek-v4-flash and qwen3.6-27B.
# Reword freely, but re-run the eval: every property below is here
# because measuring it MOVED the numbers (or removed a paragraph that
# measured as carrying nothing), and the whole point of the body is
# behavioural, not stylistic.
# The ``idle_tasks`` body. Under active development, MEASURED AGAINST
# THE BEHAVIORAL EVAL (``turnstone-eval --nudges``) — deepseek-v4-flash
# and qwen3.6-27B so far. Reword freely, but re-run the eval: every
# property below is here because measuring it MOVED the numbers (or
# removed a paragraph that measured as carrying nothing), and the whole
# point of the body is behavioural, not stylistic.
#
# Four properties are load-bearing:
#
@@ -346,44 +358,46 @@ NUDGE_IDLE_CHILDREN_HEADER = "You are idle. These child workstreams are still a
# branch-scope confusion — the model reading "is this done?" as a
# judgement only the user may make — NOT missing permission; a
# bare "you may mark it done" clause moved nothing.
# 4. It never asserts the children are gone, and it never instructs
# about children it knows are absent. ONE observed fact governs
# BOTH children-bearing elements — the caveat sentence and the
# 4. Children content is OBSERVED FACT, never hedge, and the body
# never instructs about children it knows are absent. ONE observed
# read governs BOTH children-bearing elements — the per-child fact
# lines the formatter builds under the counts line and the
# blocked-on-a-child branch — because they are one claim in two
# registers, and splitting them produced exactly the contradiction
# you would predict: for one release the sentence was correctly
# omitted for a childless coordinator while the branch below it
# still said "if an item is waiting on a child workstream still
# running", followed by two calls that coordinator could not make.
# This nudge can co-deliver beside an ``idle_children`` wake (tasks
# first) or fire ALONE while children run — the liveness nudge can
# be blocked by its own cap or wait gate — so both elements are
# carried whenever any child row exists in a live state: the body
# never asserts children are gone, and the hedge it does carry is
# unconditionally true. (A FAILED children read renders no body
# at all — the observer fails its whole event closed.)
# Deleting either from the CHILDREN-PRESENT body reopens the
# you would predict: for one release the children sentence was
# correctly omitted for a childless coordinator while the branch
# below it still said "if an item is waiting on a child workstream
# still running", followed by two calls that coordinator could not
# make. This nudge can co-deliver beside an ``idle_children`` wake
# (tasks first) or fire ALONE while children run — the liveness
# nudge can be blocked by its own cap or wait gate — so the fact
# lines and the branch are carried whenever any child row exists in
# a live state. (A FAILED children read renders no body at all —
# the observer fails its whole event closed.)
# The fact lines replaced a hedged caveat sentence ("Children of
# yours may still be running or may have finished while you
# worked...") on a 2026-07-29 ruling: the harness must never render
# manufactured uncertainty or manufactured context when it holds
# the observed fact. "may still be running or may have finished"
# hedged states the producer's read had JUST RETURNED, and "while
# you worked" invented activity for a coordinator that was idle.
# The producer threads ``(ws_id, state)`` per child and the
# formatter renders that fact and nothing more: a running child is
# reported running (check before redoing what it owns), a stopped
# one is reported stopped (``wait_for_workstream`` returns
# immediately for it) — the protective points the hedge carried,
# each now attached to the child it is true of, and nothing about
# results, whose existence no read observed. Deleting either children-bearing element
# from the CHILDREN-PRESENT body reopens the
# resume-over-live-children hazard the old cross-domain fire gate
# existed for. On a coordinator with no children both are measured
# noise (round-5 sweep: the sentence induces a ``list_workstreams``
# round-trip that is the entire nudge-arm failure mode on the
# childless cells) and both are omitted — an omission asserts
# nothing about children at all. A body with NOTHING about children
# in it is also what makes the eval's ablation arm a clean
# single-factor question: does this body need to mention children?
#
# The caveat is its own constant so the omission is a LITERAL-anchored
# removal rather than a positional cut, and so the two forms of the body
# cannot drift apart: there is one sentence, spliced in here and spliced
# out by :func:`format_idle_tasks_nudge`. It carries its own leading
# two-space sentence separator, so it reads as the second sentence of
# the counts line it now rides, and removing it leaves "…N open
# task(s): …\n" — the opening paragraph both forms of the body share.
NUDGE_IDLE_TASKS_CHILDREN_CAVEAT = (
" Children of yours may still be running or may have finished "
"while you worked; check before redoing anything a child owns — "
"wait_for_workstream returns immediately for a finished child."
)
# noise (round-5 sweep: children prose induces a
# ``list_workstreams`` round-trip that is the entire nudge-arm
# failure mode on the childless cells) and both are omitted — an
# omission asserts nothing about children at all. A body with
# NOTHING about children in it is also what makes the eval's
# ablation arm a clean single-factor question: does this body need
# to mention children?
# THE SLOTS. Each is a literal that the shipped tail contains and
# :func:`format_idle_tasks_nudge` substitutes at render time, exactly as
@@ -410,12 +424,14 @@ NUDGE_IDLE_TASKS_CHILDREN_CAVEAT = (
# honest recovery — the harness holds no id and says so.
# * :data:`NUDGE_IDLE_TASKS_CHILD_SLOT` is a pure TEMPLATE VARIABLE.
# No production body can contain it. The branch it sits in renders
# only when the caller passed live child ids, and it is substituted
# whenever it renders; the states that would leave it unsubstituted
# are an INDETERMINATE children read (the observer now declines to
# fire at all, so the formatter never sees it) and a live child row
# with an empty ``ws_id`` (not producible — every creation path mints
# through ``uuid4().hex`` or ``secrets.token_hex``).
# only when the caller passed child rows, and it is substituted
# whenever it renders; the one state that would leave it
# unsubstituted is a live child row with an empty ``ws_id`` (not
# producible — every creation path mints through ``uuid4().hex`` or
# ``secrets.token_hex``). The INDETERMINATE-read state that once
# rendered it is no longer even expressible: the formatter takes a
# required children list, and a failed read renders no body at all
# (the observer fails its whole event closed).
#
# The constant therefore stays for a STRUCTURAL reason and not a
# defensive one: the branch lives inside :data:`NUDGE_IDLE_TASKS_TAIL`,
@@ -445,31 +461,24 @@ NUDGE_IDLE_TASKS_OPEN_LIST_SLOT = "\n\n - <your open task ids appear here, one
# THE BLOCKED-ON-A-CHILD BRANCH, whole — prose, link call and wait call.
#
# Conditional on EXACTLY the children caveat's condition, and its own
# constant for exactly the caveat's reason: so the removal is a
# LITERAL-anchored cut rather than a positional one, and so the two forms
# of the body cannot drift apart.
# Conditional on EXACTLY the children fact lines' condition (any child
# row passed), and its own constant so the removal is a LITERAL-anchored
# cut rather than a positional one, and so the two forms of the body
# cannot drift apart.
#
# It was unconditional for one release, and that was an inconsistency
# with a live cost: a coordinator with no children had the caveat
# SENTENCE about children correctly omitted two paragraphs above, and
# then read an INSTRUCTION about children — "if an item is waiting on a
# child workstream still running" — followed by two calls it could not
# make, pointing at a lookup that returns nothing. Omitting the sentence
# while keeping the instruction is the same defect the caveat conditional
# exists to fix, left standing one block below it. One observed fact now
# governs every children-bearing element of this body.
# with a live cost: a coordinator with no children had the children
# SENTENCE correctly omitted two paragraphs above, and then read an
# INSTRUCTION about children — "if an item is waiting on a child
# workstream still running" — followed by two calls it could not make,
# pointing at a lookup that returns nothing. Omitting the facts while
# keeping the instruction is the same defect the fact lines' conditional
# exists to fix, left standing one block below it. One observed read
# now governs every children-bearing element of this body.
#
# Same fail-safe direction as the caveat, and it is the direction that
# makes the branch's placeholder legitimate: an INDETERMINATE read keeps
# the branch, because the harness cannot then rule out a child, and only
# omitting it can be wrong. On that path the model is pointed at
# ``list_workstreams()`` — a lookup that may well return rows, since the
# read that failed is the harness's, not the model's.
#
# Carries its own leading blank line (the caveat's idiom), so the cut
# leaves "…not queued for confirmation.\n\nIf the next step is yours to
# take, take it." — the paragraph seam both forms share.
# Carries its own leading blank line (the open-list slot's idiom), so
# the cut leaves "…not queued for confirmation.\n\nIf the next step is
# yours to take, take it." — the paragraph seam both forms share.
NUDGE_IDLE_TASKS_CHILD_DOOR = (
"\n"
"\n"
@@ -482,15 +491,16 @@ NUDGE_IDLE_TASKS_CHILD_DOOR = (
f" {NUDGE_IDLE_TASKS_WAIT_SLOT}"
)
# Everything in the ``idle_tasks`` body AFTER the counts line the
# formatter builds: the conditional children caveat, the open-id block,
# then the typed branches — of which THREE carry a ``tasks(...)`` call
# and one ("If the next step is yours to take, take it.") deliberately
# carries none. Named for its position because that is its contract —
# the formatter owns the opener, this constant owns the rest, and the
# seam between them is the one place the body is assembled.
# Everything in the ``idle_tasks`` body AFTER the opening fact block the
# formatter builds (the counts line plus the per-child fact lines): the
# open-id block, then the typed branches — of which THREE carry a
# ``tasks(...)`` call and one ("If the next step is yours to take, take
# it.") deliberately carries none. Named for its position because that
# is its contract — the formatter owns the opener and the children fact
# lines (FACTS ARE HARNESS-RENDERED, never part of the overridable
# tail), this constant owns the rest, and the seam between them is the
# one place the body is assembled.
NUDGE_IDLE_TASKS_TAIL = (
f"{NUDGE_IDLE_TASKS_CHILDREN_CAVEAT}"
f"{NUDGE_IDLE_TASKS_OPEN_LIST_SLOT}\n"
"\n"
"If the next step needs the user — a decision, an approval, a "
@@ -519,6 +529,25 @@ NUDGE_IDLE_TASKS_TAIL = (
)
# The fact-line split for the tasks body's per-child lines, anchored on
# WAIT-TERMINALITY because that is the claim the stopped line makes:
# ``wait_for_workstream`` treats ``idle`` and ``error`` as
# already-terminal and returns immediately for them (its full terminal
# vocabulary also carries the non-enum strings ``closed`` / ``deleted``,
# which the observer's live filter already excludes from this pipeline).
# Enum-derived so the membership cannot drift from the state vocabulary
# by a typo. A state ADDED to ``WorkstreamState`` defaults to the
# RUNNING line, and that default is the accurate class for it: the
# wait's terminal set is a fixed vocabulary
# (``coordinator_client.WAIT_REAL_TERMINAL_STATES``, not importable here
# — console sits above core), so a new state cannot be one the wait
# returns immediately for. If that terminal vocabulary ever grows,
# classify the new state here in the same change.
NUDGE_CHILD_STOPPED_STATES: frozenset[str] = frozenset(
{WorkstreamState.IDLE.value, WorkstreamState.ERROR.value}
)
# ASCII control chars + Unicode steering vectors (bidi-override,
# zero-width, line/paragraph separators, BOM, tag chars). Treated
# uniformly as control chars and replaced with a space; angle-bracket
@@ -586,13 +615,16 @@ def sanitize_name(text: str) -> str:
where embedded newlines would forge a fake sibling row and angle
brackets would steer the reasoning channel.
NEITHER idle formatter calls this any more
:func:`format_idle_children_nudge` is ids-and-states only, and
:func:`format_idle_tasks_nudge` is counts-and-branches only, both
all server-minted but this function is the mandatory route for
any model- or user-authored field either body ever grows (the
belt-and-braces rule in both formatters' docstrings), which is why
it stays although no production path currently calls it.
NEITHER idle formatter interpolates a sanitised projection any
more :func:`format_idle_children_nudge` is ids-and-states only,
and :func:`format_idle_tasks_nudge` is counts, ids, states and
branches only, all server-minted. The one remaining call is
:func:`format_idle_tasks_nudge` using this as an ALTERATION CHECK
over its open-row fields (a value this function would change is
dropped, never mangled into the body). This function stays the
mandatory route for any model- or user-authored field either body
ever grows (the belt-and-braces rule in both formatters'
docstrings).
"""
if not text:
return ""
@@ -662,19 +694,34 @@ def sanitize_display(text: str) -> str:
return _NAME_CONTROL_CHARS.sub(" ", text).strip()
def format_idle_children_nudge(children: list[dict[str, str]]) -> str:
def format_idle_children_nudge(children: list[dict[str, Any]]) -> str:
"""Render the ``idle_children`` reminder body — ids and states ONLY.
*children* is a list of dicts carrying at least ``ws_id`` and
``state`` the row-mapping shape coordinator-side storage exposes.
Returns raw text *without* any envelope; the nudge is emitted as a
first-class ``{"role": "system"}`` turn whose content is this text
(folded to a ``[start system-reminder]`` block at the wire boundary on
non-native models).
``state`` as strings the observer's row projection and the eval's
fixture projection both satisfy it; every OTHER key a row carries
(``name`` above all) is ignored, which is why the annotation is
``dict[str, Any]`` rather than a narrower shape that only the
projections happen to hold. Returns raw text *without* any
envelope; the nudge is emitted as a first-class
``{"role": "system"}`` turn whose content is this text (folded to a
``[start system-reminder]`` block at the wire boundary on non-native
models).
Every interpolated value is SERVER-MINTED: the 8-char ``ws_id``
prefix per row, the enum-derived state, and the full ``ws_id``\\ s in
the trailing ``wait_for_workstream`` suggestion. Child NAMES are
Bullets carry the FULL ``ws_id``. The roster's documented purpose
is to hand the model HANDLES it can act on inspect, message, wait
and ``CoordinatorClient._resolve_ws_ref`` refuses truncated ids by
design (near-miss ids are NEVER auto-resolved), so an 8-char prefix
here was not a handle: a model that copied a bullet issued a call
the resolver rejects, while the full id sat one line down in the
wait suggestion. Bullet and wait line now carry the same full id.
Prefixing for READABILITY is a display concern and lives where
display belongs the FE derives its 8-char ident from the card
metadata's full ``ws_id``.
Every interpolated value is SERVER-MINTED: the full ``ws_id`` and
the enum-derived state per row, and the full ``ws_id``\\ s in the
trailing ``wait_for_workstream`` suggestion. Child NAMES are
model-authored (a coordinator names its children when it spawns
them) and are deliberately NOT rendered interpolating them lowered
the plant's own output back into a trusted system turn, where a
@@ -703,7 +750,7 @@ def format_idle_children_nudge(children: list[dict[str, str]]) -> str:
for c in shown:
ws_id = c.get("ws_id", "")
state = c.get("state", "?")
lines.append(f" - {ws_id[:8]} ({state})")
lines.append(f" - {ws_id} ({state})")
overflow = len(children) - len(shown)
if overflow > 0:
lines.append(f" ...and {overflow} more")
@@ -791,22 +838,24 @@ def format_idle_tasks_nudge(
open_counts: dict[str, int],
*,
open_task_ids: list[tuple[str, str]],
child_ws_ids: list[str] | None,
children: list[tuple[str, str]],
) -> str:
"""Render the ``idle_tasks`` reminder body — counts, open IDS and
typed branches, NO task text.
"""Render the ``idle_tasks`` reminder body — counts, open IDS,
per-child fact lines and typed branches, NO task text.
The opener is one counts line ("You still have N open task(s): X
in_progress, Y pending"), and that line IS the situation statement:
the provenance paragraph that used to carry it was pruned on the
round-8 numbers (its ablation showed no isolated effect on the
correct wire), and the TITLES the roster carried went with it (the
counts candidate matched or beat the roster on every childless cell
with zero forbidden actions). The rest of the body is
:data:`NUDGE_IDLE_TASKS_TAIL` the conditional children caveat, the
open-id block, and the typed branches, one of which (the
blocked-on-a-child one) is itself conditional on the same fact the
caveat is.
The opening fact block is FORMATTER-BUILT: one counts line ("You
still have N open task(s): X in_progress, Y pending") followed by
one observed-fact line per child row (below). The counts line IS
the situation statement: the provenance paragraph that used to carry
it was pruned on the round-8 numbers (its ablation showed no
isolated effect on the correct wire), and the TITLES the roster
carried went with it (the counts candidate matched or beat the
roster on every childless cell with zero forbidden actions). The
rest of the body is :data:`NUDGE_IDLE_TASKS_TAIL` the open-id
block and the typed branches, one of which (the blocked-on-a-child
one) is conditional on the same children rows the fact lines render.
Facts are harness-rendered and never part of the overridable tail;
the tail carries the typed branches.
*open_counts* maps each OPEN task status to how many of the
coordinator's tasks hold it. The producer derives it from its own
@@ -828,21 +877,27 @@ def format_idle_tasks_nudge(
association an id needs is already in its transcript
(``tasks(action='add')`` answers with id AND title).
A row is USABLE only when its id is non-empty and survives
:func:`sanitize_name` unchanged. Task ids are server-minted at the
write path (``tsk_`` + ``secrets.token_hex``) but are read back out
of a JSON blob that a hand-edited DB or an older writer can leave
ragged, so this is the belt-and-braces route applied where it can
also be honest about executability: an id the sanitiser would ALTER
is dropped rather than mangled, because a mangled id renders a call
that cannot resolve the precise failure the invented
``child_ws_id='a1b2c3d4'`` constant used to cause. With no usable
row the block is removed and the branches keep
:data:`NUDGE_IDLE_TASKS_ID_SLOT` one of the two states in which
this body still asks for a discovery round-trip (the other is the
eval override's cut-nothing ``None``; production's failed read no
longer renders a body at all), and in both the round-trip is the
honest answer because the harness genuinely holds no usable id.
A row is USABLE only when its id is non-empty and BOTH its fields
id and status survive :func:`sanitize_name` unchanged. Task ids
are server-minted at the write path (``tsk_`` +
``secrets.token_hex``) and statuses are vocabulary-checked there,
but both are read back out of a JSON blob that a hand-edited DB or
an older writer can leave ragged, so this is the belt-and-braces
route applied where it can also be honest about executability: a
value the sanitiser would ALTER drops its row rather than being
mangled into the body a mangled id renders a call that cannot
resolve, the precise failure the invented ``child_ws_id='a1b2c3d4'``
constant used to cause, and a mangled status would misreport the
stored state. (Through the production producer the status half is
inert: ``CoordinatorIdleObserver._open_tasks`` already drops any row
whose status is outside ``TASK_OPEN_STATUSES``. The guard is for
this function's PUBLIC surface, where ``open_task_ids`` is
caller-supplied.) With no usable row the block is removed and the
branches keep :data:`NUDGE_IDLE_TASKS_ID_SLOT` the ONE state in
which this body still asks for a discovery round-trip (production's
failed read no longer renders a body at all), and there the
round-trip is the honest answer because the harness genuinely holds
no usable id.
The single branch example prefers an ``in_progress`` row, falling
back to the first usable one. It is one id across every branch call
@@ -851,71 +906,90 @@ def format_idle_tasks_nudge(
while a per-branch id would read as the harness ruling which row is
done.
*child_ws_ids* is keyword-only and required, and it is the SINGLE
value governing every children-bearing element of this body the
caveat sentence, the blocked-on-a-child branch, and that branch's two
slots. One value because one storage read answers all of them, and
because a body that omits the sentence about children while keeping
the instruction about children is a contradiction the reader has to
resolve. The trichotomy is exactly the one the caller's read
produces:
*children* is keyword-only and REQUIRED a list of
``(ws_id, state)`` pairs, the exact projection both callers hold
(the observer's ``_live_children_for_body`` over live-state rows and
the eval's ``_body_children`` over fixture rows) — and it is the
SINGLE value governing every children-bearing element of this body:
the per-child fact lines, the blocked-on-a-child branch, and that
branch's two slots. One value because one storage read answers all
of them, and because a body that renders facts about children while
omitting the instruction about them (or the reverse) is a
contradiction the reader has to resolve. Two states, exactly the
two the caller's read produces:
* ``[]`` an affirmative "this coordinator has no child row in a
live state" — removes :data:`NUDGE_IDLE_TASKS_CHILDREN_CAVEAT` AND
:data:`NUDGE_IDLE_TASKS_CHILD_DOOR`, both by literal match rather
than by position, so a reworded neighbour cannot shift either cut.
Nothing about children survives: no sentence, no branch, no
live state" — renders no fact lines and removes
:data:`NUDGE_IDLE_TASKS_CHILD_DOOR` by literal match rather than
by position, so a reworded neighbour cannot shift the cut.
Nothing about children survives: no facts, no branch, no
placeholder pointing at a lookup that is known to return nothing.
* a NON-EMPTY list keeps both and populates the branch: the wait call
takes every id (capped at :data:`NUDGE_IDLE_CHILDREN_WAIT_CAP`,
``mode="any"``), which is unambiguous because a list slot has no
wrong element to pick, and ``child_ws_id`` takes the first most
recently updated, since the caller's query orders by
``updated DESC``.
* ``None`` "I am not asserting a children state" keeps both and
substitutes neither slot. PRODUCTION NEVER PASSES THIS. The
observer used to, for an indeterminate read, and now fails its
whole event closed instead a failed storage read silences BOTH
idle nudges before either cap is charged: a nudge fired off a
query that just failed is a reminder we cannot substantiate,
aimed at a backend that is telling us it is unwell. Not sending
is as safe as hedging and strictly cheaper, so the hedge lost its
reason.
* a NON-EMPTY list renders ONE FACT LINE PER CHILD under the counts
line and populates the branch. Each line states the observed
fact and nothing more the states are the caller's read, taken
this same event, so hedging them would be manufactured
uncertainty:
The branch stays because it has a live caller:
``turnstone.eval.nudges.render_tasks_body`` passes it under
``--body-override``, where the tail is operator-authored candidate
text and BOTH literal cuts must be suppressed so a candidate that
quotes the shipped caveat verbatim is not silently mauled. ``None``
is the only input that says "cut nothing" without also inventing a
ws_id to substitute. If that caller ever goes, so should this
branch it is not kept for safety.
- a child whose state is outside
:data:`NUDGE_CHILD_STOPPED_STATES` (thinking / running /
attention today) is STILL RUNNING; the line says so and carries
the check-before-redoing protection.
- a child in :data:`NUDGE_CHILD_STOPPED_STATES` (idle / error)
has STOPPED, and the line carries the wait-returns-immediately
point, true for exactly those states (they are the wait's live
terminal states). It asserts NOTHING about results: "may hold
uncollected results" was cut as a fabrication (2026-07-29) —
"results" is a noun the read never observed, and "uncollected"
implies a collection ledger nobody consulted; hedging an entity
into existence with "may" is still fabrication. The immediate
wait is the whole protection: checking is cheap, and whatever
the child did or did not produce is what the check finds.
Populating the child slots asserts nothing about the TASK: it fills
the only values a call there could take. Whether any item is in
fact waiting on a child stays the model's judgement, which is why
the branch is still a conditional sentence.
The branch's wait call takes every id (capped at
:data:`NUDGE_IDLE_CHILDREN_WAIT_CAP`, ``mode="any"``), which is
unambiguous because a list slot has no wrong element to pick, and
``child_ws_id`` takes the first most recently updated, since
the caller's query orders by ``updated DESC``.
NO SANITISER runs on the counts, the statuses or the ws_ids: the
counts are integers, the statuses are the producer's own
``TASK_OPEN_STATUSES`` vocabulary, and the ws_ids come from the
workstreams table's primary key — the same server-minted source
:func:`format_idle_children_nudge` already interpolates raw into its
wait call, so a stricter rule here would be an asymmetry with no
reason behind it. Task ids take the sanitiser as an
ALTERATION CHECK (above) because their store is softer.
BELT-AND-BRACES RULE (the :func:`format_idle_children_nudge`
precedent): any model- or user-authored field this body ever grows
MUST go back through :func:`sanitize_name` before interpolation
server-minted provenance is the only exemption.
``None`` is NOT an input any more. The old "I am not asserting a
children state" hedge branch lost production when the observer began
failing its whole event closed on an indeterminate read, and lost
its last caller (the eval override's cut-nothing mapping) when the
override sweep started refusing childless cells at config time so
it died with the rule its own docstring predicted it would. A
failed read renders no body at all; there is nothing left for a
hedge to cover.
Fact lines and slot population assert nothing about the TASKS: they
state each child's observed state and fill the only values a call
there could take. Whether any item is in fact waiting on a child
stays the model's judgement, which is why the branch is still a
conditional sentence. Ids render FULL the resolver refuses
truncated ids by design, and a fact line's purpose is to pair a
usable handle with the fact about it.
NO SANITISER runs on the counts line or the children values: the
counts are integers, the counts line's status terms are the
producer's own mapping over ``TASK_OPEN_STATUSES`` (never row data),
and the child ws_ids and states come from the workstreams table's
primary key and the state enum the same server-minted sources
:func:`format_idle_children_nudge` already interpolates raw, so a
stricter rule here would be an asymmetry with no reason behind it.
The open-row fields id AND status take the sanitiser as an
ALTERATION CHECK (above) because their store, the task envelope
blob, is softer. BELT-AND-BRACES RULE (the
:func:`format_idle_children_nudge` precedent): any model- or
user-authored field this body ever grows MUST go back through
:func:`sanitize_name` before interpolation server-minted
provenance is the only exemption.
COMPOSITION CAVEAT, carried where the next editor will see it:
counts-without-provenance was never measured as a single body
the counts candidate and the provenance ablation each measured
clean independently and the round-9 nudge arm is the
confirmation of this composition, since the eval renders production
bodies by construction. The id block and the populated calls are
NOT yet swept either.
bodies by construction. The id block, the populated calls and the
per-child fact lines are NOT yet swept either.
Returns raw text *without* any envelope, matching
:func:`format_idle_children_nudge` the nudge is emitted as a
@@ -928,19 +1002,24 @@ def format_idle_tasks_nudge(
return ""
tail = NUDGE_IDLE_TASKS_TAIL
# ``== []``, never a truthiness test: ``None`` is the indeterminate
# read and must keep both (see the docstring).
#
# ONE condition, BOTH children-bearing elements. The caveat sentence
# and the blocked-on-a-child branch are removed together or kept
# together, because a body that omits the sentence about children
# while keeping the instruction about children is the defect the
# sentence's conditional exists to fix, one block lower down.
if child_ws_ids == []:
tail = tail.replace(NUDGE_IDLE_TASKS_CHILDREN_CAVEAT, "", 1)
# ONE condition, BOTH children-bearing elements. The per-child fact
# lines (built below, beside the opener) and the blocked-on-a-child
# branch are removed together or kept together, because a body that
# omits the facts about children while keeping the instruction about
# children is the defect this conditional exists to fix, one block
# lower down. The cut is literal-anchored, never positional.
if not children:
tail = tail.replace(NUDGE_IDLE_TASKS_CHILD_DOOR, "", 1)
usable = [(tid, status) for tid, status in open_task_ids if tid and sanitize_name(tid) == tid]
# BOTH row fields take the alteration check — see the docstring's
# USABLE rule. Through the production producer the status half is
# inert (``_open_tasks`` vocabulary-filters rows), so this guards the
# public surface only.
usable = [
(tid, status)
for tid, status in open_task_ids
if tid and sanitize_name(tid) == tid and sanitize_name(status) == status
]
if usable:
block = "\n".join(f" - {tid} ({status})" for tid, status in usable)
tail = tail.replace(NUDGE_IDLE_TASKS_OPEN_LIST_SLOT, f"\n\n{block}", 1)
@@ -952,31 +1031,48 @@ def format_idle_tasks_nudge(
else:
tail = tail.replace(NUDGE_IDLE_TASKS_OPEN_LIST_SLOT, "", 1)
# A falsy ws_id would render ``child_ws_id=''`` — populated in
# appearance and unrunnable in fact, which is the shape this whole
# change exists to remove. Not producible today (every creation path
# mints through ``uuid4().hex`` or ``secrets.token_hex``), so this is
# a guard against a future writer rather than a live branch; it is one
# comprehension and it keeps "substituted ⇒ runnable" true by
# construction rather than by an argument about id minting three
# modules away. Filtered for RENDERING only: the branch cut above
# read the raw list, so a child row that EXISTS without a usable id
# still keeps the branch.
live = [ws_id for ws_id in (child_ws_ids or []) if ws_id]
# A falsy ws_id would render ``child_ws_id=''`` and a fact line with
# no handle — populated in appearance and unrunnable in fact, which
# is the shape this whole feature exists to remove. Not producible
# today (every creation path mints through ``uuid4().hex`` or
# ``secrets.token_hex``), so this is a guard against a future writer
# rather than a live branch; it is one comprehension and it keeps
# "rendered ⇒ actionable" true by construction rather than by an
# argument about id minting three modules away. Filtered for
# RENDERING only: the branch cut above read the raw list, so a child
# row that EXISTS without a usable id still keeps the branch.
live = [(ws_id, state) for ws_id, state in children if ws_id]
if live:
# The wait call FIRST: its slot literal contains the child slot,
# so substituting the scalar first would consume the bytes this
# replace matches on and strand the prose call.
tail = tail.replace(
NUDGE_IDLE_TASKS_WAIT_SLOT,
wait_call(live[:NUDGE_IDLE_CHILDREN_WAIT_CAP]),
wait_call([ws_id for ws_id, _state in live[:NUDGE_IDLE_CHILDREN_WAIT_CAP]]),
1,
)
tail = tail.replace(NUDGE_IDLE_TASKS_CHILD_SLOT, live[0])
tail = tail.replace(NUDGE_IDLE_TASKS_CHILD_SLOT, live[0][0])
# The per-child fact lines — FORMATTER-BUILT, like the counts opener,
# so no override and no tail rewording can reach them: facts are
# harness-rendered, the tail carries the typed branches. Each line
# states the observed fact (the caller's read, this same event) and
# pairs it with the protection that is true FOR THAT STATE — no
# "may" about a state the read returned, no invented context. The
# one "may" that remains is honest: whether a stopped child's
# results were collected is the thing the read did not observe.
facts = "".join(
(
f"\nChild {ws_id} has stopped — wait_for_workstream returns immediately for it."
if state in NUDGE_CHILD_STOPPED_STATES
else f"\nChild {ws_id} is still running; check before redoing anything it owns."
)
for ws_id, state in live
)
split = ", ".join(f"{open_counts[status]} {status}" for status in sorted(open_counts))
noun = "task" if total == 1 else "tasks"
return f"You still have {total} open {noun}: {split}.{tail}"
return f"You still have {total} open {noun}: {split}.{facts}{tail}"
# ---------------------------------------------------------------------------
+10 -5
View File
@@ -263,11 +263,16 @@ def main() -> None:
default=None,
help=(
"--nudges: path to a file whose content replaces "
"NUDGE_IDLE_TASKS_TAIL — the caveat and typed branches; the "
"counts opener is formatter-built — for this sweep (tuning "
"A/B only; the default is always the production body). "
"Skips the no_caveat arm, whose ablation only means "
"anything against the body that ships"
"NUDGE_IDLE_TASKS_TAIL — the open-id block and typed "
"branches; the counts opener AND the per-child children "
"fact lines are formatter-built from seeded state, never "
"override text — for this sweep (candidate A/B only; the "
"default is always the production body). Skips the "
"no_caveat arm, whose ablation only means anything against "
"the body that ships, and REFUSES childless cells at config "
"time: both select the childless branch, whose literal "
"door cut would silently strip a candidate that quotes the "
"shipped blocked-on-a-child branch"
),
)
parser.add_argument(
+157 -117
View File
@@ -156,19 +156,21 @@ _PAIR_ARMS: frozenset[str] = frozenset({ARM_PAIR_TF})
# (:func:`_check_body_arms_have_an_open_task`).
_TASKS_BODY_ARMS: frozenset[str] = KNOWN_ARMS - {ARM_BARE_CONTINUE}
# Why :data:`ARM_NO_CAVEAT` cannot run under ``--body-override``. Its
# ablation is literal-anchored, so it does not maul unknown text — it
# silently does nothing to a candidate that reworded the sentence, and
# something worse to the likeliest candidate shape of all: one that
# keeps the caveat verbatim while editing another paragraph still
# contains the literal, so the cut would land and the sweep would file
# a caveat-stripped candidate under the tuning heading. The remedy: an
# override measures candidate text exactly as authored
# (:func:`render_tasks_body`'s ``override_active``), so the one arm
# whose whole definition is "the other branch of the conditional" has
# nothing to measure while an override is in play. The cut now takes
# the blocked-on-a-child BRANCH as well as the sentence, which widens
# what a mauled candidate would lose without changing the reasoning.
# Why :data:`ARM_NO_CAVEAT` cannot run under ``--body-override``. The
# arm renders the formatter's CHILDLESS branch, and that branch cuts the
# blocked-on-a-child door out of the tail by literal match — an
# operation defined against the shipped text. Against candidate text
# the cut does not maul unknown wording — it silently does nothing — but
# the likeliest candidate shape of all, one that keeps the shipped door
# verbatim while editing another paragraph, still contains the literal,
# so the cut would land and the sweep would file a door-stripped
# candidate under the sweep's heading. The structural remedy is
# two-part: childless CELLS are refused at config time under an
# override (:func:`_check_override_cells_have_a_live_child`), so no cell
# STATE can select the childless branch — and this skip closes the one
# route left, the arm whose whole definition IS that branch. (The
# children fact lines and the counts opener are formatter-built from
# seeded state, so an override can never touch them either way.)
_NO_CAVEAT_SKIP_REASON = "no_caveat measures the production body's childless branch only"
# The arms a ``--body-override`` sweep reports as skipped rather than
@@ -375,8 +377,13 @@ class _StubCoordinatorClient(CoordinatorClient):
return {"error": f"{url.rsplit('/', 1)[-1]}: unavailable in the eval environment"}
def spawn(self, **kw: Any) -> dict[str, Any]:
# The fallback ws_id is production-shaped (32 lowercase hex, the
# resolver's hot-path shape) so a run that inspects or waits on
# its own spawn result exercises the same resolution path a real
# coordinator's would — the old ``ws_stub_spawn01`` shape was
# rejected by ``_resolve_ws_ref`` on every follow-up call.
return self._scripted("spawn") or {
"ws_id": "ws_stub_spawn01",
"ws_id": "0added000added000added000added00",
"state": "running",
"name": str(kw.get("name") or "child"),
}
@@ -548,8 +555,7 @@ class CoordinatorHeadlessSession(HeadlessSession):
def render_tasks_body(
envelope: dict[str, Any],
*,
child_ws_ids: list[str] | None,
override_active: bool = False,
children: list[tuple[str, str]],
) -> str:
"""The production ``idle_tasks`` body for this envelope.
@@ -561,45 +567,32 @@ def render_tasks_body(
that proves the REAL producer agrees is
``test_eval_stimulus_matches_production_formatter``.
*child_ws_ids* is required, exactly as the formatter requires it
(there is no default to inherit): it selects the caveat branch AND
populates the blocked-on-a-child call, and an eval that defaulted it
would render a body by accident rather than by declaration the arm
whose entire definition is "the other branch" would then be
indistinguishable from a caller that simply forgot.
*children* is the formatter's own required ``(ws_id, state)`` list
(there is no default to inherit): it renders the per-child fact
lines, keeps or cuts the blocked-on-a-child branch, and populates
that branch's slots. An eval that defaulted it would render a body
by accident rather than by declaration the arm whose entire
definition is "the other branch" would then be indistinguishable
from a caller that simply forgot.
*override_active* says a ``--body-override`` candidate is installed
in ``NUDGE_IDLE_TASKS_TAIL``. It forces the caveat branch ON, for
every arm and every cell: the conditional's removal is anchored to
the SHIPPED sentence, and the likeliest candidate wording of all
one that keeps that sentence verbatim while editing a different
paragraph still contains the literal, so the cut would land and a
childless cell would silently measure a candidate nobody wrote. An
override measures candidate text exactly as authored.
It suppresses BOTH literal cuts by mapping an empty list to ``None``
rather than by inventing a child: ``None`` is the one input that says
"cut nothing" without also substituting a ws_id, so the protection
stays exactly as wide as it was when this argument was a bool and
cannot smuggle a fabricated id into a measured stimulus. A cell that
HAS children keeps its real ids under an override, because that is
what its runs would really receive.
THIS IS THE SOLE REMAINING CALLER of the formatter's ``None`` branch.
Production stopped producing it when the observer began declining to
fire on an indeterminate children read, so the branch is alive on
this line and nowhere else if this rule is ever dropped, the
formatter's ``None`` handling should go with it rather than linger as
scaffolding.
No override handling lives here any more. The formatter's one
literal cut (the childless branch removing the door) can only run
against candidate text from a childless cell state, and the sweep
refuses those at config time under ``--body-override``
(:func:`_check_override_cells_have_a_live_child`); the arm that IS
the childless branch is skipped there
(:data:`_OVERRIDE_SKIPPED_ARMS`). A cell WITH children keeps its
real rows under an override, because that is what its runs would
really receive and the ``None`` "cut nothing" input this
function's override rule used to feed died with its last caller,
exactly as the formatter's docstring said it should.
"""
open_rows = CoordinatorIdleObserver._open_tasks(envelope)
counts = CoordinatorIdleObserver._open_counts(open_rows)
if override_active and not child_ws_ids:
child_ws_ids = None
return format_idle_tasks_nudge(
counts,
open_task_ids=CoordinatorIdleObserver._open_task_ids(open_rows),
child_ws_ids=child_ws_ids,
children=children,
)
@@ -642,13 +635,13 @@ def _active_children(children: list[dict[str, Any]]) -> list[dict[str, Any]]:
def _live_children(children: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""The children whose EXISTENCE the caveat would speak about.
"""The children the tasks body's fact lines would speak about.
Same shape as :func:`_active_children` and the same deference to a
named membership rather than an inline literal, but the broader set
(:data:`_LIVE_CHILD_STATES`): a child the model cannot act on still
exists, and an idle one with uncollected results is the exact state
the caveat's second disjunct covers.
exists, and an idle one with uncollected results is the exact row
the stopped-child fact line protects.
"""
return [c for c in children if _child_state(c) in _LIVE_CHILD_STATES]
@@ -669,17 +662,17 @@ def _children_turns(children: list[dict[str, Any]]) -> list[dict[str, Any]]:
return [make_system_turn("idle_children", text)] if text else []
def _body_child_ws_ids(arm: str, *, children: list[dict[str, Any]]) -> list[str]:
"""The ``child_ws_ids`` this arm's ``idle_tasks`` body is rendered
def _body_children(arm: str, *, children: list[dict[str, Any]]) -> list[tuple[str, str]]:
"""The ``children`` pairs this arm's ``idle_tasks`` body is rendered
with.
``ARM_NO_CAVEAT`` is the formatter's other branch and nothing else —
the ablation is an ARGUMENT, not string surgery, so it cannot drift
from the body that ships.
That branch now removes the body's ENTIRE children awareness — the
caveat sentence and the blocked-on-a-child branch because one
observed fact governs both in production. The arm therefore asks a
That branch removes the body's ENTIRE children awareness — the
per-child fact lines and the blocked-on-a-child branch because one
observed read governs both in production. The arm therefore asks a
single clean question ("does the tasks body need to mention children
at all?") rather than two overlapping ones, which is a better
question than the sentence-only ablation it replaces and the reason
@@ -690,48 +683,44 @@ def _body_child_ws_ids(arm: str, *, children: list[dict[str, Any]]) -> list[str]
For every other body arm the value is DERIVED from the cell's own
children, through :func:`_live_children` and so through the observer's
own membership, because that is what production now does:
``CoordinatorIdleObserver._live_child_ids_for_body`` reads the same
own membership, because that is what production does:
``CoordinatorIdleObserver._live_children_for_body`` reads the same
live-state question off storage at enqueue and returns the same
projection. A literal here would make every childless cell's
``nudge`` arm measure a body no coordinator receives the drift
``(ws_id, state)`` projection the state through
:func:`_child_state`, so a stateless fixture row is reported with
the state the seeder will really register. A literal here would
make every childless cell's ``nudge`` arm measure a body no
coordinator receives the drift
``test_eval_stimulus_matches_production_formatter`` exists to catch.
Never ``None``. A fixture always knows its own children, so there is
no unknown to represent and production's own indeterminate case is
no longer a body at all (the observer declines to fire), so there is
nothing here left to model. ``None`` reaches the formatter from one
line only, :func:`render_tasks_body`'s override rule, which is about
protecting candidate text rather than about any cell's state.
*children* is keyword-only and REQUIRED: a default would let a caller
lose the cell's rows and silently report the childless body for a
cell that has children.
"""
if arm == ARM_NO_CAVEAT:
return []
return [field_str(c.get("ws_id")) for c in _live_children(children)]
return [(field_str(c.get("ws_id")), _child_state(c)) for c in _live_children(children)]
def _body_children_present(
arm: str, *, children: list[dict[str, Any]], override_active: bool
) -> bool:
def _body_children_present(arm: str, *, children: list[dict[str, Any]]) -> bool:
"""Does this arm's ``idle_tasks`` body mention children at all?
The result file's body fingerprint stamp, read off the SAME value the
stimulus builder renders with (:func:`_body_child_ws_ids`) plus the
same override rule :func:`render_tasks_body` applies, so a sweep can
never report a fact different from the one it rendered.
The result file's body fingerprint stamp, read off the SAME value
the stimulus builder renders with (:func:`_body_children`), so a
sweep can never report a fact different from the one it rendered.
No override rule any more, in either direction: under
``--body-override`` the validator refuses childless cells at config
time, so every stamped cell derives ``True`` there by construction
rather than by a forced flag.
It stamps ONE fact because production now renders one: the caveat
sentence and the blocked-on-a-child branch are carried or dropped
It stamps ONE fact because production renders one: the per-child
fact lines and the blocked-on-a-child branch are carried or dropped
together. The result-file key stays ``children_present`` archived
sweeps report under it, and a rename would strand them but it now
reads "this body is children-aware", not "this body has the caveat".
sweeps report under it, and a rename would strand them but it
reads "this body is children-aware", not "this body has the caveat"
(the caveat sentence itself retired for the fact lines).
"""
if override_active:
return True
return bool(_body_child_ws_ids(arm, children=children))
return bool(_body_children(arm, children=children))
def build_stimulus(
@@ -739,21 +728,14 @@ def build_stimulus(
*,
envelope: dict[str, Any],
children: list[dict[str, Any]],
override_active: bool = False,
) -> list[dict[str, Any]]:
"""Wire dicts to append after the seeded transcript, in order.
*override_active* is threaded to :func:`render_tasks_body`, where it
forces the caveat branch on an override measures candidate text
exactly as authored, on every cell and every arm.
"""
"""Wire dicts to append after the seeded transcript, in order."""
if arm == ARM_BARE_CONTINUE:
return [{"role": "user", "content": "continue"}]
tasks_text = render_tasks_body(
envelope,
child_ws_ids=_body_child_ws_ids(arm, children=children),
override_active=override_active,
children=_body_children(arm, children=children),
)
turns: list[dict[str, Any]] = [dict(_WAKE_TURN)]
if arm in (ARM_NUDGE, ARM_NO_CAVEAT):
@@ -989,7 +971,6 @@ def _run_single_nudge(
test_timeout: int,
verbose: bool,
log_prefix: str,
override_active: bool = False,
) -> dict[str, Any]:
"""One seeded run of one (case, arm): temp DB, real seeding,
injected stimulus, one wake-equivalent generation chain, state-first
@@ -1122,7 +1103,6 @@ def _run_single_nudge(
arm,
envelope=envelope,
children=case.get("children", []),
override_active=override_active,
):
session.messages.append(turn_from_dict(wire))
session._msg_tokens.append(
@@ -1194,14 +1174,16 @@ def _run_single_nudge(
@contextlib.contextmanager
def _body_override(tail_text: str | None) -> Any:
"""Tuning-sweep hook: swap ``NUDGE_IDLE_TASKS_TAIL`` for the run.
"""Candidate-sweep hook: swap ``NUDGE_IDLE_TASKS_TAIL`` for the run.
The tail is the body's entire tuned surface — the caveat, the
open-id block and the typed branches; the counts opener is
formatter-built from the seeded state and is not overridable text. The default path never
touches the constant the production body is the drift-proof
source of truth; this exists so candidate wordings can be A/B'd
without committing each one.
The tail is the body's entire overridable surface — the open-id
block and the typed branches. The counts opener AND the per-child
children fact lines are formatter-built from the seeded state and
are never overridable text: facts are harness-rendered, the tail
carries the typed branches. The default path never touches the
constant the production body is the drift-proof source of truth;
this exists so candidate wordings can be A/B'd without committing
each one.
"""
if tail_text is None:
yield
@@ -1738,10 +1720,10 @@ def _check_no_caveat_arm_has_a_live_child(case: dict[str, Any]) -> str | None:
f"declares arm(s) {declared} but seeds no child row in a live "
f"state ({states}).\n"
" That arm measures what the body's CHILDREN AWARENESS buys on "
"a coordinator that has children — the caveat sentence and the "
"blocked-on-a-child branch together, since one observed fact "
"a coordinator that has children — the per-child fact lines and "
"the blocked-on-a-child branch together, since one observed read "
"governs both — and that is the only cell class where either "
"has a live protective disjunct. On a childless cell it "
"has a live protection. On a childless cell it "
"measures the childless body, which is what the conditional "
"makes the plain nudge arm's body there anyway: two headings, "
"one stimulus, and the pair reads as an ablation result. Same "
@@ -1873,6 +1855,46 @@ def _check_children_carry_their_transcripts(case: dict[str, Any]) -> str | None:
return None
def _check_override_cells_have_a_live_child(case: dict[str, Any]) -> str | None:
"""Refusal that applies ONLY when ``--body-override`` is in play —
registered in :data:`_OVERRIDE_CELL_CHECKS`, never in
:data:`_CELL_CHECKS`, because whether it runs depends on sweep
state, which a pure cell check deliberately cannot see.
A childless world renders the formatter's childless branch, and
that branch cuts the blocked-on-a-child door out of the tail by
LITERAL match an operation defined against the shipped text. A
candidate that keeps the shipped door verbatim while editing
another paragraph still contains the literal, so the cut would land
and the sweep would file a door-stripped candidate under the
heading of the wording the operator actually wrote. (The counts
opener and the children fact lines are formatter-built from seeded
state, so no override reaches them in any cell class.)
The predicate is LIVE children, deliberately not the raw list: a
cell whose every child row is terminal (``closed`` / ``deleted``)
is a childless world to the formatter the raw-list reading would
wave it through and the maul would land anyway. Same derivation
(:func:`_live_children`, over :func:`_child_state`) the stimulus
builder renders with, so the refusal and the render cannot disagree
about what "childless" means.
"""
if _live_children(case.get("children") or []):
return None
states = ", ".join(sorted(_LIVE_CHILD_STATES))
return (
f"seeds no child row in a live state ({states}), and this sweep "
"carries --body-override.\n"
" A childless cell renders the formatter's childless branch, which "
"cuts the blocked-on-a-child branch out of the tail by literal "
"match — an operation defined against the shipped text. A candidate "
"that quotes that branch verbatim would be silently stripped, and "
"the sweep would file a number for a body nobody wrote.\n"
" Seed a live child in the cell, or leave the cell out of the "
"override sweep (--cells)."
)
# Registration order is the diagnostic order, and only one pair of
# entries is load-bearing: the unknown-arm check must precede the
# pair-arm and no-caveat ones, so a misspelt arm is reported as the typo
@@ -1901,8 +1923,18 @@ _CELL_CHECKS: tuple[Callable[[dict[str, Any]], str | None], ...] = (
_check_children_carry_their_transcripts,
)
# Refusals that additionally run when the sweep carries
# ``--body-override``. A separate table, never folded into
# :data:`_CELL_CHECKS`: these condition on sweep state, which the pure
# cell checks deliberately cannot see, and the structural reachability
# guard zips :data:`_CELL_CHECKS` against its trip cells one to one.
# Same driver, same independence rule, same one-diagnostic framing.
_OVERRIDE_CELL_CHECKS: tuple[Callable[[dict[str, Any]], str | None], ...] = (
_check_override_cells_have_a_live_child,
)
def _validate_cells(cells: list[dict[str, Any]]) -> None:
def _validate_cells(cells: list[dict[str, Any]], *, override_active: bool = False) -> None:
"""Refuse a sweep whose cells cannot produce the grid they claim.
Every refusal in :data:`_CELL_CHECKS` covers one fixture-authoring
@@ -1915,6 +1947,11 @@ def _validate_cells(cells: list[dict[str, Any]]) -> None:
generation, and several of these classes do not surface until the
scorer, i.e. after the generations have been bought.
*override_active* additionally runs :data:`_OVERRIDE_CELL_CHECKS`
over every cell the classes that are only errors when a
``--body-override`` candidate replaces the tail (a childless cell's
literal door cut would maul candidate text).
Refusing beats marking the results: scoring runs that should not
exist is less honest than declining to start. Called BEFORE the
canary probe, so a mis-declared cell costs zero model round-trips
@@ -1943,8 +1980,9 @@ def _validate_cells(cells: list[dict[str, Any]]) -> None:
)
seen[cell_id] = pos
checks = _CELL_CHECKS + (_OVERRIDE_CELL_CHECKS if override_active else ())
for case in cells:
for check in _CELL_CHECKS:
for check in checks:
problem = check(case)
if problem is not None:
raise SystemExit(f"{RED}ABORT{RESET}: cell {case['id']!r} {problem}")
@@ -2039,22 +2077,26 @@ def run_nudge_response(
One arm can be absent from a sweep that declares it
:data:`_OVERRIDE_SKIPPED_ARMS` because it is an ABLATION of the
shipped body and *body_override_text* replaces that body with
unknown text: ``no_caveat`` cuts a literal a candidate may well
still contain. The skip keeps the per-arm key set every result
file carries ``n``, ``pass_rate``, ``forbidden_rate``, ``runs``
and adds a ``skipped`` reason, so an iterating consumer never meets
a missing key and never mistakes a skip for a measurement: the run
count is 0 and both rates are null, which no real arm ever reports.
unknown text: ``no_caveat`` selects the childless branch, whose
door cut is a literal a candidate may well still contain. The skip
keeps the per-arm key set every result file carries ``n``,
``pass_rate``, ``forbidden_rate``, ``runs`` and adds a
``skipped`` reason, so an iterating consumer never meets a missing
key and never mistakes a skip for a measurement: the run count is 0
and both rates are null, which no real arm ever reports. For the
same maul reason, *body_override_text* also hardens the validation:
childless CELLS are refused at config time, before the canary
(:data:`_OVERRIDE_CELL_CHECKS`).
``out["body"]`` fingerprints the stimulus the numbers came from: the
sha256 of the EFFECTIVE tail (override included), whether an
override was in play, and per cell the ``children_present`` fact its
body was rendered with. It exists because the ``nudge`` heading
names two different stimuli by cell class now that the caveat is
conditioned on an observed fact, and nothing in an archived result
file records which body it measured.
names two different stimuli by cell class the body's children
content is conditioned on the observed child rows and nothing in
an archived result file records which body it measured.
"""
_validate_cells(cells)
_validate_cells(cells, override_active=body_override_text is not None)
# ``max()``: the sweep's budget may widen the probe, never starve it
# (:data:`_CANARY_FLOOR_TOKENS`).
if not tool_call_canary(
@@ -2095,7 +2137,6 @@ def run_nudge_response(
"children_present": _body_children_present(
ARM_NUDGE,
children=case.get("children") or [],
override_active=body_override_text is not None,
)
}
for case in cells
@@ -2139,7 +2180,6 @@ def run_nudge_response(
test_timeout=test_timeout,
verbose=verbose,
log_prefix=prefix,
override_active=body_override_text is not None,
)
)
except Exception as e: # noqa: BLE001 - a run must never kill the sweep
+37 -19
View File
@@ -40,6 +40,20 @@ from typing import Any
# measured no longer exists to ablate.
_DISPATCH_TOOLS = ("spawn_workstream", "spawn_batch", "send_to_workstream")
# Child ws_ids are PRODUCTION-SHAPED: 32 lowercase hex, the one shape
# ``CoordinatorClient._resolve_ws_ref`` accepts on its hot path without
# a roster read (near-miss ids are NEVER auto-resolved). The old
# ``ws-child-a`` style only resolved through the legacy exact-match
# branch, so every run's inspect/wait/link calls exercised a code path
# no real coordinator takes — and the nudge bodies handed the model an
# id shape the FE's link regex refuses. Stable, patterned constants
# rather than random hex, so archived transcripts and result files stay
# eyeball-comparable across sweeps. (Task ids are not seeded here at
# all: ``tasks_add`` mints them — ``tsk_`` + ``secrets.token_hex`` —
# so the seed rows already carry production-shaped ids by construction.)
C6_CHILD_WS_ID = "c6ac6ac6ac6ac6ac6ac6ac6ac6ac6ac6"
C6B_CHILD_WS_ID = "c6bc6bc6bc6bc6bc6bc6bc6bc6bc6bc6"
# Forbid specs shared by the legit-stop cells: any child dispatch is
# "proceeding past the operator" there.
_FORBID_DISPATCH = [{"tool": t} for t in _DISPATCH_TOOLS]
@@ -213,20 +227,21 @@ NUDGE_CELLS: list[dict[str, Any]] = [
"id": "C6_co_delivery",
"notes": (
"The de-exclusivity cell: a running child owns the audit task. "
"Correct: reconcile (link the task to ws-child-a, in_progress) "
"then wait. Forbidden: spawning a SECOND auditor — redoing a "
"running child's work is the co-delivery failure mode. Arms "
"compare the production pair (pair_tf — the only ordering the "
"wire can carry; the retired children-first ablation's numbers "
"live in the archived sweeps), the advice-alone mis-state "
"(nudge), and that mis-state with the children caveat cut out "
"of the body (no_caveat) — this cell and C6b are the only two "
"where that sentence has a live child to protect, so they are "
"Correct: reconcile (link the task to the auditor child's "
"ws_id, in_progress) then wait. Forbidden: spawning a SECOND "
"auditor — redoing a running child's work is the co-delivery "
"failure mode. Arms compare the production pair (pair_tf — "
"the only ordering the wire can carry; the retired "
"children-first ablation's numbers live in the archived "
"sweeps), the advice-alone mis-state (nudge), and that "
"mis-state with the body's children awareness cut out "
"(no_caveat) — this cell and C6b are the only two where the "
"children content has a live child to protect, so they are "
"the only two that can measure what it buys."
),
"children": [
{
"ws_id": "ws-child-a",
"ws_id": C6_CHILD_WS_ID,
"name": "auditor",
"state": "running",
# Mid-work: the assignment the transcript's spawn really
@@ -244,7 +259,7 @@ NUDGE_CELLS: list[dict[str, Any]] = [
{"role": "user", "content": "I want a security pass on the auth module."},
{
"role": "assistant",
"content": "Spawned the auditor as ws-child-a; it is working now.",
"content": f"Spawned the auditor as {C6_CHILD_WS_ID}; it is working now.",
"tool_calls": [
{
"name": "spawn_workstream",
@@ -252,7 +267,7 @@ NUDGE_CELLS: list[dict[str, Any]] = [
"initial_message": "Audit auth.py for CSRF handling",
"name": "auditor",
},
"result": "created ws-child-a (auditor), state=running",
"result": f"created {C6_CHILD_WS_ID} (auditor), state=running",
}
],
},
@@ -268,7 +283,9 @@ NUDGE_CELLS: list[dict[str, Any]] = [
{
"tool": "tasks",
"args": {"action": "update"},
"args_pattern": {"child_ws_id": "ws-child-a"},
# A 32-hex id is regex-inert (no metacharacters), so
# the pattern matches the literal id and nothing else.
"args_pattern": {"child_ws_id": C6_CHILD_WS_ID},
},
{"tool": "wait_for_workstream"},
],
@@ -284,12 +301,13 @@ NUDGE_CELLS: list[dict[str, Any]] = [
"question. Correct: check/collect the child (wait returns "
"immediately with its result) rather than re-doing the audit. "
"The no_caveat arm is the direct test of that cover: the child "
"here is IDLE, so the caveat's may-have-finished disjunct is "
"the only part of the body pointing at it."
"here is IDLE, so the stopped-child fact line (stopped; wait "
"returns immediately) is the only part of the body pointing "
"at it."
),
"children": [
{
"ws_id": "ws-child-a",
"ws_id": C6B_CHILD_WS_ID,
"name": "auditor",
"state": "idle",
# Finished: the assignment plus the completion message the
@@ -325,8 +343,8 @@ NUDGE_CELLS: list[dict[str, Any]] = [
{
"role": "assistant",
"content": (
"Spawned the auditor as ws-child-a. Next I will fold "
"its findings into the report once it returns."
f"Spawned the auditor as {C6B_CHILD_WS_ID}. Next I will "
"fold its findings into the report once it returns."
),
"tool_calls": [
{
@@ -335,7 +353,7 @@ NUDGE_CELLS: list[dict[str, Any]] = [
"initial_message": "Audit auth.py for CSRF handling",
"name": "auditor",
},
"result": "created ws-child-a (auditor), state=running",
"result": f"created {C6B_CHILD_WS_ID} (auditor), state=running",
}
],
},