removeAvailabilityProjectionLocked compacted each resource's relationships
in place with a Relationships[:0] filter. That is only safe if registry
clones own their backing array, and they did not: cloneResource deep-copies
roughly twenty-five slice and map fields but Relationships was left as the
shallow copy from the struct assignment, so a clone shared the original's
array.
The reachable path is ordinary. GET /api/resources rebuilds a registry from
Monitor.UnifiedResourceSnapshot, whose records come from GetAll clones, so
the filter wrote through into the live monitor registry from an HTTP
goroutine without holding that registry's lock. Every request performed
unconditional same-value writes, and whenever a legacy checks edge was
genuinely dropped the compaction left a duplicated trailing relationship on
the live host resource. The corrupting case is the post-upgrade seed the
projection exists to repair.
cloneResource now clones Relationships like every other slice, and the
filter builds a fresh slice so correctness no longer depends on each caller
having cloned first.
Regression coverage:
- TestCloneResourceGivesRelationshipsTheirOwnBackingArray pins the clone
isolation, and fails with the clone line removed
- TestAvailabilityRepeatedProjectionKeepsHostRelationshipsStable drives the
real ingest path across repeated re-projection
599c8e634 changed the TrueNAS pool projection to publish the ZFS data vdev
layout ("mirror", "raidz2", "stripe") as Storage.Topology. Topology is the
closed discriminator vocabulary every storage consumer keys on, and TrueNAS
pools travel as ResourceTypeStorage, so the frontend identifies them solely
by storage.topology === 'pool'. On any appliance whose pools report data
vdevs, every pool therefore fell out of the TrueNAS page, its datasets
orphaned, and per-system pool counts read zero.
Nothing caught it because internal/truenas/fixtures.go carries no VDevs, so
the projection still returned "pool" for every fixture while the producer
side pinned the new "mirror" value. Both suites stayed green and disagreed
with each other.
Pools now publish Topology "pool" again, with the layout in the new
Storage.VDevLayout field. poolVDevLayout returns an empty string when the
native report carries no data vdevs so callers fall back rather than
inventing a layout, and the presentation surfaces that previously read the
layout out of topology now prefer vdevLayout and fall back to topology.
Alert typing was unaffected (unified_eval.go already routes non-dataset
storage to truenas-pool through its default arm); incident metadata carried
the layout string and now carries the discriminator again.
Regression coverage pins the boundary from both sides, driving real vdev
layouts rather than the vdev-less fixtures:
- TestPoolTopologyStaysDiscriminatorAcrossVDevLayouts (producer, five layouts)
- TestStorageTopologyAndVDevLayoutAreDistinctIdentityFields (clone and wire)
- pool identity boundary cases in truenasPageModel.test.ts (consumer)
- topology label cases in storageAdapters.test.ts and the TrueNAS drawer model
The registry audit requires exact_files to be sorted, and 5715ec754 inserted
useAlertOverridesState.test.tsx before useAlertDestinationsTabState.test.tsx
rather than after it, which failed the audit.
Verified: registry_audit.py --check and contract_audit.py --check both clean.
2118e3345 moved the Virtualization Hosts threshold section off a bare
`type === 'agent'` selection and onto provider-owned nodes, but landed without
the contract delta or a registered verification artifact, so the completion
guard blocked it.
The alerts contract now states the rule the fix depends on. Threshold sections
are keyed by override identity rather than resource type, and a resource that
appears in two sections has two different override keys, so a threshold saved
from the wrong row is written where alerting never reads it. Frontend
primitives records that surfaces classify through the shared
`isPulseAgentPlatformResource` classifier instead of re-deriving the standalone
versus provider-owned distinction from a raw resource type.
`useAlertOverridesState.test.tsx` already carried the right proof and simply
was not on the alerts-frontend-surface accepted list, so it is registered
rather than duplicated elsewhere.
Verified: canonical completion guard passes on the staged set, registry.json
parses.
Adds the subsystem contract deltas and verification artifacts for the
preceding wearout and physical-disk-node fixes, which landed without them.
The contracts now state the rule rather than leaving it implicit in each
caller. Wearout has two absent states and one real zero, -1 is the unreported
sentinel, and 0 is evidence only from a device that reports endurance at all.
storagehealth.WearoutReported is the single authority, and alerting, the
unified risk projection, and the frontend all gate on it. Storage-recovery
records that disk-to-node association is evidence-directional, so a disk with
no Proxmox scope still matches on node name while a scoped disk stays inside
its instance.
Proofs cover the predicate itself, a spent SSD alerting where it previously
stayed silent, absent evidence staying quiet on both the unreported sentinel
and a rotational zero, the view returning the sentinel rather than the struct
zero value, and the presentation helpers agreeing with the server.
Verified: full frontend suite 1061 files / 19405 tests, canonical completion
guard, gofmt, go build ./..., and internal/alerts, internal/monitoring,
internal/storagehealth, internal/unifiedresources.
Preserve tenant-scoped metadata through partial URL updates and project stable URLs across runtime identities. Use a safe adjacent launch control across overview tables with desktop and mobile regression coverage.
Keep the configured cluster URL as primary and recover discovered member
endpoints asynchronously while it is healthy. Reset member reachability
evidence when the effective address changes.
Refs #1437 and #1493
Route canonical cooldown refires through one resolved-state critical section and document the m.mu to resolvedMutex invariant. Exercise production refire, recovery, broadcast, alias repair, cleanup, and shutdown paths under the race detector while preserving cooldown history and start-time semantics.
Keep manual check commands active until registry collection completes. Deduplicate replayed and concurrent commands, bound collection and acknowledgement retries, surface registry result counts, and prove timeout, rate-limit, and replay behavior.