Skip to content

feat(deploy): the control-owned shared artifact store — record contract, read-only mount, consumer binding - #6999

Merged
rbuergi merged 17 commits into
mainfrom
feat/control-owned-artifact-store
Oct 20, 2026
Merged

rbuergi merged 17 commits into
mainfrom
feat/control-owned-artifact-store

Conversation

@rbuergi

@rbuergi rbuergi commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Why

Maintainer, 2026-10-19: prebuilt bundles, modules, the assembly cache and the NuGet cache are content-addressed, yet every portal keeps its own copy on its own /data. On 2026-10-19 the public instance's 128 GiB share filled (≈121 GiB of it prebuilt-bundles/) and every core CD and module publication failed with "The specified share is full". The decision: one store per environment, owned by the CONTROL instance, produced only by the build server, mounted read-only by every other instance. Design: Doc/Architecture/SharedArtifactStore (in this PR and in the companion storage-prune PR; identical file).

What (increment 1, platform half)

  • Contract — DeploymentContent.ArtifactStore (role: Owner|Consumer, volume, consumer: sourceNamespace + sourceClaim) and VolumeClaim.ReadOnly. Either role renders PreWarm__PrebuiltBundleRoot=<mount>/prebuilt-bundles; a consumer also renders PreWarm__PrebuiltBundleRetention__Delete=false. DeploymentPortalConfig.ArtifactStoreProblems names every shape that would give the store a second writer or no store. WithArtifactStore, WithVolume(readOnly); parity table row.
  • Chart — persistence.<name>.readOnly → readOnly: true on the mount and the claim.
  • Operator — hosting-artifact-store-bind: the static read-only PV (ReadOnlyMany, csi.readOnly, Retain, distinct handle) + claim binding the owner's share into a consumer namespace, the share read off the owner's bound PV (never typed into a record); an existing binding is verified, never overwritten. ClusterRole gains persistentvolumes: create. Declared in the not-yet-wired list until the Plugins plan step emits it.

Evidence

  • MeshWeaver.Deployment.Contract.Test: 95/95 green, incl. the new ArtifactStoreRecordTest. Reverting the consumer read-only guard and the sweep disarm turns two of them red (checked).
  • Operator behaviour suite, bind block: 17/17 green; making the PV writable and skipping the share check turns 2 red (checked).
  • helm template with persistence.artifacts.readOnly=true renders readOnly: true on both the mount and the claim.

Live effect

None on merge: no record declares artifactStore yet. The ClusterRole change reaches the cluster only through Memex's helm-release lane. The record changes (control as Owner, then each portal as Consumer) and BAKE_PUBLISH_TARGETS are maintainer steps, listed in the design doc.

🤖 Generated with Claude Code

rbuergi and others added 2 commits October 20, 2026 12:06
…ct, read-only chart mount, consumer binding

Maintainer, 2026-10-19: the content-addressed build output every portal copies onto its own /data
(prebuilt bundles first) moves to ONE store per environment, OWNED by the control instance, produced
only by the build server, and mounted READ-ONLY by every other instance. This is the platform half of
increment 1 (Doc/Architecture/SharedArtifactStore):

