Skip to content

A cell finds its own runs without the last-execution stamp (#3301) - #3318

Merged
rbuergi merged 1 commit into
mainfrom
fix/3301-a-cell-finds-its-runs-without-the-stamp
Sep 4, 2026
Merged

rbuergi merged 1 commit into
mainfrom
fix/3301-a-cell-finds-its-runs-without-the-stamp

Conversation

@rbuergi

@rbuergi rbuergi commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Closes #3301.

Pairs-with: none — this PR removes no public type and no public member. It adds one type
(CodeRunHistory) and two constants; nothing outside core is asked to change, and the two names are
free in MeshWeaver.Plugins (gh api search/code … repo:Systemorph/MeshWeaver.Plugins → total_count 0,
measured 2026-09-05). No <see cref> becomes ambiguous: ResolveOutputCurrency is a new NAME, not a
new overload — deliberately, because #3305's RunActivity(…, LogMessage) overload turned an
unqualified cref into CS0419 in three Plugins files (Plugins#1338) and that break shape has no gate.

The defect, and why #3300 did not close it

A cell reads its own run history from ONE denormalised pointer: the last-execution stamp
(LastExecutedAt / LastExecutedBy / LastActivityPath / LastExecutedCodeHash), written
fire-and-forget after the dispatch. It is one write to one node — it lands whole or not at all — so
when it fails (#3249: workspace not ready, a refused partition write, content unreadable) the cell is,
after the next page load, indistinguishable from a cell nobody has ever run.

#3300 made that failure reported (one Error, event id 3249) and wrote up why recovering it on
the cell is impossible from the dispatching hub: all three candidate shapes are a second write to
the node whose first write just failed
, i.e. recovery in the same failure domain. That argument is
correct and stands. It simply says nothing about the reader — because the reader's fix is not a
write.

The run is not lost. CodeNodeType.HandleExecuteScript creates the Activity node before it
dispatches and stamps the originating cell onto it (ActivityLog.HubPath = hub.Address.Path). The
cell → run edge is durable; it was only missing in the direction the view reads it. Nothing followed
it back.

The fix — one read, along the edge that already exists

CodeRunHistory (src/MeshWeaver.Mesh.Contract/CodeOutputCurrency.cs, beside the currency rule it
extends, as the issue asked):

step cost
1 OutputCurrency() — the stamp. Anything but NeverRun returns unchanged. no query
2 Only on NeverRun: one listing for an Activity naming this cell on HubPath → Unverified. one row
3 No hit → NeverRun, exactly as today. —
  • A listing by predicate, not a point read. A stale negative is harmless by construction — the
    worst outcome is the answer the cell already gives today. Reading a specific known node stays
    GetMeshNodeStream's job.
  • A recovered run is Unverified, never Current. The Activity records that the cell ran,
    never what it ran. Same evidence as a stamp that landed without its fingerprint, so the same
    fail-closed verdict — claiming currency here would be the wrong claim the CodeNodeType's last-execution stamp swallows its failure, and nothing can tell that it did #3249 rule exists to
    prevent.
  • It runs only where it can change the outcome. The issue's cost paragraph asked for exactly
    this: a notebook of normally-stamped cells costs zero queries, because step 1 answers first.
  • Reactive end to end (IObservable<CodeOutputCurrency>, one verdict then completion) — no async,
    no Task<T>, no .Result. Immutable collections throughout (ImmutableArray<string>).

Where it looks. ActivityNamespaces reconstructs the layers visible from the cell alone — its own
ActivityParentPath (with {viewer} expanded), the partition root, the reading viewer's home —
deduplicated into ONE union query, usually one namespace. Stated in the code and the doc: a
partition-level DefaultActivityParentPath pointing at a fourth place is not covered (that lookup
is a live partition-registry query, which Mesh.Contract sits below). Where it applies the listing
finds nothing and the verdict falls back to NeverRun — today's answer — so the gap costs nothing
that was already working.

Two cross-assembly agreements, both pinned — because no compiler sees them

The lookup filters on nodeType:Activity and expands {viewer}, and both are declared in assemblies
it sits below. A rename on either side would leave the query looking for something nothing writes:
zero rows, every recovered cell silently back to NeverRun, no compiler error anywhere.

  • CodeRunHistoryTest.TheActivityNodeTypeNameMatchesTheGraphVocabulary — equality against
    GraphNodeTypeNames.Activity.
  • CodeRunHistoryTest.TheViewerSentinelExpandsTheSameWayOnBothSides — pinned behaviourally: it
    drives CodeNodeType.ResolveActivityParent and requires the answer the lookup assumes. The
    dispatcher now spells the sentinel through CodeRunHistory.ViewerHomeSentinel instead of a second
    literal.

Verification

Deterministic repro, through a real mesh —
CodeCellCurrencyThroughTheMeshTest.ARunWhoseStampNeverLandedIsStillFoundByTheActivityItWrote. Two
cells really run through ExecuteScriptRequest, so two real Activities share one _Activity
namespace; one cell's stamp is then WIPED, reproducing exactly what #3249's failure paths leave
behind. Four arms, so the test cannot pass vacuously: the wiped cell reads NeverRun from its own
content (the defect, asserted); it resolves to Unverified (the fix); a cell that genuinely never ran
still resolves to NeverRun (non-vacuity — and the neighbour's activity in the same namespace must
not be mistaken for it); the still-stamped neighbour resolves to Current without a query.

Mutation-proved, twice — the guard was made to fail on purpose and quoted:

  1. Drop content.hubPath:{cellPath} from RunsQuery → 2 failures:

    Expected value to be NeverRun because no Activity anywhere names this cell — and the neighbour's
    run, which lives in the very same _Activity namespace, must not be mistaken for it. … but
    found Unverified.

    Expected "namespace:acme/_Activity nodeType:Activity sort:LastModified-desc limit:1" to contain
    "content.hubPath:acme/notebook/cell-7" because HubPath is the cell → run edge the dispatcher
    stamps onto the Activity node BEFORE it dispatches…

  2. Make ResolveOutputCurrency never consult the mesh (the pre-fix behaviour) → 1 failure:

    Expected value to be Unverified because the Activity node created before the dispatch still names
    this cell on HubPath, so the run is not lost — only the cell's pointer to it is. … but found
    NeverRun.

Both mutations reverted; the suite is green again.

Builds — dotnet build -c Release -warnaserror, one project per invocation, all 0 Error(s) / 0 Warning(s):
MeshWeaver.Mesh.Contract · MeshWeaver.Graph · MeshWeaver.Documentation · MeshWeaver.Graph.Test ·
MeshWeaver.Documentation.Test.

Tests: MeshWeaver.Graph.Test currency + run-history suites — 30/30 passed.
DocumentationLinkIntegrityTest, WhatsNewEntryIntegrityTest, UnkeyedActivityLogMessageRatchetGuard
— 9/9 passed. Full MeshWeaver.Documentation.Test guard suite green.

Work products committed

  • Doc/Architecture/ScriptExecution → new subsection "Recovering the run for the READER — follow the
    edge, do not write another marker"
    , replacing the "what is still not covered" paragraph that
    pointed at this issue. It states the cost model, the namespace derivation, the two cross-assembly
    agreements, and what remains uncovered.
  • What's New entry (Category: Fix): A cell that ran no longer claims it never did.

Not in scope

  • The output pane is deliberately not re-pointed at a recovered activity — this answers whether
    the cell ran, not what it produced.
  • The operator still gets the 3249 Error. The stamp failing is a real fault; a reader who can no
    longer see the consequence is not a reason to stop reporting it.
  • The Plugins half (CodeViews.IsOutputStale rendering the recovered verdict) follows a core pin
    bump — MW_PLATFORM_REF is 7d644de95 and this change is not in it yet.

🤖 Generated with Claude Code

The stamp is one write to one node — it lands whole or not at all — so a cell
whose stamp failed (#3249) reads back as NeverRun and is indistinguishable from
one nobody ever ran. #3300 established that recovering that ON THE CELL is
impossible from the dispatching hub: every candidate is a second write to the
node whose first write just failed. It said nothing about the READER, because
the reader's fix is not a write.

The run is not lost. HandleExecuteScript creates the Activity node BEFORE it
dispatches and stamps the originating cell onto ActivityLog.HubPath, so the
cell -> run edge is durable; nothing followed it back.

CodeRunHistory.ResolveOutputCurrency does, beside the currency rule it extends:
OutputCurrency() first (anything but NeverRun costs no query), and only on
NeverRun one listing for an Activity naming this cell -> Unverified. A recovered
run is never Current: the Activity records THAT the cell ran, never what it ran.
No hit leaves NeverRun exactly as today.

Two cross-assembly agreements hold the lookup together and no compiler sees
either — the nodeType it filters on and the {viewer} sentinel it expands are
declared in assemblies Mesh.Contract sits below. CodeRunHistoryTest pins the
first by equality and the second by driving CodeNodeType.ResolveActivityParent;
the dispatcher now spells the sentinel through the shared constant.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 4, 2026 22:10
@rbuergi
rbuergi enabled auto-merge September 4, 2026 22:10
@rbuergi

rbuergi commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

The Plugins half — CodeViews.IsOutputStale rendering the recovered verdict — is tracked as Systemorph/MeshWeaver.Plugins#1345. It follows a core pin bump past this PR (MW_PLATFORM_REF = 7d644de95, core 2026-09-04 11:07Z), not effort. That issue also records a second drift found while measuring: the toolbar still re-derives the currency rule by hand instead of calling OutputCurrency(), which #3270 introduced so that no surface would.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are unresolved review comments (including a future-dated What’s New entry and a query projection issue) that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Implements #3301 by allowing a code cell to recover “has been run” evidence even when the last-execution stamp did not land, by following the durable Activity edge (ActivityLog.HubPath) via a bounded, existence-only query path; adds tests and documentation to pin the cross-assembly agreements this relies on.

Changes:

  • Add CodeRunHistory and ResolveOutputCurrency(...) (reactive) to recover NeverRun → Unverified by querying Activities that reference the cell’s path.
  • Pin the two cross-assembly agreements (Activity nodeType vocabulary + {viewer} sentinel expansion) with new tests, plus an end-to-end mesh repro test.
  • Update docs and add a What’s New entry explaining the reader-side recovery behavior.
File summaries
File Description
test/MeshWeaver.Graph.Test/CodeRunHistoryTest.cs Unit tests pinning query vocabulary and {viewer} expansion behavior.
test/MeshWeaver.Graph.Test/CodeCellCurrencyThroughTheMeshTest.cs End-to-end mesh repro ensuring a stampless run is still found and yields Unverified.
src/MeshWeaver.Mesh.Contract/CodeOutputCurrency.cs Adds CodeRunHistory and the mesh lookup fallback for NeverRun.
src/MeshWeaver.Graph/Configuration/CodeNodeType.cs Reuses the shared {viewer} sentinel constant to prevent drift with the reader-side lookup.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-09-05-a-cell-can-find-its-own-runs-again.md What’s New entry describing the user-visible fix.
src/MeshWeaver.Documentation/Data/Architecture/ScriptExecution.md Architecture doc update explaining the reader-side recovery and its constraints.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +194 to +196
public static string RunsQuery(string cellPath, string activityNamespace) =>
$"namespace:{activityNamespace} nodeType:{ActivityNodeTypeName} "
+ $"content.hubPath:{cellPath} sort:LastModified-desc limit:1";
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    4 files      4 suites   1m 52s ⏱️
1 487 tests 1 487 ✅ 0 💤 0 ❌
1 488 runs  1 488 ✅ 0 💤 0 ❌

Results for commit aea4e9e.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

475 tests   475 ✅  40s ⏱️
  1 suites    0 💤
  1 files      0 ❌

Results for commit aea4e9e.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

251 tests   251 ✅  2m 10s ⏱️
  1 suites    0 💤
  1 files      0 ❌

Results for commit aea4e9e.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

368 tests   366 ✅  49s ⏱️
  3 suites    2 💤
  3 files      0 ❌

Results for commit aea4e9e.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

588 tests   396 ✅  2m 56s ⏱️
  3 suites  192 💤
  3 files      0 ❌

Results for commit aea4e9e.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

2 302 tests   2 302 ✅  2m 36s ⏱️
    3 suites      0 💤
    3 files        0 ❌

Results for commit aea4e9e.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Test Results

   15 files     15 suites   11m 7s ⏱️
5 471 tests 5 277 ✅ 194 💤 0 ❌
5 472 runs  5 278 ✅ 194 💤 0 ❌

Results for commit aea4e9e.

@rbuergi
rbuergi added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 5c4668d Sep 4, 2026
28 checks passed
@rbuergi
rbuergi deleted the fix/3301-a-cell-finds-its-runs-without-the-stamp branch September 13, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A cell cannot find its own runs without the last-execution stamp

2 participants