Skip to content

ci(module-pack): a caller can withhold packages from the hand-over (publish-withhold) - #6461

Merged
rbuergi merged 2 commits into
mainfrom
ci/w18a-module-publish-withhold
Oct 11, 2026
Merged

rbuergi merged 2 commits into
mainfrom
ci/w18a-module-publish-withhold

Conversation

@rbuergi

@rbuergi rbuergi commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

What

node-repo-module-pack.yml gets one optional input, publish-withhold: the names of the packages a publishing call must not hand to the registry. Their modules are still built, inspected and tested, and their bundle artifacts still reach the caller's gates. Only the hand-over is left out, and the receipt says publication: withheld.

Empty by default, so every existing caller behaves exactly as before.

Why

MeshWeaver.Plugins has published no module bundle, tag or seal since 2026-10-09T13:47Z. Its settle-locks job decides whether a main run may publish, and it decided for the whole tree: one package with an unsettled lock or an unstamped platform floor withheld all 78. A package merged in the last hour is in that state by definition (its floor is stamped by the main run that verified it, through a pull request, and then its lock settles through another), so under steady merging a commit with nothing waiting only appears in a quiet stretch of more than an hour.

Evidence, from Plugins main run 38099782041 (push, 45cff25a8e), job Settle manifest locks (main owns them):

Governance: its sources changed since its floor was stamped, so minMeshVersion 3.1.10484 is unverified for them
Hosting:    its sources changed since its floor was stamped, so minMeshVersion 3.1.10486 is unverified for them
Store:      its sources changed since its floor was stamped, so minMeshVersion 3.1.10487 is unverified for them
##[notice]45cff25a8e is NOT settled (22 lock(s) would move) — this run publishes, seals and tags nothing

and in the same run Tag module versions, publish-bake and publish-bake-previous-epoch concluded skipped while every gate was green.

The Plugins half decides per package and needs the lane to leave out exactly the modules of the packages that wait. publish is one boolean for the whole call, so the lane could not do that.

How

  • Plan step: a module whose entry's package is in the list (whole-line match, grep -qxF) gets need_publish=false and the fact withheld=true, whatever the ledger answered. Both publish modes read need_publish, so neither hands it over. No Published ledger record is written for it, so a later run still publishes it.
  • Receipt: published: false, publication: withheld.
  • Verdict (node-repo-pack-verify.py): the note names the withheld modules beside the superseded ones. No new failure condition.

The lane executes the list and does not derive it. Which packages wait, and that everything depending on a waiting package waits with it, is the caller's decision.

Tests

  • module-pack-batch.py --self-test: workflow_publish_withhold_problems asserts the input, the exact-name match, the plan line and both receipt lines against the real workflow, with five negative controls (hand-over left on, substring match, receipt says published, receipt does not name withheld, input missing).
  • node-repo-pack-verify.py --self-test: one published and one withheld is green and names the module; a run whose only leg was withheld says so; negative control with nothing withheld.
  • Run locally: both self-tests, check-workflow-shell.py, check-workflow-yaml-keys.py, check-workflow-timeouts.py, and dotnet build src/MeshWeaver.Documentation -c Release -warnaserror (0 warnings, 0 errors).

Not verified: the plan step has not run inside the lane yet. The first run that exercises it is the Plugins pull request that passes the input.

Order

This lands first. The Plugins pull request that passes publish-withhold cannot start a run before this is on main (an input the lane does not declare is refused at workflow start), which is the safe direction: the caller can never withhold on a lane that ignores the list.

Doc: Doc/Architecture/ModuleBuildArchitecture → Per-module deploy, item 4.

Pairs-with: none — additive optional workflow input; no public type or member is removed.

Refs Systemorph/MeshWeaver.Plugins#3326

🤖 Generated with Claude Code

…ublish-withhold)