- contract: DeploymentContent.ArtifactStore (role Owner|Consumer, the volume that carries it, and for
  a consumer the owner's namespace + claim) and VolumeClaim.ReadOnly; either role renders
  PreWarm__PrebuiltBundleRoot as <mount>/prebuilt-bundles, a consumer also disarms its per-portal
  bundle sweep; DeploymentPortalConfig.ArtifactStoreProblems names every shape that would give the
  store a second writer or no store (writable consumer, consumer creating a claim, store on /data,
  bundles.registry beside it, missing source); WithArtifactStore + WithVolume(readOnly).
- chart: persistence.<name>.readOnly renders readOnly on the mount and the claim.
- operator: hosting-artifact-store-bind creates the STATIC read-only PV (ReadOnlyMany, csi.readOnly,
  Retain, distinct handle) + claim that bind the owner's share into a consumer namespace — the share is
  read off the owner's bound PV, never typed into a record; an existing binding is verified, never
  overwritten. ClusterRole: persistentvolumes create.

Nothing here changes a live instance: no record declares the block yet, and the Plugins plan step that
emits the binding lands after this image is on hosting-operator:main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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

Consumer teardown can delete the shared backing store, and existing-binding verification does not enforce Retain reclamation.

5 open issues
What changed in this PR

Introduces the platform foundation for a control-owned shared artifact store, reducing duplicate build artifacts across portals. No deployment record enables it yet.

Changes:

  • Adds Owner/Consumer contracts, validation and bundle-root configuration.
  • Adds read-only Helm mounts and an operator command for consumer bindings.
  • Adds regression coverage and architecture documentation.
File Description
test/​MeshWeaver.Deployment.Contract.Test/​ArtifactStoreRecordTest.cs Tests serialization, configuration and validation.
src/​MeshWeaver.Documentation/​Data/​Architecture/​StoragePrune.md Documents storage measurement and pruning.
src/​MeshWeaver.Documentation/​Data/​Architecture/​SharedArtifactStore.md Describes the design and rollout stages.
src/​MeshWeaver.Documentation/​Data/​Architecture/​ConfiguringAnInstanceFromAspire.md Updates the configuration parity table.
src/​MeshWeaver.Deployment.Contract/​InstanceShape.cs Defines store roles and read-only volumes.
src/​MeshWeaver.Deployment.Contract/​DeploymentRecordExtensions.cs Adds fluent configuration helpers.
src/​MeshWeaver.Deployment.Contract/​DeploymentPortalConfig.cs Derives bundle settings and validates stores.
src/​MeshWeaver.Deployment.Contract/​DeploymentContent.cs Adds the artifact-store declaration.
deploy/​helm/​templates/​memex-portal/​deployment.yaml Renders read-only extra-volume mounts and claims.
deploy/​aks/​operator/​test/​stubs/​artifact-store-bind/​kubectl Simulates Kubernetes binding operations.
deploy/​aks/​operator/​test/​run-tests.sh Adds binding behavior tests.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​owner-pvc.json Models a bound owner claim.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​owner-pvc-pending.json Models an unbound owner claim.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​owner-pv.json Models the owner's Azure Files volume.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​owner-pv-disk.json Models an unsupported storage driver.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​existing-pvc-other.json Models a conflicting consumer claim.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​existing-pvc-bound.json Models an existing bound consumer claim.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​existing-pv-writable.json Models an unsafe writable binding.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​existing-pv-same.json Models a matching existing binding.
deploy/​aks/​operator/​test/​fixtures/​artifact-store-bind/​existing-pv-other.json Models a binding to another share.
deploy/​aks/​operator/​bin/​hosting-artifact-store-bind Creates and verifies consumer bindings.
deploy/​aks/​manifests/​hosting-operator/​operator-rbac.yaml Grants persistent-volume creation permission.
.github/​workflows/​hosting-operator.yml Registers the command and updates inventory checks.

Review effort: Balanced

Comment thread src/MeshWeaver.Deployment.Contract/DeploymentPortalConfig.cs
Comment thread src/MeshWeaver.Deployment.Contract/DeploymentPortalConfig.cs
Comment on lines +318 to +319
[Description("Mount read-only")]
public bool ReadOnly { get; init; }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6fab96e. SyncVolumes now passes isReadOnly to WithVolume, and a change of the flag counts as stale in the replacement comparison, just like a change of source.

Comment thread src/MeshWeaver.Documentation/Data/Architecture/SharedArtifactStore.md Outdated
Comment thread src/MeshWeaver.Documentation/Data/Architecture/StoragePrune.md Outdated
… sweep not scheduled (Enabled=false), Aspire honours readOnly, doc links and supported procedure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 10:23
@rbuergi

rbuergi commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

The red brew install + test (macOS) is infrastructure and has nothing to do with this diff. Homebrew moved azure-cli from homebrew/core to a cask ("No available formula with the name azure-cli … brew install --cask azure-cli"), so the job fails before it touches anything this PR changes. The check is not required.

rbuergi added a commit that referenced this pull request Oct 20, 2026
…er steps — same text as #6999

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…umers switch only after the rid-aware reader/publisher

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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

Consumer teardown and insufficient existing-volume validation can delete the shared artifact store.

3 open issues
3 resolved since last review

Review effort: Balanced

Comment thread deploy/helm/templates/memex-portal/deployment.yaml
Copilot AI balanced review requested due to automatic review settings October 20, 2026 10:32

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.

🔵 Needs a closer look

Consumer teardown and acceptance of existing Delete-policy bindings can delete the shared backing store.

3 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Fix malformed links in storage manuals

src/​MeshWeaver.Documentation/​Data/​Architecture/​SharedArtifactStore.md:20

[P2] Remove the duplicated closing brackets from the links in both new manuals. [Storage Prune]](...) does not form an inline Markdown link. The same typo occurs in SharedArtifactStore.md:121,138,178–181 and StoragePrune.md:110–112, leaving the measurement, retention and publication references without working navigation. Replace ]]( with ]( at those locations and retain the corrected ../ targets.

Review effort: Balanced

…t>/{rid}/prebuilt-bundles, expanded by the portal at boot

A compatibility key names no architecture and the publisher refuses a second architecture under one
key, so an amd64 and an arm64 build would collide in one shared store. The store keeps one bundle
store per RID in the layout every reader already reads; the record renders the root with a {rid}
placeholder and ConfigureMemexMesh expands it to the process's own <os>-<arch> before anything reads
the key (BundleRootRuntimeIdentifier). The producer needs no new code: one BAKE_PUBLISH_TARGETS
entry per architecture, <account>/<share>/<rid>. Docs: fixed the doubled ]] in the sibling links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 10:46
…rt mounts them at fixed, writable paths)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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

