Skip to content

Commit cfee628

Browse files
committed
feat(dataplane): wrap read_program return in Sourced provenance (LocalValue)
Sourced[T, S] + the six aliases (LocalValue, CachedValue, ReplayedValue, GossipedValue, ExternalValue, SanitizedValue) were exported with zero production consumers — the provenance vocabulary was dead code that the doc §3.4 says should distinguish local-fresh-read vs cached-stale- read at every call site. dp.read_program now returns Result[LocalValue[Versioned[ProgramSnapshot]] | None, DataPlaneError]. The phantom-type wrapper makes the read's provenance explicit at the type level. Future replay paths would return ReplayedValue[Versioned[...]]; future cache reads would return CachedValue[Versioned[...]]; mypy then enforces that a function demanding LocalValue[Program] cannot accept a CachedValue[Program] by mistake (bug class #13 closed at the type system level). Production caller updated: gigaevo/runner/dag_runner.py _timeout_read_is_fresh unwraps the LocalValue via .value.value to reach the underlying Versioned. 5 tests updated to unwrap the LocalValue wrapper; 1 new test pins the phantom-wrapper shape returned by read_program. This is the foothold: a follow-up provenance audit can rg "LocalValue\|CachedValue\|ReplayedValue" gigaevo/ and find the first real consumer; subsequent reads (crdt_read, lwwr_get) can adopt the same pattern.
1 parent 32239c6 commit cfee628

3 files changed

Lines changed: 35 additions & 9 deletions

File tree

gigaevo/dataplane/tests/test_transition_state.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,10 @@ async def test_existing_returns_versioned(self, coord: dp.DataPlane) -> None:
337337
result = await coord.read_program(dp.ProgramId("p-13"))
338338
assert isinstance(result, dp.Ok)
339339
assert result.value is not None
340-
assert result.value.value["state"] == "RUNNING"
341-
assert result.value.epoch >= 1
340+
# ``LocalValue`` wraps the freshness-checked ``Versioned``.
341+
versioned = result.value.value
342+
assert versioned.value["state"] == "RUNNING"
343+
assert versioned.epoch >= 1
342344

343345
async def test_stale_floor_raises(self, coord: dp.DataPlane) -> None:
344346
await _put_program(coord, "p-14", dp.ProgramState.QUEUED)
@@ -351,11 +353,29 @@ async def test_stale_floor_raises(self, coord: dp.DataPlane) -> None:
351353
current = await coord.read_program(dp.ProgramId("p-14"))
352354
assert isinstance(current, dp.Ok) and current.value is not None
353355
future = await coord.read_program(
354-
dp.ProgramId("p-14"), min_epoch=current.value.epoch + 10
356+
dp.ProgramId("p-14"), min_epoch=current.value.value.epoch + 10
355357
)
356358
assert isinstance(future, dp.Err)
357359
assert isinstance(future.error, dp.StaleReadError)
358360

361+
async def test_returns_localvalue_phantom_wrapper(
362+
self, coord: dp.DataPlane
363+
) -> None:
364+
"""The successful return is wrapped in :data:`LocalValue` — the
365+
:class:`Sourced` phantom-tag alias for a fresh local read. This
366+
is the structural discriminator for bug class #13 (stale cache
367+
returns as authoritative)."""
368+
await _put_program(coord, "p-localvalue", dp.ProgramState.QUEUED)
369+
result = await coord.read_program(dp.ProgramId("p-localvalue"))
370+
assert isinstance(result, dp.Ok)
371+
assert result.value is not None
372+
# The outer wrapper is a Sourced instance (the LocalValue alias).
373+
assert isinstance(result.value, dp.Sourced)
374+
# The inner ``Versioned`` is reachable via ``.value`` and carries
375+
# the freshness witness.
376+
versioned = result.value.value
377+
assert isinstance(versioned, dp.Versioned)
378+
359379

360380
# ── token discipline ─────────────────────────────────────────────────
361381

@@ -547,4 +567,4 @@ async def test_batch_partial_failure_returns_err_after_partial_commit(
547567
# The first item's commit survived.
548568
read = await coord.read_program(dp.ProgramId("b-good"))
549569
assert isinstance(read, dp.Ok) and read.value is not None
550-
assert read.value.value["state"] == "RUNNING"
570+
assert read.value.value.value["state"] == "RUNNING"

gigaevo/runner/dag_runner.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,13 @@ async def _timeout_read_is_fresh(self, prog: Program) -> bool:
283283
return False
284284
# ``Ok(None)`` means the blob is gone (someone deleted it).
285285
# That is not the freshness contract we wanted to assert; treat
286-
# as "not fresh enough to act on" and defer.
287-
return result.value is not None
286+
# as "not fresh enough to act on" and defer. Otherwise the
287+
# coordinator returns a :data:`LocalValue` (the phantom-tag
288+
# alias for a fresh local read) wrapping the
289+
# :class:`Versioned` admission record; we only need its
290+
# presence here, not the inner value.
291+
local_value = result.value
292+
return local_value is not None
288293

289294
async def _run(self) -> None:
290295
logger.info("[DagScheduler] start")

tests/database/test_redis_storage_dataplane.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,8 @@ async def test_eventual_returns_value_at_any_epoch(
652652
)
653653
assert isinstance(result, Ok)
654654
assert result.value is not None
655-
assert result.value.value["state"] == ProgramState.RUNNING.value
655+
# ``LocalValue`` wraps the freshness-checked ``Versioned``.
656+
assert result.value.value.value["state"] == ProgramState.RUNNING.value
656657

657658
async def test_at_least_below_floor_returns_stale_read_error(
658659
self,
@@ -684,7 +685,7 @@ async def test_at_least_below_floor_returns_stale_read_error(
684685
)
685686
assert isinstance(eventual, Ok)
686687
assert eventual.value is not None
687-
observed_epoch = eventual.value.epoch
688+
observed_epoch = eventual.value.value.epoch
688689

689690
# Asking for one beyond the observed epoch fires the stale
690691
# branch. The same call at FreshnessEventual succeeded above —
@@ -741,7 +742,7 @@ async def test_legacy_min_epoch_kwarg_still_works(
741742
# First read picks up the current epoch.
742743
result = await coord.read_program(ProgramId(prog.id))
743744
assert isinstance(result, Ok)
744-
observed_epoch = result.value.epoch # type: ignore[union-attr]
745+
observed_epoch = result.value.value.epoch # type: ignore[union-attr]
745746
stale = await coord.read_program(
746747
ProgramId(prog.id), min_epoch=observed_epoch + 1
747748
)

0 commit comments

Comments
 (0)