Repository navigation
A 13-minute-old Hosting install records FleetWatchCadence.cs and the NODE is absent — three Hosting NodeTypes uncompilable, and memex's roll to the 3.0.0 candidate cannot converge #4588
Description
What is failing
memex's roll onto the 3.0.0 release candidate cannot converge, and the cause is an install that recorded a source file it did not land.
Hosting/Issue/Source/FleetWatchCadence is absent from the mesh while Plugins/Hosting's install record claims it, so every NodeType declaring it as a shared source fails to compile.
Measured 2026-09-17, control instance (memex.systemorph.com)
The node is absent, and the absence is readable:
get @Hosting/Issue/Source/FleetWatchCadence
-> Not found: Hosting/Issue/Source/FleetWatchCadence
search 'name:*FleetWatchCadence* partitions:all'
-> count: 0, truncated: false, coverage.partitions = 102 partitions, `hosting` among them
The install record claims it — Plugins/Hosting, version 42, package version 1.22.0, installedFromRef: HEAD, installedAtUtc: 2026-09-17T10:47:22Z, installedNodeCount: 222:
installedFiles["Hosting/Issue/Source/FleetWatchCadence.cs"] = 7e8e1d69f169117b39833c4a260334db4ff350996e5d54c47dfd5a6260441c74
So an install that ran 13 minutes before the read recorded the file, and the node is not in the store.
The file exists upstream: Hosting/Issue/Source/FleetWatchCadence.cs on MeshWeaver.Plugins main, added by 8fe9d70dfc at 2026-09-17T05:37:50Z (the #4471 fleet-watch work). Code search finds 23 hits for the symbol, including Hosting/InstanceAction.json, Hosting/DeploymentStatus/Source/DeploymentStatusLayoutAreas.cs and Hosting/FleetConsole/Source/FleetConsoleLogic.cs.
What the compile says, verbatim from a get on a node of the type:
MISSING SOURCES: 1 of 2 declared source queries for 'Hosting/DeploymentStatus' matched NO nodes on
this mesh ('shared=@Hosting/Issue/Source/FleetWatchCadence'). ...
CS0246 Error (line 436): The type or namespace name 'FleetWatchCadence' could not be found
and for Hosting/InstanceAction, 1 of 12 declared source queries with 8+ CS0103 FleetWatchCadence.
The deploy consequence
Deployments/memex-roll-8798-release-candidate-20260917 (created 07:33:54Z) set the image at 07:34:19Z, generation 770, and is still state: Running, phase: Verify one generation three and a half hours later. Ops/Status/memex, sampled 10:58:53Z:
generations: 2,converged: false- pod
…-ddd9f5f75-tfxpqon3.0.0-ci.8798, started 07:34:06Z,restarts: 1, noready - two pods on
3.0.0-ci.8710,ready: true— still serving, so there is no user-visible outage /api/versionstill answersafde4eab
🚨 Attribution caveat: the uncompilable types are a strong candidate for the refused readiness — a type that regressed on the new image is what nodetype_bake refuses on — but I did not read the new replica's own readiness reason. The fleet watch reports /health unreachable … TaskCanceledException for that pod, and for one of the two healthy ci.8710 pods as well, so that signal does not separate them. The three types are typed on the old pods (their content-types names only Hosting/ConfigAudit), so the compile failure is new on this image rather than pre-existing.
Why this is filed separately
This is the class already tracked by #2387 and #3659 — a package records a declared node as installed while the node is ABSENT — but every earlier instance argued from retained samples or a two-week-old record. This one has a minutes-old install record, the upstream file, the absent node and a blocked deploy in one reading, on the control instance itself. It is also the current blocker for tagging v3.0.0: no portal can move onto a candidate image while this holds.
Not done
No remediation was applied: the node was not hand-created, nothing was rolled back, and the stuck rollout was left in place (the old pods serve). The open question is why the install wrote 221 of 222 files — whether the node write was refused, lost, or never attempted — and InstalledPackageRepairService has not healed it across the 10:47Z install.
Read-only measurement 2026-09-17 by Claude Opus 5: get / search on the control instance, REST reads on both repositories, curl /api/version. No mesh write, no cluster access.
Activity
rbuergi commentedon Sep 17, 2026
Re-measured 20 minutes later — still true, and the shape is sharper: the partition is a MIX of two Hosting commits, and its two bookkeepers name different ones
Read-only on the control instance (memex.systemorph.com), 2026-09-17 11:1x–11:2xZ. Nothing written,
nothing rolled, no node hand-created.
The node is still absent — and at the commit the partition is SYNCED to, that is correct
But
Hosting/_GitSync(v457, read 11:1xZ) says the partition is at plugins061976bc(2026-09-15T21:46Z) —
lastSyncCommitShaandlastAttemptedCommitSha,lastSyncOutcome: ImportedWithErrors, attempted 11:13:52Z. And/healthagrees that this is the intended target:061976bcis 53 commits behind8fe9d70dfc, the commit that addsFleetWatchCadence.cs.GET .../contents/Hosting/Issue/Source/FleetWatchCadence.cs?ref=061976bc→ 404. So the file'sabsence from the partition is not a lost write at all: it does not exist at the sealed commit.
What IS wrong: the partition holds 1.22 files that reference it
Hosting/InstanceAction/Source/ObservationQueries(live node)lastModified2026-09-17T07:49:16.62Z, version 8061976bcFleetWatchCadencemain(1.22)Hosting/InstanceAction.jsonalso declaresshared=@Hosting/Issue/Source/FleetWatchCadenceHosting/InstanceActioncompilationStatus: Error, 11 ×CS0103 The name 'FleetWatchCadence' does not exist in the current context,adoptedModuleVersion: 1.21.6,currentModuleVersion: 1.21So the partition carries the 1.22 body of
ObservationQueriesbeside a 1.21 source set — agenuine mix, not a dropped node.
The second bookkeeper says 1.22 outright.
Plugins/Hosting(v42,lastModified10:47:22Z):version: 1.22.0,installedFromRef: **HEAD**,installedAtUtc: 2026-09-17T10:47:22Z,installedNodeCount: 222,autoUpdate: true,updatePolicy: Auto— and itsinstalledFilesnamesHosting/Issue/Source/FleetWatchCadence.cs. One partition, two writers, two answers: the sealreconciler says
061976bc(1.21), the registry install record says HEAD (1.22.0).That is the shape of #4355 — "registry auto-update and the seal reconciler both write a GitSynced
partition with separate bookkeeping". 🚨 Its fix, #4364 (
e76fa9f8f2, merged 2026-09-15T06:05:50Z),is in the image this instance runs (
afde4eabis 164 commits ahead of it, 0 behind), so this isnot "the fix has not landed" — either it is incomplete, or the writer here is a different one
(a package install at
installedFromRef: HEADwhile_GitSyncis seal-pinned). I cannot separatethose two from outside; the import activity for the 07:49Z write is what would.
Not the #4456 cause
Hosting/Issue/Source/FleetWatchCadence.cson pluginsmainis 16,710 bytes with zero NUL bytesand zero control characters, so it is not the unstorable-byte refusal that #4456 turned out to be.
But #4456's structural fix — #4467,
758d502c6c, merged 2026-09-16T06:01:13Z, moving the importmemory to the per-node manifest and naming the failed paths — is not in this image:
afde4eabis 9 commits behind it. So the instrument that would say which node an import could not write is
exactly what the stalled roll is carrying.
What changed since the filing, and what did not
Hosting/DeploymentStatusrecovered:compilationStatus: Okat 11:12:54Z, because its1.21 node declares no
sourcesarray at all — theshared=query the filing quoted is a 1.22declaration. So that one is an artefact of the mix, not a separate fault.
Erroron the control instance:Hosting/InstanceAction,Hosting/InstanceRequest,Hosting/Issue,Hosting/PlatformBuildInbox(plus the unrelatedEssentials/Mailbox).InstanceActionbeing one of them means no instance action runs on thecontrol instance — no Sample, Logs, HelmRelease or Roll — which is the same consequence A proven-commit import applies modifications but not additions — the partition is left referenced-but-incomplete, and a force update imports 0 nodes #4456
recorded.
/api/versionanswers3.0.0+afde4eab…(core commit dated2026-09-15T21:50:58Z), 3¾ h after the deployment set the image. The old pods still serve, so still
no user-visible outage.
c84c6c05(09-12):7 Hosting NodeTypes at
compilationStatus: Error,Hosting/Issuefailing withCS0117 'IssueKinds' does not contain a definition for 'JobFailed',CS0103 'SafeUrl',CS1061 'IssueContent' does not contain a definition for 'Workload'— different symbols, samesignature: files from one Hosting version referencing definitions from another, with
adoptedModuleVersion: 1.18.7againstcurrentModuleVersion: 1.22.What is owed
package install whose
installedFromRefisHEADwhile_GitSyncis pinned to a sealed commit.That is the question this issue should carry; the title's "install records a file it did not
land" is the symptom seen from one side.
either portal — and the loop worth naming out loud: the fix for the import defect that blocks the
roll can only arrive by the roll.
Leaving open on (1). Re-titling would be premature until the writer is identified.
Read-only measurement 2026-09-17 11:1x–11:2xZ:
get/searchon memex.systemorph.com and memex.meshweaver.cloud,curl /api/version+/health,gh api .../contents?ref=…and commit comparisons on both repositories. No mesh write, no cluster access, no roll.rbuergi commentedon Sep 17, 2026
Correction to my comment above: the probe waits 5 s, not 10 — which makes the reading stronger, not weaker
I took the probe timeout from the wrong line. Measured in the committed values:
deploy/helm/values.yamlshipsprobes.startup: periodSeconds 5, timeoutSeconds 5, failureThreshold 60;Systemorph/Memex(deployments/aks/memex/values.memex.public.yamlanddeployments/aks/memex-cloud/values.memexcloud.public.yaml) raiseperiodSecondsto 10 andfailureThresholdto 1080 to fit a cold bake, and leavetimeoutSeconds: 5untouched.So
/healthat 8.12 / 9.62 / 9.52 s is not marginally over the probe's patience — it is never once inside it.failureThreshold: 1080buys 1080 attempts at a wall. Everything else in the comment above is unchanged: the 3 h kill budget isperiodSeconds 10 × failureThreshold 1080, and 11:23:40Z + 3 h = 14:23:40Z still lands on the measuredrestarts: 1.Per-instance headroom against that fixed 5 s, same image line:
/healthwarmmemexmemex-cloudTwo things follow. It is a property of the instance, not of the release. And a warm reading is not the one that matters — a booting replica runs its cold bake on the same CPU and the same volume as these checks, so the number that decides a roll is
/healthon a pod that is still starting, which nothing has ever sampled.Correction by Claude Opus 5, 2026-09-17, read-only.
rbuergi commentedon Sep 17, 2026
Re-measured 2026-09-17 16:0x–16:2xZ on the control instance — the shape has moved, and the cause is now pinned
The install did not fail to write the file. It wrote it, and the partition's OTHER writer removed it.
1. The node is back, and what brought it back names the mechanism
Hosting/Issue/Source/FleetWatchCadencenow exists —version 1,lastModified 2026-09-17T16:03:45Z. What changed at that minute was not an install:Hosting/_GitSyncadvancedfrom
061976bc(2026-09-15T21:46Z) tod98fc2ac(2026-09-17T08:28Z) at 16:04:02Z, v548 → v549,lastSyncOutcome: Imported.The five files
maincarries that061976bcdoes not are exactly:All five appeared in the mesh at 16:03:45–46Z, each at version 1 — i.e. created, having been
absent.
contents/Hosting/Issue/Source/FleetWatchCadence.cs?ref=061976bc→ 404;?ref=main→200. A partition-shaped absence that ends the minute the seal moves to a commit carrying those
files is a prune, not a lost write. That answers the issue's open question ("whether the node
write was refused, lost, or never attempted"): it was attempted at 14:34:30Z, it landed, and
Hosting/_GitSync— which re-imports the same subdirectory every few minutes at the commit sealedfor the running framework — removed it again.
Not the #3659/#4525 class either: the file is a clean node file (16,710 bytes, valid
// <meshweaver>header, no NUL byte, no BOM), and the running image (afde4eab) predates #4525 inany case.
2. The record, still claiming a tree the partition does not hold
Plugins/Hostingv46 —1.22.1,installedFromRef: HEAD,installedAtUtc 14:34:30Z,installedNodeCount: 222,autoUpdate: true— still listsHosting/Issue/Source/FleetWatchCadence.cs = 7e8e1d69…. The record is not lying at stamp time; itis made false minutes later by the partition's other writer, and re-made true when the seal catches
up. Its falseness lasts exactly as long as the distance between the ref the installer resolved and
the seal.
3. The new shape is the same defect one step later
Of the three types, only
Hosting/Issueis still at Error (1 of 1,namespace:Hosting scope:descendants nodeType:NodeType content.compilationStatus:Error, coveragehosting):d98fc2acmain061976bc…d98fc2ac?Issue/Source/IssueLayoutAreas.csIssue/Test/IssueTests.csThe sealed import is a delta from the previous seal: it rewrote the view and left the test
alone — where the installer's
maincopy from 14:34:30Z was still sitting. The partition holds onefile from each tree. That is the mix in
Doc/Architecture/OnePartitionOneBookkeeping, with the twowriters' roles swapped.
🚨 So gate 2 (#4355) cannot close this class on its own. It makes the INSTALLER's delta full on
a synced partition; this mix was produced by the OTHER writer's delta, which has the identical blind
spot — it diffs its own baseline, never the mesh. Whichever writer diffs second lands a mix, so the
remedy is the one the invariant already names: one writer.
4. Why the install was at
HEADat all — the residue #4259 left#4259 pins the boot default install to the sealed commit precisely so that "the two unattended
writers of this partition now agree on its tree". But
InstanceAutoRegistrationService.ProvenRefcan only attribute a seal to a repository: a source with no
RepoPath— a remote registry, aregistered
IPackageSource— answers "no repository to attribute a seal to" and is listed at itsconfigured ref, which defaults to
HEAD. The control instance's plugin source is exactly thatshape, and
installedFromRef: HEADis the fingerprint of it.The fix
#4619 — gate 1c:
InstanceAutoRegistrationService.InstallasksPartitionContentOwnershipfor a candidate whose ref this boot could not prove, and holds wherethe installer does not own the content (
Undeterminedholds too — a wrong hold is lifted by thenext boot, a wrong install cannot be taken back). A proven ref is not held, so #4259's lane is
untouched; a mesh with no tracking layer is unchanged. The hold fetches nothing, adopts no module,
still re-asserts declared access, and is recorded as a named SKIP — never a failure, never a poll.
Without the gate, the test written for it reproduces the loop itself: a node the partition's own
writer does not carry is written back at the branch tip on the very next pass.
Not done
Read-only measurement — no mesh write, no cluster access, no remediation applied here. The stale
Plugins/Hostingrecord is left as it is: with the fix in place the installer stops resolving a refahead of the seal, and the claim becomes true again as the seal advances (as four of the five files
already did at 16:03Z).
Measured 2026-09-17 by Claude Opus 5:
get/searchon memex.systemorph.com, REST reads onMeshWeaver.Plugins (
contents,compare,commits).fix(#4588): only a WRITER counts as a writer, and a hold is not a ski…
rbuergi commentedon Oct 11, 2026
(re-posted 2026-10-11; originally by @rbuergi on 2026-09-17)
The attribution caveat, measured — and it is a SECOND, independent defect
This issue's caveat reads: "the fleet watch reports
/health unreachable … TaskCanceledExceptionfor that pod, and for one of the two healthyci.8710pods as well, so that signal does not separate them." It does not separate them because both pods are over that watch's budget, and the reason is not the bake gate.Measured 2026-09-17 (read-only:
curlon a public unauthenticated endpoint,get/searchon the control instance, oneSampleand threeLogsInstanceActions)Three consecutive reads from outside the cluster, same host, same pod:
https://memex.systemorph.com/healthDegradedhttps://memex.systemorph.com/alivehttps://memex.systemorph.com/readySame TLS, same ingress, same process — so the seconds are entirely in the untagged checks that only
/healthruns. The chart reads/healthas thestartupProbewithtimeoutSeconds: 10,periodSeconds: 10,failureThreshold: 1080.Control:
https://memex.meshweaver.cloud/healthanswers in 0.14–0.75 s, so a 9 s/healthis not inherent to the image family.The roll that replaced the one in this issue behaves identically
Ops/Actions/roll-memex-20260917-8812set the image at 11:23:36Z (generation 771). Podmemex-portal-deployment-d77fd7c88-x7nzm:restarts: 0,/healthunreachable on all three podsrestarts: 1restarts: 1,/healthunreachable on all three again11:23:40Z + (10 s × 1080) = 14:23:40Z — the container was killed at its startup budget, to the minute, and the next attempt started the same clock (
bake-reporton it records a fresh sweep at 14:29:41Z). It is a loop: the pod is killed mid-bake every three hours, so the bake never finishes either.🚨 The bake gate was GREEN across it
At 14:38:15Z the fleet watch recorded a body from that pod whose first word was
Degraded.ObservationQueries.GetTextAsyncthrows on any non-2xx, so that reading was a 200 — andHealthReport.Statusis the worst of the entries, so no registered check was Unhealthy at that instant,nodetype_bakeincluded. Loki holds nonodetype_bake … Unhealthyline for namespacememexsince 2026-09-16T00:51Z (38 matches total across all namespaces, newest 2026-09-16T06:20Z onclient-b).So the uncompilable Hosting types this issue documents are a sufficient explanation for the first part of a boot (
BakePhase.Running→nodetype_bakeUnhealthy → 503 → probe fails, by design) — but they do not explain a not-Ready pod at 14:51Z whose aggregate was a 200 at 14:38Z. Once the sweep settles, the pod still cannot graduate, because/healthcannot answer inside the probe's own 10 s timeout.Refuted while checking: source-discovery is not short here
The new pod's
source-discoveryreadsDegraded(a gap reached 996 ms = 100 % of the 1000 ms completion window), which looks like #3704. It is a proximity warning, not a short map: the pod settled 873*/Sourcenodes and 405*/Testnodes against 861 and 399 on the twoci.8710pods — larger, as content growth predicts. No fold ended early.Why the slow check has no name
The framework logs a duration for every check, at Information when the check is Healthy — which this fleet filters out of Loki for the
Microsoft.*categories. Measured: not one line matchingwith status Healthyhas ever landed inOps/Logs. And the/healthbody printed only the non-Healthy entries plus the census-tagged ones. So an expensive check that answers Healthy appeared nowhere — not in the body, not in the log. The Warning-level ones that are visible are all cheap (pending_module_activation17–68 ms over 156 samples today;content-typesandbundle_adoption~0.05 ms;required_modules239–550 ms), which is why the 8–10 s sits in a check nothing can name.Fixed in #4606:
/healthnow publishes its total and its slowest checks on line two, so the next occurrence names its own cause in onecurl. That PR deliberately does not widen any timeout. It also writes up two policies (HealthChecksrequest-timeout and output-cache, both 20 s) thatAddDefaultHealthChecksregisters andMapDefaultEndpointsattaches to nothing.Nothing here contradicts this issue
The absent
Hosting/Issue/Source/FleetWatchCadencenode and the three uncompilable Hosting NodeTypes are real and still unfixed — they are why the bake keeps having work to do on every boot. This adds the second half: even with that fixed, this instance's/healthis at the probe's budget, so a roll onto it is not safe until one of the two is addressed.Read-only measurement 2026-09-17 by Claude Opus 5. No mesh content write, no cluster access, no mutating InstanceAction.