Consumer teardown can delete the shared backing store, and binding validation and runtime-specific lookup remain incomplete.

3 open issues
2 previously missed issues

In code that hasn't changed since last review

Medium severity Honor ReadOnly for fixed-path volumes

deploy/​helm/​templates/​memex-portal/​deployment.yaml:466

[P2] Honor ReadOnly for fixed-path volumes too, or reject those uses explicitly. A normal users volume with ReadOnly=true still mounts writable in Helm: its fixed mount and claim reference never read this flag (deployment.yaml:438–439, 706–707), and both new loops exclude it. Aspire honors the same setting, so the record now has different write permissions across renderers. Rejecting reserved names specifically for artifact stores does not cover ordinary volume declarations.

Medium severity Allow explicit false to replace an existing read-only setting

src/​MeshWeaver.Deployment.Contract/​DeploymentRecordExtensions.cs:426

[P2] An explicit readOnly: false cannot undo an earlier true: this OR permanently preserves the read-only setting through WithVolume. Reconfiguring the volume as writable therefore still produces a read-only mount. Use a nullable optional argument so omission preserves the existing setting and explicit false replaces it.

1 resolved since last review

Review effort: Balanced

Comment thread src/MeshWeaver.Hosting/BundleRootRuntimeIdentifier.cs
Copilot AI balanced review requested due to automatic review settings October 20, 2026 10:56

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.

🔵 Needs a closer look

Consumer teardown and insufficient existing-binding validation can delete the shared backing store.

3 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Resolve the owner volume for Aspire Consumers

memex/​aspire/​Memex.Aspire.Hosting/​MemexHostingExtensions.cs:325

[P2] An Aspire Consumer still gets its own Docker volume rather than the owner's store. SyncVolumes always derives the source as <resource-name>-<volume-name> and does not resolve SourceNamespace or SourceClaim. Owner and Consumer resources therefore receive different volumes, while the Consumer's bundle root points into its separate read-only volume and finds none of the owner's publications. Resolve the owner volume for Consumers, or explicitly reject this role on Aspire until shared binding is supported.

Review effort: Balanced

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    27 files      27 suites   1h 2m 3s ⏱️
13 917 tests 13 720 ✅ 197 💤 0 ❌
13 947 runs  13 750 ✅ 197 💤 0 ❌

Results for commit 7dbc584.

♻️ This comment has been updated with latest results.

…tore; registry template kept; Aspire refuses a Consumer

- hosting-pv-purge keeps every share a binding labels meshweaver.io/purpose=artifact-store or any other
  namespace's PV references, deleting only the stale PV object (first behaviour tests for pv-purge;
  disabling the guard turns 3 red).
- BundleRootRuntimeIdentifier keeps the unexpanded root at PreWarm:PrebuiltBundleRootTemplate for readers
  that serve other runtimes.
