Repository navigation
A cell finds its own runs without the last-execution stamp (#3301) - #3318
Conversation
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>
|
The Plugins half — |
There was a problem hiding this comment.
🟡 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
CodeRunHistoryandResolveOutputCurrency(...)(reactive) to recoverNeverRun → Unverifiedby 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.
| public static string RunsQuery(string cellPath, string activityNamespace) => | ||
| $"namespace:{activityNamespace} nodeType:{ActivityNodeTypeName} " | ||
| + $"content.hubPath:{cellPath} sort:LastModified-desc limit:1"; |
Test Results (shard 5) 4 files 4 suites 1m 52s ⏱️ Results for commit aea4e9e. |
Test Results (shard 1)475 tests 475 ✅ 40s ⏱️ Results for commit aea4e9e. |
Test Results (shard 0)251 tests 251 ✅ 2m 10s ⏱️ Results for commit aea4e9e. |
Test Results (shard 3)368 tests 366 ✅ 49s ⏱️ Results for commit aea4e9e. |
Test Results (shard 2)588 tests 396 ✅ 2m 56s ⏱️ Results for commit aea4e9e. |
Test Results (shard 4)2 302 tests 2 302 ✅ 2m 36s ⏱️ Results for commit aea4e9e. |
Test Results 15 files 15 suites 11m 7s ⏱️ Results for commit aea4e9e. |
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 arefree in MeshWeaver.Plugins (
gh api search/code … repo:Systemorph/MeshWeaver.Plugins→total_count 0,measured 2026-09-05). No
<see cref>becomes ambiguous:ResolveOutputCurrencyis a new NAME, not anew overload — deliberately, because #3305's
RunActivity(…, LogMessage)overload turned anunqualified cref into
CS0419in 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), writtenfire-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 id3249) and wrote up why recovering it onthe 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.HandleExecuteScriptcreates the Activity node before itdispatches and stamps the originating cell onto it (
ActivityLog.HubPath = hub.Address.Path). Thecell → 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 itextends, as the issue asked):
OutputCurrency()— the stamp. Anything butNeverRunreturns unchanged.NeverRun: one listing for an Activity naming this cell onHubPath→Unverified.NeverRun, exactly as today.worst outcome is the answer the cell already gives today. Reading a specific known node stays
GetMeshNodeStream's job.Unverified, neverCurrent. 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.
this: a notebook of normally-stamped cells costs zero queries, because step 1 answers first.
IObservable<CodeOutputCurrency>, one verdict then completion) — noasync,no
Task<T>, no.Result. Immutable collections throughout (ImmutableArray<string>).Where it looks.
ActivityNamespacesreconstructs the layers visible from the cell alone — its ownActivityParentPath(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
DefaultActivityParentPathpointing at a fourth place is not covered (that lookupis a live partition-registry query, which
Mesh.Contractsits below). Where it applies the listingfinds nothing and the verdict falls back to
NeverRun— today's answer — so the gap costs nothingthat was already working.
Two cross-assembly agreements, both pinned — because no compiler sees them
The lookup filters on
nodeType:Activityand expands{viewer}, and both are declared in assembliesit 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 againstGraphNodeTypeNames.Activity.CodeRunHistoryTest.TheViewerSentinelExpandsTheSameWayOnBothSides— pinned behaviourally: itdrives
CodeNodeType.ResolveActivityParentand requires the answer the lookup assumes. Thedispatcher now spells the sentinel through
CodeRunHistory.ViewerHomeSentinelinstead of a secondliteral.
Verification
Deterministic repro, through a real mesh —
CodeCellCurrencyThroughTheMeshTest.ARunWhoseStampNeverLandedIsStillFoundByTheActivityItWrote. Twocells really run through
ExecuteScriptRequest, so two real Activities share one_Activitynamespace; 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
NeverRunfrom its owncontent (the defect, asserted); it resolves to
Unverified(the fix); a cell that genuinely never ranstill resolves to
NeverRun(non-vacuity — and the neighbour's activity in the same namespace mustnot be mistaken for it); the still-stamped neighbour resolves to
Currentwithout a query.Mutation-proved, twice — the guard was made to fail on purpose and quoted:
Drop
content.hubPath:{cellPath}fromRunsQuery→ 2 failures:Make
ResolveOutputCurrencynever consult the mesh (the pre-fix behaviour) → 1 failure:Both mutations reverted; the suite is green again.
Builds —
dotnet build -c Release -warnaserror, one project per invocation, all0 Error(s)/0 Warning(s):MeshWeaver.Mesh.Contract·MeshWeaver.Graph·MeshWeaver.Documentation·MeshWeaver.Graph.Test·MeshWeaver.Documentation.Test.Tests:
MeshWeaver.Graph.Testcurrency + run-history suites — 30/30 passed.DocumentationLinkIntegrityTest,WhatsNewEntryIntegrityTest,UnkeyedActivityLogMessageRatchetGuard— 9/9 passed. Full
MeshWeaver.Documentation.Testguard suite green.Work products committed
Doc/Architecture/ScriptExecution→ new subsection "Recovering the run for the READER — follow theedge, 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.
Category: Fix): A cell that ran no longer claims it never did.Not in scope
the cell ran, not what it produced.
3249Error. The stamp failing is a real fault; a reader who can nolonger see the consequence is not a reason to stop reporting it.
CodeViews.IsOutputStalerendering the recovered verdict) follows a core pinbump —
MW_PLATFORM_REFis7d644de95and this change is not in it yet.🤖 Generated with Claude Code