The module lane's `publish` is one boolean for every module of the call. A caller whose
publication is decided per package (MeshWeaver.Plugins: a package whose lock is not settled,
or whose platform floor is not stamped for its sources, waits with its dependents) could
therefore only publish all of its modules or none, and one waiting package withheld the
whole tree: nothing was handed to the registry from Plugins main between 2026-10-09T13:47Z
and 2026-10-11.

`publish-withhold` takes the names of the packages a publishing call must not hand over.
Their modules are still built, inspected and tested and their bundles still reach the
caller's gates; the plan step turns only the hand-over off (exact match on the entry's
`package`), and the receipt says `publication: withheld`, which the lane's verdict names.
Empty by default, so every other caller is unchanged.

Guards: module-pack-batch.py --self-test asserts the input, the exact-name match, the plan
and both receipt lines, each with a negative control; node-repo-pack-verify.py --self-test
covers the verdict's note, with a negative control.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 02:05

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

Staged publication reporting omits withheld modules and incorrectly claims their ledger keys are already served.

1 open finding
What changed in this PR

Adds package-level publication withholding to reusable module-pack CI.

Changes:

  • Adds the optional publish-withhold workflow input.
  • Records and reports withheld publication outcomes.
  • Documents the per-package withholding contract.
File Description
.github/​workflows/​node-repo-module-pack.yml Plans and records withheld packages.
.github/​scripts/​node-repo-pack-verify.py Reports withheld modules.
.github/​scripts/​module-pack-batch.py Adds workflow contract checks.
src/​MeshWeaver.Documentation/​Data/​Architecture/​ModuleBuildArchitecture.md Documents withholding behavior.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

# says WHY nothing was published rather than leaving it to be inferred.
withheld=false
if [ "$PUBLISH" = true ] && grep -qxF "$(bk get --module "$MODULE" package)" "$RUNNER_TEMP/publish-withhold.txt"; then
withheld=true; need_publish=false

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.

Correct, and fixed in 309ef0c. Under publish-mode: staged a withheld module reached the staging step with need_publish=false and was recorded as "the module build ledger already serves this key", which is false for a key no registry serves.

  • The staging step now reads the withheld fact and takes its own branch before the ledger one. The record's reason says the caller withholds the package (publish-withhold), that the registry does not serve the key yet, and that a later run of the caller hands it over.
  • module-publication.py no longer asserts one cause for every record in its nothing-owed summary. It lists each record with the reason that record states. The per-record log line already printed rec.reason, so it now carries the right text too.
  • Guard: module-pack-batch.py --self-test asserts the staged branch against the real workflow, with a negative control that removes it and must be caught.

… own reason

Under publish-mode: staged, a withheld module reached the staging step with need_publish=false
and was recorded as "the module build ledger already serves this key" — false for a key no
registry serves, and the publication lane repeated it in its summary.

The staging step now reads the `withheld` fact and records that the caller withholds the
package and that the registry does not serve the key yet. The publication lane's
nothing-owed summary lists each record's own reason instead of asserting one for all.
module-pack-batch.py --self-test asserts the staged branch, with a negative control.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 02:14

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

Mixed staged and withheld receipts currently omit withheld modules from the verifier output.

1 open finding
Previously missed (1)

In code that hasn't changed since last review

Medium severity Report withheld modules when staged publishing has mixed receipts

.github/​scripts/​node-repo-pack-verify.py:238

A staged publishing call withholds only some packages, so its receipts contain both staged and withheld entries. This condition skips the withheld-reporting branch whenever staged is non-empty, and the following staged note does not include stood_down; the gate therefore passes while omitting every withheld module. Handle the staged case first and append stood_down, then use the all-stood-down branch for runs with no staged entries.

🧠 Review effort: Balanced

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

    24 files      24 suites   52m 42s ⏱️
11 647 tests 11 454 ✅ 193 💤 0 ❌
11 661 runs  11 468 ✅ 193 💤 0 ❌

Results for commit 309ef0c.

@rbuergi
rbuergi merged commit 0b619e0 into main Oct 11, 2026
53 checks passed
@rbuergi
rbuergi deleted the ci/w18a-module-publish-withhold branch October 11, 2026 07:23
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