- Aspire's SyncVolumes refuses artifactStore.role Consumer by name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 11:16
rbuergi added a commit that referenced this pull request Oct 20, 2026
…eps the store (same text as #6999)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rbuergi

rbuergi commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Answers to the review's summary items that have no thread:

  • "Consumer teardown … can delete the shared backing store": correct, and serious. hosting-pv-purge deletes the share behind every Released PV a torn-down namespace claimed, and a consumer's binding PV is claimed by the consumer's namespace. Fixed in this PR: pv-purge keeps every share that a binding labels meshweaver.io/purpose=artifact-store or that any other namespace's PV references, and deletes only the stale PV object. Both conditions are read from one kubectl get pv -o json listing, and a listing that cannot be read deletes nothing. These are the first behaviour tests pv-purge has had (stubs/pv-purge): the own share is deleted; the store share and a cross-referenced unlabelled share are kept; shares_kept is reported. Disabling the guard turns 3 of them red (checked).
  • "Resolve the owner volume for Aspire Consumers": Aspire now refuses artifactStore.role: Consumer by name in SyncVolumes. A consumer's binding is a Kubernetes static PV, and a Docker volume of its own would hold none of the owner's publications.
  • Existing-binding validation: hosting-artifact-store-bind already refuses an existing PV pointing at another share, a writable one, or one claimed by another claim, and refuses a consumer claim bound to another volume. All of these are covered in the bind block.

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.

🔵 Needs a closer look

Shared-volume safety checks and runtime-specific publication guidance need correction before rollout.

3 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Make runtime-specific publication a rollout prerequisite

src/​MeshWeaver.Documentation/​Data/​Architecture/​SharedArtifactStore.md:81

Make runtime-specific publication a rollout prerequisite rather than saying the producer needs no new code. main-cd.yml:3554–3555 gives every architecture lane the same target list, and publish-bake-bundles.sh:1708–1710 publishes to every entry. Adding both runtime paths sends each architecture to both stores, populating the wrong subtree and triggering the other architecture's collision refusal. Also, the current ARM64 lane produces linux-arm64, not osx-arm64; the publisher accepts only Linux runtimes. Correct this paragraph and rollout step 3: require per-lane target selection, use linux-arm64 for the existing ARM64 build, and mark macOS publication as pending a macOS producer.

1 resolved since last review

Review effort: Balanced

…2); the arm64 lane is linux-arm64

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rbuergi

rbuergi commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

On "Make runtime-specific publication a rollout prerequisite" (previously missed, no thread): correct on every point. The doc no longer says the producer needs no new code. Per-lane target selection is now increment 2 and a rollout prerequisite for maintainer step 3: today every lane gets the same list and publishes to every entry, and the lease path is derived from the same targets by bake-publication-lock.sh and rebind-bake-scope.sh. The existing arm64 lane is named linux-arm64, and osx-arm64 is marked as waiting for a macOS producer. The identical copy in #7000 carries the same text.

Copilot AI balanced review requested due to automatic review settings October 20, 2026 12:21

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.

🔵 Needs a closer look

Existing-binding verification omits handle isolation, and creation can bypass verification by attempting to patch existing resources.

3 open issues

Review effort: Balanced

rbuergi and others added 2 commits October 20, 2026 14:31
…es the existing binding's handle is its own

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rbuergi

rbuergi commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

On the latest summary, both items are valid and fixed: (1) the PV and the claim are now created (kubectl create -f -), never applied, so a resource that appears between the read and the write fails the run instead of being patched into shape unverified; (2) an existing binding must carry its own volumeHandle (5th segment = the PV name), and one sharing the owner's handle is refused. The bind block is now 23/23.

Copilot AI balanced review requested due to automatic review settings October 20, 2026 12:31

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

Consumer binding can select the wrong share, accept stale staging credentials, or leave a conflicting reservation that blocks recovery.

2 open issues
2 resolved since last review

Review effort: Balanced

