Skip to content

ci(chart-freeze): the platform's chart copy is frozen — the chart lives in MeshWeaver.Deploy - #7194

Merged
rbuergi merged 4 commits into
mainfrom
ci/chart-freeze
Oct 25, 2026
Merged

rbuergi merged 4 commits into
mainfrom
ci/chart-freeze

Conversation

@rbuergi

@rbuergi rbuergi commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

The owner decided that this repository's chart is frozen: all chart edits go to Systemorph/MeshWeaver.Deploy, and #7127 deletes this copy. This PR adds a guard so the two copies cannot drift apart again.

Why

Systemorph/MeshWeaver.Deploy#1 seeded MeshWeaver.Deploy from this repository's chart material at fdcbc25983. After that, both copies stayed editable, and both changed (#7143, #7159, #7184, #7170, #7119 here; Deploy#2 and Deploy#3 there). Two pipelines baked hosting-operator:main, one from each copy, so :main flapped between two charts. Systemorph/MeshWeaver.Deploy#4 ports this repository's changes since the seed into Deploy.

What

  • scripts/check-chart-freeze.py reads git diff --name-status --no-renames <merge base> HEAD. It refuses any frozen path that is added, modified, retyped, or renamed into, with the message the chart lives in Systemorph/MeshWeaver.Deploy. A pure deletion passes. A line it cannot parse counts as a violation, so it never passes by default.
  • Frozen means exactly what Deploy#1 copied:
    • deploy/helm/, deploy/helm-db/, deploy/aks/values.aks.yaml, deploy/aks/manifests/, deploy/aks/scripts/, deploy/homebrew/
    • .github/workflows/chart-gate.yml, .github/workflows/homebrew.yml
    • scripts/check-config-key-coverage.py, .github/scripts/test-bundle-registry.py
  • Exempt: .github/scripts/build-run.py and deploy/build-host/**. main-cd builds the build-host runner image from them here, and the Deploy chart gate reads them from a declared checkout of this repository.
  • dotnet-test.yml gets a new job, Chart frozen (lives in MeshWeaver.Deploy):
    • It runs on every pull request, with no path filter and no credential, and runs its self-test first.
    • It is a needs: of Consolidate test results and has its own != 'success' fail step. It cannot be skipped into green.
  • The policy chart-lives-in-deploy is added to the register. Chart Ownership and the Runner Pool records the move.

Verified locally

What this means for other open work

Pairs-with: none — no public type or member is removed.

🤖 Generated with Claude Code

…es in MeshWeaver.Deploy

MeshWeaver.Deploy was seeded from this repository's chart material (Deploy#1). Both copies stayed
editable and both moved, so two pipelines baked hosting-operator:main from two different charts.
The paths Deploy#1 copied are now frozen here (policy chart-lives-in-deploy):

- scripts/check-chart-freeze.py refuses a pull request that adds, modifies, retypes or renames
  into deploy/helm, deploy/helm-db, deploy/aks/{values.aks.yaml,manifests,scripts},
  deploy/homebrew, chart-gate.yml, homebrew.yml, check-config-key-coverage.py or
  test-bundle-registry.py, with "the chart lives in Systemorph/MeshWeaver.Deploy". A pure
  deletion passes (that is how #7127 removes the copy). build-run.py and deploy/build-host/ stay
  editable: main-cd builds the build-host image from them.
- Job "Chart frozen (lives in MeshWeaver.Deploy)" runs it on every pull request against the merge
  base, self-test first, and is a need of the required check with its own fail step.
- The policy is registered and ChartOwnershipAndRunnerPool records the move.

Refs #7127.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 25, 2026 12:35
@rbuergi
rbuergi enabled auto-merge (squash) October 25, 2026 12:36

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

The checker permits frozen-path bypasses and introduces violations of the existing Git path-listing guard.

3 open issues
What changed in this PR

This PR freezes core’s deployment chart material as ownership moves to Systemorph/MeshWeaver.Deploy, while allowing deletion of the old copy for #7127.

Changes:

  • Adds a chart-freeze checker with self-tests and explicit build-host exemptions.
  • Connects the new pull-request gate to Consolidate test results.
  • Documents chart ownership and registers the freeze policy.
File Description
src/​MeshWeaver.Documentation/​Data/​Architecture/​PolicyNotProse.md Registers the chart ownership and freeze policy.
src/​MeshWeaver.Documentation/​Data/​Architecture/​ChartOwnershipAndRunnerPool.md Documents the move, frozen paths and exemptions.
scripts/​check-chart-freeze.py Adds diff validation and self-tests for the freeze.
.github/​workflows/​dotnet-test.yml Adds the freeze job and propagates failure to the required check.

Review effort: Balanced

Comment thread scripts/check-chart-freeze.py Resolved
Comment thread scripts/check-chart-freeze.py Resolved
Comment thread scripts/check-chart-freeze.py Resolved
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    25 files  ±0      25 suites  ±0   56m 53s ⏱️ + 1m 21s
11 684 tests ±0  11 682 ✅ ±0  2 💤 ±0  0 ❌ ±0 
11 710 runs  ±0  11 708 ✅ ±0  2 💤 ±0  0 ❌ ±0 

Results for commit 5220ebc. ± Comparison against base commit f820aaf.

This pull request removes 59 and adds 22 tests. Note that renamed tests count towards both.

   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---, expected: True)
   --- End of inner exception stack trace ---, kind: "Timeout", layer: null)
 ---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---
 ---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
 ---> (Inner Exception #1) MeshWeaver.Messaging.HubDisposingException: Hub node/a is shutting down — cannot create "x". The address may reactivate (recycle / restart); retry to get the authoritative answer.
 ---> (Inner Exception #1) System.ArgumentException: Value does not fall within the expected range.<---
 ---> (Inner Exception #1) System.InvalidOperationException: boom<---
 ---> (Inner Exception #1) System.InvalidOperationException: source B is misconfigured<---
…
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall as an aggregate's FIRST member", fault: System.AggregateException: One or more errors occurred. (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
 ---> MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall as an aggregate's SECOND member", fault: System.AggregateException: One or more errors occurred. (The operation has timed out.) (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
 ---> System.TimeoutException: The operation has timed out.
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---
, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall nested in an aggregate inside a wrapper", fault: System.InvalidOperationException: outer
 ---> System.AggregateException: One or more errors occurred. (Value does not fall within the expected range.) (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
 ---> System.ArgumentException: Value does not fall within the expected range.
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---

   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall wrapped as InnerException", fault: System.InvalidOperationException: outer
 ---> MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "an aggregate of unrelated faults", fault: System.AggregateException: One or more errors occurred. (The operation has timed out.) (Value does not fall within the expected range.)
 ---> System.TimeoutException: The operation has timed out.
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.ArgumentException: Value does not fall within the expected range.<---
, expected: False)
MeshWeaver.Messaging.Hub.Test.FaultClassifierTest ‑ Classifies(shape: "a bare disposed object with no context — not prova"···, exception: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'MemoryStream'., kind: "Fault", layer: null)
MeshWeaver.Messaging.Hub.Test.FaultClassifierTest ‑ Classifies(shape: "a cancellation whose timeout cause sits at aggrega"···, exception: System.OperationCanceledException: t
 ---> System.AggregateException: One or more errors occurred. (x) (The operation has timed out.)
 ---> System.InvalidOperationException: x
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.TimeoutException: The operation has timed out.<---

   --- End of inner exception stack trace ---, kind: "Timeout", layer: null)
MeshWeaver.Messaging.Hub.Test.FaultClassifierTest ‑ Classifies(shape: "a driver wrapping a connect timeout", exception: MeshWeaver.Messaging.Hub.Test.FaultClassifierTest+FakeDbException (0x80004005): Failed to connect
 ---> System.TimeoutException: The operation has timed out., kind: "Transient", layer: "Store")
MeshWeaver.Messaging.Hub.Test.FaultClassifierTest ‑ Classifies(shape: "a hub announcing its disposal", exception: MeshWeaver.Messaging.HubDisposingException: Hub node/a is shutting down — cannot create "stream". The address may reactivate (recycle / restart); retry to get the authoritative answer.
Object name: 'node/a'., kind: "HubTeardown", layer: null)
MeshWeaver.Messaging.Hub.Test.FaultClassifierTest ‑ Classifies(shape: "a mixed aggregate keeps the defect", exception: System.AggregateException: One or more errors occurred. (Success) (boom)
 ---> System.Net.Sockets.SocketException (0x80004005): Success
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.InvalidOperationException: boom<---
, kind: "Fault", layer: null)
…

♻️ This comment has been updated with latest results.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… matches its own path

Review findings on #7194:
- git C-quotes a path with a non-ASCII byte, a quote or a control character, so an added
  deploy/helm/templates/café.yaml reached is_frozen() quoted and passed. The diff is now read with
  -z and parsed as status/path fields; check-git-path-listings.py is clean (0 of 130).
- A frozen directory replaced by a tracked symlink produced D for its files and A deploy/helm, and
  the directory path itself matched no prefix. A frozen directory now matches its own path too.
- The file input is --diff-file (a saved -z listing), so the listing guard no longer reads the
  option name as an unquoted listing.
Self-test: 15 cases, including the non-ASCII addition, the directory-to-symlink swap, a rename into
a frozen path, an unknown status and a dangling status.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi enabled auto-merge (squash) October 25, 2026 12:47
@meshweaver-cloud
meshweaver-cloud Bot disabled auto-merge October 25, 2026 12:48

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

Two confirmed path-classification gaps allow prohibited additions to pass the deletion-only gate.

2 open issues
3 resolved since last review

Review effort: Balanced

Comment thread scripts/check-chart-freeze.py Resolved
Comment thread scripts/check-chart-freeze.py Resolved

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

The guard permits ancestor-symlink replacements and accepts malformed saved diff records as clean.

2 open issues
2 resolved since last review

Review effort: Balanced



def is_frozen(path: str) -> bool:
return _matches(path, FROZEN) and not _matches(path, EXEMPT)

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.

That's true in principle, but I'm not changing this PR for it. The gate exists to stop well-meant edits to a copy that now lives in MeshWeaver.Deploy. Replacing deploy/aks with a symlink to a new editable tree is a deliberate, reviewable restructuring that nobody makes by accident. Also, #7127 has already deleted every frozen path from main (I checked: deploy/aks now holds only the docs, envs and infra), so nothing frozen sits behind such a symlink. Refusing a non-deletion at an ancestor of a frozen path, while keeping the exemptions, is tracked as hardening in triage (rbuergi/Feedback/20261025-chart-freeze-hardening).

Comment on lines +87 to +98
status = fields[i]
if not status or status[0] not in KNOWN_STATUS or i + 1 >= len(fields):
# Past an unreadable field the status/path pairing is lost, so nothing after it can be
# read either: one violation names where reading stopped.
out.append(("?", status))
break
# A C or R entry carries two paths; --no-renames should never produce one, but read both.
width = 3 if status[0] in "CR" else 2
for path in fields[i + 1:i + width]:
if is_frozen(path) and status[0] != "D":
out.append((status[0], path))
i += width

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.

I agree the reader should validate the whole status token and the path count. It isn't reachable on a realistic input, though. --diff-file is git's own -z output saved by CI, never written by hand, so neither a truncated R record nor a Dgarbage status occurs. The production path --base reads git directly. This is tracked together with the ancestor case in rbuergi/Feedback/20261025-chart-freeze-hardening.

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

Human confirmation is needed that the previously raised edge cases cannot let prohibited changes pass the new gate.

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