Repository navigation
Description
Four defects found while investigating a write-lane degradation on memex-cloud (2026-08-25, ~14:45–15:05 UTC). Reads and renders were healthy throughout; writes failed. Filed together because they were found in one incident, but they are independent and separately fixable — A and B are the outage, C and D are silent bugs the incident exposed.
Ruled out first, with evidence, so nobody re-treads it: this was not the fattened/draining pods. Both were wedged in Terminating (15 min past a 90 s app drain; terminationGracePeriodSeconds=1800, so they hold activations for half an hour) and were force-deleted — writes failed identically afterwards. It was also not the freshly-armed Modules:AutoRecycleOnStaleBuild (disproved separately on #2194).
A. CreateNodeRequest never replies: the create chain subscribes to the node's stream before the node exists
The primary user-visible failure. Three independent reproductions (create rbuergi/WriteProbe2224, different MCP session hubs), each a 60 s timeout, and the node genuinely absent afterwards. The message-flow trace names the whole chain:
AWAITING CreateNodeRequest → POSTED → RECEIVED runLevel=Started → ENQUEUED
→ ROUTED onTarget=True → HANDLER_ENTER
→ CREATE_CHAIN_SUBSCRIBED path=rbuergi/WriteProbe2224
→ HANDLER_EXIT state=Processed
… a handler was entered and no reply, completion or fault has been recorded since …
[ROUTE] NotFound: No node found at 'rbuergi/WriteProbe2224'. Closest ancestor is 'rbuergi' (remainder='WriteProbe2224').
[SYNC_STREAM] OnError for 6LGxECQAzE2EKtMwdHqhOA (Reference=MeshNodeReference { Path = }, Owner=rbuergi/WriteProbe2224)
MeshWeaver.Messaging.DeliveryFailureException: No node found at 'rbuergi/WriteProbe2224'.
System.TimeoutException: No response received in hub portal/mcp-rbuergi-04ecb166 within 00:01:00 for request CreateNodeRequest
The chain subscribes to the new node's sync stream, the route answers NotFound because it has not been persisted yet, the stream terminates with OnError, and the chain dies inside code that records no terminal stage — so the caller waits the full timeout and the create never lands. This is the "initial state never arrives" shape: the cure must be that the create chain does not depend on a stream that cannot exist yet (or tolerates absence until the persist lands), not a retry/watchdog around it.
[STALE-CALLBACK] diagnostics already name the gap precisely: "the work that owes the reply is either STILL RUNNING or terminated inside code that records no stage. Add hub.NoteRequestStage(request.Id, …) at that handler's terminal arms to split the two." Worth doing regardless — it is the difference between "hung" and "faulted" in every future incident.
B. A finished Store/Provision keeps re-racing its own compile-state node — a write storm that starves the fleet
Measured on the two serving pods: 39 and 95 MonotonicWriteGuard CONFLICTs per 2 minutes, versions racing 1277→1282 on Store/Provision/_Activity/compile-state, every loser rebased and retried. The guard's own log states the defect: "Find the writer that adopted a stale own-node snapshot; do not relax this guard." The provisioning request had already reached state: Done (7/7 phases) with requestedAction cleared — so a finished request was still driving compiles.
Collateral in the same window, on unrelated partitions:
[DeleteNode] timeout path=BusinessRules/Scope/_Access/Anonymous_Access stage=commit partial-deleted=0
System.TimeoutException: [DeleteNode:commit] the bottom-up delete … did not drain within 30s
[UpdateRemote] LATE_NACK_TERMINAL hub=cache/… target=BusinessRules/_Policy code=Unknown attempt=0
msg=TimeoutException … the optimistically-acked write did NOT apply and is not auto-retryable
Recycling the request instance and the Store/Provision NodeType hub halved the rate (39→21, 95→46), which confirms the writer as the source but not the mechanism. Possible fuel worth checking in that order: an explicit compile of a type in the package, and two git_hub_sync update triggers, both issued after the provision reported Done.
C. Provisioning order makes an import failure PERMANENT and silent
A package repo that ships an instance of its own new type fails on import — the instance is imported in phase 4, its NodeType compiles in phase 7:
NodeType 'MyAi/Panel' is not registered
and the sync pointer advances anyway, so every subsequent retry is a no-op and the failure is permanent until the repo changes. That combination — wrong order, plus a pointer that advances past a failed item — will bite every package whose repo contains an instance of a type it introduces. Either the phases must order type-compile before instance-import, or a failed item must hold the pointer.
D. UserIdentityCache hot loop: 2/second, on every serving pod
fail: MeshWeaver.Blazor.Infrastructure.UserIdentityCache[0]
As<User> for User: value is MeshWeaver.Graph.Configuration.NodeTypeDefinition (MeshWeaver.Graph), not convertible
360 occurrences per 3 minutes on each of the serving pods (one pod had 0, so it is triggered by something request-shaped, not by startup). The cache asks for a User and gets the NodeType definition named "User" — a path/identity collision between the type node and a user instance. .As<T>() is doing its job (returning null and saying why, rather than a silent miss), so the bug is the caller's resolution, and the loop means nothing backs off. Independent of A–C and cheap to fix.
Related: #2194 (same-day memory RCA — the flag was tested and cleared there), #2221/#2224 (registry static assets), the _App migration and MyAi provisioning that ran in this window.
Activity
rbuergi commentedon Aug 25, 2026
Item B follow-up: recycling does not clear the writer — the storm regenerates.
Measured across the recycle of both the request instance (
Admin/Provision/MyAi) and theStore/ProvisionNodeType hub:So the dip immediately after the recycles was transient and the rate returned. Recycling the owning hub is therefore not a remedy, and the earlier reading of "halved" should not be taken as progress — whatever adopts the stale own-node snapshot re-establishes itself after the hub comes back. That points away from per-hub in-memory state and toward something re-driven from durable state (a request whose terminal write did not actually stop the driver, or a watcher re-arming from the persisted activity), which narrows where to look.
Nothing has fired a new provision since 14:45 and none is planned, so the ongoing rate is not new work — it is the same finished request still driving.
Item D rate for the record: the
UserIdentityCacheloop is steady at 383 per 2 minutes (~3.2/s) on both serving pods, unchanged across the same window — independent of B, as expected.rbuergi commentedon Aug 25, 2026
🚨 Correcting item B — I filed it wrong, twice over
Two new pieces of evidence, and both cut against my own framing.
1. The conflicts are not
Store/Provision's, and never were. Grouping the last 4 minutes by node:Ten-plus types across five partitions. Independently corroborated: the identical pattern was observed at 09:15–09:30Z on
Store/Catalog/_Activity/compile-state(versions 1650→1651, 1653→1654, 1657→1658, 1661→1662), 5.5 hours before the MyAi provision existed. So "a finished request that won't stop" is simply wrong — the rate tracks compile traffic, on whichever types are compiling.2. More importantly: this race is documented, deliberate, and its loser is correct by design. NodeTypeCompilation.md → "What the claim does NOT fix":
So the
MonotonicWriteGuard CONFLICTlines are expected behaviour under concurrent compiles, not a defect, and my "write storm that starves the fleet" claim was not established — the write-lane failure is fully explained by item A alone, which reproduces with no provision running and no compile in flight.What genuinely remains open from item B, and it is narrower:
[DeleteNode:commit] … did not drain within 30son_Accessnodes across unrelated partitions.[UpdateRemote] LATE_NACK_TERMINAL … target=BusinessRules/_Policy … the optimistically-acked write did NOT apply and is not auto-retryable— an acknowledged write that silently did not apply is a correctness problem regardless of what else is true.Items A, C and D stand unchanged. Anyone acting on the original B framing — in particular pausing provisioning to "stop the storm" — should stop doing so: it will not change these numbers, and no provision has run since 14:45.
rbuergi commentedon Aug 25, 2026
🚨 Item A upgraded — it is NOT "the create is lost". The create SUCCEEDS and the REPLY is lost.
This inverts the correct client behaviour, so it matters more than the original filing.
Evidence, three nodes, two independent investigators
rbuergi/WriteProbe2224(mine)CreateNodeRequest60 s timeout; agetright after said Not foundcreatedDate 14:57:05,version 1, content byte-identical to attempt 2MyAi/SettingscreatedDate 14:54:36,version 1, content as sentAdmin/Migrations/AppTiles2state: Running, log["[start] RefreshAppTiles"], still running 25 min laterConsequences — "lost create" and "lost reply" demand opposite client behaviour
Admin/Migrations/AppTiles(Done 14:02) andAppTiles2(Running) are two mesh-wide sweeps armed 40 seconds apart, the second only because the first "failed".hub.NoteRequestStage(request.Id, …)at the handler's terminal arms is exactly what makes "never started" indistinguishable from "started, reply lost". That instrumentation is not a nicety — it is what lets a client decide whether retrying is safe.And reads are unstable in BOTH directions — I asserted otherwise earlier and was wrong
I repeatedly reported "reads and renders are fine". They are not, in this window:
get rbuergi/WriteProbe2224returnedNot foundat ~15:03 for a node created at 14:57. The route agreed —[ROUTE] NotFound: No node found at 'rbuergi/WriteProbe2224'logged at 15:02, five minutes after the node existed.deletethat returnedDeleted, agetstill returned the node asstate: Active(with alastModifiedI never wrote); a secondgetseconds later returnedNot found. So the delete had applied and the read was serving a superseded version.That read instability is very likely the same fault as the create chain's:
CREATE_CHAIN_SUBSCRIBED→ the subscription's own read of the just-written node answersNotFound→[SYNC_STREAM] OnError→ the chain terminates with no stage recorded → no reply. So the refined mechanism is a read-after-write visibility failure inside the routing/stream layer, not "subscribed before the persist" — the persist has happened; the reader cannot see it.What this changes for anyone acting on this issue
Not foundduring an incident; re-read before concluding absence.Admin/Migrations/AppTiles2has beenRunningfor ~25 minutes with only[start]logged — either a long walk or a wedge; it is being left alone rather than re-armed, which is the right call given the above.rbuergi commentedon Aug 25, 2026
Read side, discriminated: the two read routes fail differently, and only one of them is a defect
Independent evidence (peer session, same session, seconds apart) separates them cleanly:
search path:rbuergi/_App scope:children name:*LinkedIn*→ 1 result (only a stale 08-24 tile); two tiles minted at 14:44/14:45 absent.get @rbuergi/_App/LinkedIn-RolandLinkedIn-LinkedIn→ present, v2, correct content. Same for the second tile.So:
search/ queryget[ROUTE] NotFoundgetis what every caller treats as ground truthThat narrows item A's read-side symptom to the routing/stream layer and removes the query lag from the bug's scope entirely — a distinction I did not draw when I first wrote "reads are unreliable", and the sharper statement is: the authoritative read route intermittently lies; the lagging route was always documented as lagging.
Practical rule (please apply it rather than distrusting reads wholesale): verify existence with a direct-path
get, never withsearch; and if agetsays Not found during an incident, confirm with a second read before acting.🚨 A documentation/behaviour mismatch worth its own fix
AGENTS.md currently sanctions the unsafe pattern outright:
But an existence check answered from a lagging index is exactly how duplicate work gets armed: install-record checks, tile-mint duplicate guards, and the standard-packs
AlreadyInstalledprobe can each read "absent" for something that already exists and act on it. That is a plausible mechanical source of the duplicate mints and re-installs seen under load today — and it is doubly serious given the item-A finding above, since a caller whose create "timed out" and whose query then says "absent" has two independent reasons to wrongly conclude nothing happened, and to retry work that already ran.Proposed: strike "existence checks" from the valid-query list in AGENTS.md and CqrsAndContentAccess.md, replacing it with "existence checks for a specific path →
GetMeshNodeStream(path)/ directget; a query may only answer existence questions where a stale negative is harmless (autocomplete, listings)". Any existing guard that decides create-or-skip from a query should be treated as suspect.rbuergi commentedon Aug 25, 2026
Blast radius audited — the cost shape is duplicated WORK, not duplicated DATA. Keeping the severity honest, since "plausible source of duplicate mints" deserved checking rather than repeating.
The plugin-side existence checks that read through a query are all path-deterministic and idempotent, so a stale negative costs a redundant write rather than a second node:
AppTileRefresh.Missingdecides "no record" from an_Appchildren snapshot, then writes viaCreateOrUpdateNodeat the deterministic{viewer}/_App/{packageId}— a stale negative re-writes the same path.StandardPacks.AlreadyInstalledreads the manifest to decide whether to install; the installer'sCopyPlanskips by PATH, so a stale "not installed" re-runs an install that lands the same copies (that tolerance is deliberate — it is what makes re-install non-destructive to learner work).So nobody needs to hunt for corrupted tile records. The real cost is the one already visible: two mesh-wide sweeps armed 40 seconds apart — expensive and confusing, not corrupting.
The dangerous variant, for anyone auditing further: a query-driven existence check whose write target id is minted per attempt rather than derived from the path. That shape turns a stale negative into genuine duplicate data. None found in the plugin repos; worth a look wherever a guard and its write don't share a deterministic key.
Doc fix in flight (core's AGENTS.md line plus a new section in CqrsAndContentAccess.md) with this severity split written into it, so the guidance says which case is merely wasteful and which is corrupting.
rbuergi commentedon Aug 25, 2026
Severity note on item A's read side: the false negative is FREQUENT, and it has now corrupted two investigations
Three confirmed occurrences in one afternoon, on three different paths, by two different people:
getsaidrbuergi/WriteProbe2224MyAi/SettingsDefaultViews/ExamplesThat is not an oddity to note — it is a routine outcome, and it has already produced two wrong conclusions that were published before being caught:
The practical rule needs strengthening, because the earlier version was insufficient. "Verify by
get, notsearch" is necessary but not enough: all three cases above WERE direct-pathgets. The honest rule is:That applies to code as much as to humans: any guard that treats one
NotFoundas "does not exist" and then writes is exposed, which is the same hazard as the query-index one but on the route everybody trusts.Given the frequency and the demonstrated capacity to derail diagnosis, item A's read side deserves the same priority as its write side rather than being read as a footnote to it.
rbuergi commentedon Aug 25, 2026
Item A also fires on
memex(namespacememex/ memex.systemorph.com), not only memex-cloudFound while triaging #2165–#2167, which turn out to be a symptom of this. The signature is item
A's, verbatim, on a different partition and a different node type — so it is not memex-cloud-specific
and not
_App-specific.Loki, pods
…-z2xcv/…-97pjf, 2026-08-24. The incident nodeAdmin/_LogIncident/6e643370636ed34awas created at 17:54:18 and was at durable Version=3:Two things worth adding to item A from this:
persistence.Read, not only the route.HandleCreateOrUpdatedispatched its inner create — which it only does when its direct persistence read returns null —
for a node that had been durable for 2.5 minutes. So the visibility failure is not confined to the
routing/stream layer.
MonotonicWriteGuardrefuses the resultingVersion=1write, and the create still reportssuccess (
Node created at …follows the CONFLICT). So a caller cannot tell a create that landedfrom one the guard discarded — which is the same "the reply does not describe what happened"
property item A already names, at the other end of the write.
Also confirms item D independently:
6e643370636ed34ais theUserIdentityCache/As<User> … NodeTypeDefinitionincident, ticketed as #2163.Unrelated but adjacent: the create-vs-fold decision in
LogIncidentIngestServicewas inferred from apoint-read and treated a
NotFoundas proof of absence — exactly the inference this issue says isunsafe. On the path where the route fails but persistence can see the node, that sent a
first-sighting payload through
CreateOrUpdateNodeRequest's content-replacing merge and wiped thelive incident's draft, issue link and occurrence count. Fixed in
Systemorph/MeshWeaver.Plugins#663 (create-only, then fold — the store decides). That is a separate
defect; it does not address this one.
Review: target the exact grant, snapshot the failure list, bound the …
rbuergi commentedon Aug 25, 2026
Status after #2267 — two of the four items are done, and item A has a named root cause with a live reproduction
Leaving this open deliberately. Here is what is closed and what is not.
Item A — one concrete root cause fixed, the item is NOT closed
PackageInstaller.WaitForGatingpointed an exact-pathGetMeshNodeStreamat{root}/_Access/Public_Access— a node its own doc calls optional ("a partition whose node type does not gate never writes the grant, and that is a normal, common shape").That is not a wait. Per
Doc/Architecture/CqrsAndContentAccess.md→ "An OPTIONAL node" (added by #2244, hours before):So the method suppressed the gating write it existed to wait for, and every later read of that path was served a replayed NotFound for the breaker's backoff window. That is this issue's "read-after-write visibility failure", with a specific caller behind it rather than a framework-wide fault — and it matches the MeshWeaver.Education CI signature verbatim (
[SYNC_STREAM] OnError … Owner=<Course>/_Access/Public_Access→No node found→CreateOrUpdateNodeRequest … target <unset>→ 60 sTimeoutException→[ProvisionPlan] 'Edu' FAILED in phase 2/7 'Create home root').Rewritten to the sanctioned composition — a live children listing for existence, empty-on-absent, never a point read of an absent path.
Why the item stays open: I fixed the caller I could reproduce. I cannot prove it is the only one.
src/has 451GetMeshNodeStream(call sites, and a systematic audit for exact-path reads of optional nodes is real work that deserves its own change. In particular the memex-cloudrbuergi/WriteProbe2224case is not separately reproduced here — the storm-breaker mechanism explains it, but a second caller with the same shape would too.Two things worth keeping in mind for whoever picks that up, both learned while fixing this one:
select:pathalone is a listing that can never match.MeshNode.Pathis COMPUTED (Namespace + "/" + Id), so projecting only the path blanks both of its inputs and every row comes back with an empty path. Useselect:path,id,namespace.GatingSettleTimeoutis also the "nothing to wait for" answer, so a wait that used to NotFound instantly now genuinely waits. Scope it to the cases where something is actually expected, or the fix trades a correctness bug for a latency one (measured here: 30.3 s → 0.74 s once scoped).Item B — nothing to do
Retracted by the reporter as documented-by-design (
NodeTypeCompilation.md→ "What the claim does NOT fix"; the loser's outcome is already correct). No change made.Item C — FIXED
The baseline guard tested the outcome literal
"Failed", and a per-file failure reports"ImportedWithErrors"— a different string. So a partial import advancedLastSyncCommitShapast the commit whose nodes never landed,GitHubWebhookProcessor.SkipReasonanswered "already at this commit" for every later green build, and the Space's own UI read "up to date" throughout.StaticRepoImportResult.Failednow carries the tally the importer had always computed, and the decision is a named, pinned predicate (GitHubSyncService.MayAdvanceBaseline,PartialImportHoldsBaselineTest, 6 tests).The ordering half is deliberately not done. The issue offers "either the phases must order type-compile before instance-import, or a failed item must hold the pointer". The pointer guard is what makes the failure non-permanent, and it self-heals on the next sync of the same commit — the type node landed in the very pass that refused the instance. Reordering the import is a behaviour change on the path every deployment boots through and deserves its own change with its own pins.
Item D — FIXED by #2245 (merged)
Verified rather than assumed:
UserIdentityCacheis a DI singleton with one query subscription, andTryGetEmailrejects theNodeTypeDefinitionrow, so nothing wrong was ever stored. The collision lived in the query result — a NodeType declaration filed under the very type it declares — so removing that row makes the log line impossible at any emission rate, rather than merely rarer.Remaining on this issue: item A's audit — every other exact-path read of an optional node, and whether
rbuergi/WriteProbe2224had a second cause.Merge pull request #2359 from Systemorph/fix/2229-item-a-optional-nod…
rbuergi commentedon Aug 26, 2026
Post-close recurrence, 2026-08-26 13:08Z — the plugin-gate on #2364,
Store/CouponCompile succeeded, then the result write did not converge, then the gate marked it failed. Zero compiler diagnostics.
Attribution: #2364's diff is three workflow files and one Documentation.Test guard — it cannot reach a plugin compile. #2380 ran the same gate against the same plugins tip (
3df19b833) minutes apart andStore/Couponwasok. So this is the race, sampled unevenly, not a regression in either tree.Worth noting for the mechanism: every compile-status field was refused as stale in one burst — the writer's base was behind the live node for all five at once, which reads as the writer holding a stale snapshot of the whole node rather than losing a race on one field. That is the same shape as #2339's
lastModified-only refusals, one notch worse. Not re-run; the PR is rebased onto a fresh tree per the "my diff cannot reach it" rule.rbuergi commentedon Aug 26, 2026
Second post-close occurrence, on a different node, with the exact log line — which sharpens the diagnosis.
MeshWeaver.Plugins PR #702, run 32973942631,
Compile + render node repos (MeshWeaver from ACR):The compile did not fail — the RECORDING of it did
CompileStateMirrorcould not write the satellite carrying the compile state: a 60-second no-response onCreateOrUpdateNodeRequesttoportal/nodeops-*. With the state never landing, the gate readsStore/Purchaseas not-Compiledand reportscompile: FAILED.That is why these failures carry zero compiler diagnostics — there is nothing wrong with the code. It is the fail-closed-and-silent shape in its purest form: a successful compile, an unwritable result, and a gate that reports a compile error. Nobody files it as a write bug because it presents as a content bug, and the message names the node, not the lane.
Two occurrences, and what they share
Store/Coupon[AdoptedSourceStamp] … the write did not converge; IsDirty=trueafter five MergeGuard refusals in one burstStore/PurchaseCreateOrUpdateNodeRequesttimed out after 60 s, "target hub was not found"Different nodes and different proximate errors — a refusal storm versus a timeout — so it is not node-specific and probably not one bug. What they share is the outcome: the compile result cannot be persisted, and the gate translates that into a compile failure. Both are non-deterministic on identical input (#2380 passed the same gate on the same tip minutes after #2364 failed).
Worth noting about this run's timing
The
HeartBeatEventwarnings immediately after show the mesh enteringRunLevel=DisposeHostedHubs~3 s after the gate verdict, so this write was pending across the run's teardown. I am not claiming teardown caused it: the request had already been outstanding a full 60 s when it timed out, i.e. it was issued long before shutdown began. But whether the gate concludes while writes are still in flight is worth checking — if it does, a slow write becomes a reported compile failure by construction.What would make this actionable
The gate should distinguish "the compiler rejected this" from "we could not read/record the state". Right now they are the same red, and only the second one is a platform bug. A gate that says
compile FAILEDwith zero diagnostics is reporting on something other than the compile.Cross-reference: #2339 (
CrossHubPatchAtomicityTest) has an agent on the MergeGuard side, briefed that per-field partial merge is intentional and that a previous atomic-patch fix was built and reverted.rbuergi commentedon Aug 26, 2026
Partial mechanism for the recurrence — one half explained, one half explicitly NOT
Root-causing #2339 turned up a delivery defect that plausibly reaches the
Store/Coupon"compile FAILED with zero diagnostics,
IsDirty=trueafter fiveMergeGuardrefusals in one burst"recurrence — but only for one of the two write paths involved, so I want to be precise rather than
let it absorb the whole report.
The defect (fixed in #2391).
DataExtensions.ChangeContainsStampedWrite— the write-identitygate every cross-hub
stream.Updatewaits on — serialized the entire node to read two scalars(id, version). It runs once per still-pending patch on every emission of the owner's reduced stream,
so a burst of K concurrent writes to one node runs it up to K(K+1)/2 times, and it is
self-amplifying: the further emissions lag, the more patches are pending, the more full rebuilds
each next emission must pay before it can be delivered. Counted on a 288-write burst: 9 455
full-node rebuilds, at 0.9–1.9 ms each. The owner applied all 288 patches in 1.5 s while every
subscriber saw a ~2.7 s gap with no frames at all — on an idle 18-core box.
Why that can produce a non-converging write after repeated refusals
UpdateRemote's conflict recovery (RebaseSource) waits up toConflictRebaseBound= 5 s forthe writer's own mirror to advance past the version the owner refused, then rebuilds the patch
against whatever it has. A stalled mirror has not advanced —
STALE_MIRRORfires, the re-attempt isrebuilt from the same base, and it is refused identically. That is a write that never converges,
produced by repeated refusal, and the stall that disarms the recovery is worst precisely when many
concurrent writers are hitting one node — i.e. exactly when refusals cluster into "five in one
burst". 2.5–2.7 s idle scales past 5 s on a loaded 4-vCPU pod without difficulty.
It also matches the "non-deterministic on identical input" signature: #2380 passing the same gate on
the same plugins tip minutes later is what you would expect when the discriminator is how many
writers happened to overlap.
Why it does NOT explain the line that was actually logged
[AdoptedSourceStamp]writes viaworkspace.GetMeshNodeStream()with no path — the own-nodepath (
UpdateOwn). That never goes throughMergeGuardorUpdateRemoteat all. So the fiverefusals are evidence that concurrent cross-hub writers were active on that node in that window —
this defect's precondition — but I have not shown a causal chain from the delivery stall to that
own-node stamp write failing to converge.
So: this is a contributing mechanism, not a closure. If #2391 does not stop the recurrence, the
own-write half needs its own investigation, and the thing to instrument first is what clobbers or
loses the own-node commit that
stampHighWaterhas already recorded — not the merge guard.fix(#3051): do not claim "nothing was written" when the write had sta…
fix(mesh): a create whose store is unreachable answers Unavailable, n…