public Task StartAsync(CancellationToken cancellationToken)
{
var root = services.GetService<IConfiguration>()?[ShippedPrebuiltBundles.PublishedRootConfigKey];
if (!retention.Enabled)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added PrebuiltBundleRetentionStartupTest. It starts the real hosted service with a non-blank root and fires ApplicationStarted. Enabled is the positive control: the first pass runs and records its fault at once on a mesh-less host. Disabled records nothing. Making the guard if (false && …) turns it red (checked).

…er's explicit share attributes and refuses stale staging credentials

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 12:55
@rbuergi

rbuergi commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

On the latest summary:

  • Wrong share: the owner PV's explicit storageAccount/shareName/resourceGroup now win over the handle, the same way pv-purge reads them. Tested with an owner whose attributes differ from its handle.
  • Stale staging credentials: an existing binding whose nodeStageSecretRef differs from the owner's current one is refused. Tested.
  • Conflicting reservation blocks recovery: when the PV is created and the claim creation then fails, a re-run verifies that PV (same share, read-only, Retain, labelled, own handle, owner's Secret) and creates the claim. A PV or claim that fails verification is a named refusal by design: a binding pointing elsewhere is a person's decision, and this command never deletes. The refusal says exactly which object to remove.
    Bind block: 25/25.

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

Shared-volume validation gaps and a new test call rejected by the blocking-bridge guard remain unresolved.

3 open issues

Review effort: Balanced

Comment thread test/MeshWeaver.Hosting.Test/PrebuiltBundleRetentionEnabledTest.cs Outdated
rbuergi added a commit that referenced this pull request Oct 20, 2026
…ssify against the retention contract (+ StoragePrune, SharedArtifactStore docs) (#7000)

* feat(operator): hosting-storage-prune — measure what fills an artifact volume, classify it against the retention contract

On 2026-10-19 the public instance's 128 GiB /data share was 100% full and every core CD and module
publication failed with "The specified share is full". The portal's own bundle sweep had collected
nothing for weeks — correctly: the fleet consumer inventory was incomplete — but its verdict was a
log line in one pod.

hosting-storage-prune walks a data volume (or, with --claim, the control-owned shared artifact
store) in a short-lived Job in the instance's namespace, read-only by default, and reports ONE
gzip+base64 JSON fact the control plane records: the share's size and use, the top-level
directories, and every prebuilt-bundle identity and generation classified as current, young,
unsealed, superseded, legacy or other. It never deletes under prebuilt-bundles/ (release artifacts:
deletion needs the complete consumer inventory, the protected-label set and the publication lease).
--execute deletes only abandoned assembly-cache temp files (>1 day) and dumps shadowed by a dump
volume (>7 days), each re-checked in the pod at the moment of deletion.

Behaviour tests run the walker FOR REAL on a fixture tree: the execute run deletes exactly the two
permitted files and leaves every other byte — current/sealed/unsealed/young generations, legacy
identities, release markers, caches, the young temp file, the young dump, a temp file behind a
symlink — untouched.

Docs: Doc/Architecture/StoragePrune (the measurement and the classes) and
Doc/Architecture/SharedArtifactStore (the control-owned store, architecture-aware layout, one-way
sync to a client environment's control, local-module builds, maintainer steps); linked from the
retention contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: sibling links resolve (../), supported surfaces in the maintainer steps — same text as #6999

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: StoragePrune states the schedule is prepared, not running; consumers switch only after the rid-aware reader/publisher

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(operator): storage-prune classifies a legacy identity once, never again through its generations

Measured on the first report-only run against the public instance's data share (2026-10-20):
181 superseded and 197 unsealed generations at 0 KiB were the generations inside legacy identities,
counted a second time. The identity is the item; its generations are state only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(operator): storage-prune walks every per-architecture store of the shared store (<rid>/prebuilt-bundles); docs links fixed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: SharedArtifactStore — template key, Aspire refusal, teardown keeps the store (same text as #6999)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: no store role on an instance serving other runtimes (same text as #6999)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(operator): a failed read is never a zero — storage-prune reports unreadable paths, marks the report incomplete, and an incomplete walk deletes nothing

Review: an incomplete filesystem scan could read as a successful one (du/stat errors went to
/dev/null and counted as 0). The walker now emits @@mw-store unreadable for every failed read (on
fd 3, so a read inside $(…) cannot swallow it), the report carries complete/unreadableCount and
storage_complete=false, and --execute keeps every candidate when any read failed. Tested with a du
that fails for one identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(operator): storage-prune — a value flag without a value is refused (no endless shift loop); a failing listing marks the walk incomplete

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(operator): hosting-storage-prune is wired — MeshWeaver.Plugins#3993 emits it; move it into the plan list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: the prune command ships with #7000 — status says so until its image is live

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: per-lane target selection is a rollout prerequisite (increment 2); the arm64 lane is linux-arm64

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…; list the two new pages in the topic map

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 13:16

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.

🔵 Needs a closer look

The purge filter can miss another namespace’s share reference when CSI attributes are empty, allowing deletion of storage still in use.

2 open issues
1 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 20, 2026 13:25

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.

🔵 Needs a closer look

Shared-share deletion protection and consumer-binding validation still have unresolved correctness gaps.

2 open issues

Review effort: Balanced

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.

2 participants