Priority and scope
P1: A tag-selected manual rehearsal can publish packages even when the maintainer explicitly selected dry run and left publish disabled. Treat this as release-safety work before the next rehearsal.
Reviewed default develop at 612e5c03bba3e811741385cd7440ffb5af43b8df. main at b9697f2f91aa737360479a434f7d3cc01ccaa8fd has the identical source tree, so this is not develop-only.
Evidence and cause
The publish condition starts with github.ref_type == 'tag' || .... The checks for inputs.dry_run == false and inputs.publish == true apply only to the second branch. The dry-run handoff can report a no-push rehearsal immediately before that publish job. The final step pushes all three packages.
Minimal reproduction (proposed, not executed)
Evaluate the workflow's job conditions with event_name=workflow_dispatch, ref_type=tag, ref=refs/tags/, inputs.dry_run=true, inputs.publish=false, and successful prerequisite jobs. Do not test against the real NuGet endpoint; use a condition-unit test or a mocked publish command.
Actual behavior predicted from source
The publish condition is true solely because ref_type is tag. The job can request production-environment approval and, if that gate permits it and credentials are present, invoke NuGet push. Actual production protection settings were not inspected, so automatic publication without approval is not asserted.
Expected behavior
Every manual dry run must exclude the publish job regardless of branch/tag selection. The publish=false input must not be bypassed by selecting a tag.
Fix direction
Separate tag-push events from manual dispatches. Require a push event for automatic tag publishing, and evaluate dry_run/publish explicitly for every manual-dispatch path. Keep production approval as defense in depth.
Regression acceptance
- Test the full event × ref-type × publish × dry_run matrix
- Tag dispatch with dry_run=true never schedules a publish or calls push
- Branch dry runs remain nonpublishing
- Authorized valid tag pushes and approved manual production runs still publish
- Validate conditions themselves, rather than just searching YAML for the word dry_run
Duplicate review
This is a narrow follow-up to closed #17 and merged #18: their broad dry-run requirement was implemented, but the tag-dispatch condition bypasses it. All 18 existing issue/PR records and 72 issue-conversation comments were inspected; none currently tracks this specific unresolved case.
Verification limits
This is a static source finding. The reproduction was not compiled or run, and no repository code, release workflow, or benchmark was executed in this review. The existing exact-head CI run passed its build/test and Roslyn-host jobs; release pack, artifact-verification, and publish jobs were skipped. That existing run does not validate this proposed regression.
Prepared with OpenAI Codex.
Validation update (2026-10-02)
Confirmed by source-extracted condition evaluation. The earlier static-only verification statement is superseded for the checks below. The source remains 612e5c03bba3e811741385cd7440ffb5af43b8df; no fix was applied.
Executed checks
An independent Python evaluator read the original job conditions directly from .github/workflows/ci.yml and evaluated their documented boolean/string expression subset, including the needs success gate. It completed 72 event/ref/input cases and 12 failed/cancelled/skipped-prerequisite controls. This was not a GitHub Actions workflow execution.
For workflow_dispatch on refs/tags/1.0.0, assuming successful prerequisites and a valid tag matching GitVersion:
| publish |
dry_run |
Pack reachable |
Artifact verification reachable |
Publish reachable |
| false |
false |
false |
false |
false |
| false |
true |
true |
true |
true |
| true |
false |
false |
false |
false |
| true |
true |
true |
true |
true |
Both dry-run tag dispatches therefore reach the publish job, including publish=false. Branch dry runs remained nonpublishing. All 12 prerequisite failure/cancellation/skip controls blocked publication.
Scope and limits
The raw publish expression is true in all four tag-dispatch rows, but the two dry_run=false rows do not reach publishing because pack is skipped. A true publish expression alone is insufficient; the successful dependency chain is part of this reproduction.
The matrix assumes later package and artifact validation succeeds. Production-environment protections and credential availability were not inspected. No workflow was dispatched and no package was published, so this confirms that the condition can reach the production approval gate, not that approval can be bypassed.
The evaluator follows GitHub's documented expression behavior and job dependency rules. Synthetic pull-request/tag combinations in the matrix are negative controls, not normal pull-request contexts.
Overall cloud-validation scope
The dated evidence above supersedes the earlier static-only verification statement for the listed cases; unexecuted regression-acceptance variants remain proposed. The unchanged full solution built in Release with zero warnings/errors. The existing suites recorded 653 passes and 2 failures across 655 tests, with no skips. Both failures, TrimmedConsumerPublishesAndRunsWithoutLiteMapperWarnings and NativeAotConsumerPublishesAndRunsWithoutLiteMapperWarnings, were cloud infrastructure blocked: ILLink's out-of-process ComputeManagedAssemblies task host failed with MSB4216 and Unix-domain pipe SocketException (13), permission denied. Trimming/AOT behavior was not established, and these tests are not intrinsically Windows-only. The repository's separate win-x64 final-package release lane still requires a Windows execution environment and Windows/MSVC toolchain.
The 20 focused generator tests comprised 14 intentional contract assertion failures reproducing defects and 6 passing controls/measurement cases; they are not an all-green acceptance suite. No production fixes or releases were made.
Priority and scope
P1: A tag-selected manual rehearsal can publish packages even when the maintainer explicitly selected dry run and left publish disabled. Treat this as release-safety work before the next rehearsal.
Reviewed default
developat612e5c03bba3e811741385cd7440ffb5af43b8df.mainatb9697f2f91aa737360479a434f7d3cc01ccaa8fdhas the identical source tree, so this is not develop-only.Evidence and cause
The publish condition starts with
github.ref_type == 'tag' || .... The checks forinputs.dry_run == falseandinputs.publish == trueapply only to the second branch. The dry-run handoff can report a no-push rehearsal immediately before that publish job. The final step pushes all three packages.Minimal reproduction (proposed, not executed)
Evaluate the workflow's job conditions with event_name=workflow_dispatch, ref_type=tag, ref=refs/tags/, inputs.dry_run=true, inputs.publish=false, and successful prerequisite jobs. Do not test against the real NuGet endpoint; use a condition-unit test or a mocked publish command.
Actual behavior predicted from source
The publish condition is true solely because ref_type is tag. The job can request production-environment approval and, if that gate permits it and credentials are present, invoke NuGet push. Actual production protection settings were not inspected, so automatic publication without approval is not asserted.
Expected behavior
Every manual dry run must exclude the publish job regardless of branch/tag selection. The publish=false input must not be bypassed by selecting a tag.
Fix direction
Separate tag-push events from manual dispatches. Require a push event for automatic tag publishing, and evaluate dry_run/publish explicitly for every manual-dispatch path. Keep production approval as defense in depth.
Regression acceptance
Duplicate review
This is a narrow follow-up to closed #17 and merged #18: their broad dry-run requirement was implemented, but the tag-dispatch condition bypasses it. All 18 existing issue/PR records and 72 issue-conversation comments were inspected; none currently tracks this specific unresolved case.
Verification limits
This is a static source finding. The reproduction was not compiled or run, and no repository code, release workflow, or benchmark was executed in this review. The existing exact-head CI run passed its build/test and Roslyn-host jobs; release pack, artifact-verification, and publish jobs were skipped. That existing run does not validate this proposed regression.
Prepared with OpenAI Codex.
Validation update (2026-10-02)
Confirmed by source-extracted condition evaluation. The earlier static-only verification statement is superseded for the checks below. The source remains
612e5c03bba3e811741385cd7440ffb5af43b8df; no fix was applied.Executed checks
An independent Python evaluator read the original job conditions directly from
.github/workflows/ci.ymland evaluated their documented boolean/string expression subset, including theneedssuccess gate. It completed 72 event/ref/input cases and 12 failed/cancelled/skipped-prerequisite controls. This was not a GitHub Actions workflow execution.For
workflow_dispatchonrefs/tags/1.0.0, assuming successful prerequisites and a valid tag matching GitVersion:Both dry-run tag dispatches therefore reach the publish job, including
publish=false. Branch dry runs remained nonpublishing. All 12 prerequisite failure/cancellation/skip controls blocked publication.Scope and limits
The raw publish expression is true in all four tag-dispatch rows, but the two
dry_run=falserows do not reach publishing because pack is skipped. A true publish expression alone is insufficient; the successful dependency chain is part of this reproduction.The matrix assumes later package and artifact validation succeeds. Production-environment protections and credential availability were not inspected. No workflow was dispatched and no package was published, so this confirms that the condition can reach the production approval gate, not that approval can be bypassed.
The evaluator follows GitHub's documented expression behavior and job dependency rules. Synthetic pull-request/tag combinations in the matrix are negative controls, not normal pull-request contexts.
Overall cloud-validation scope
The dated evidence above supersedes the earlier static-only verification statement for the listed cases; unexecuted regression-acceptance variants remain proposed. The unchanged full solution built in Release with zero warnings/errors. The existing suites recorded 653 passes and 2 failures across 655 tests, with no skips. Both failures,
TrimmedConsumerPublishesAndRunsWithoutLiteMapperWarningsandNativeAotConsumerPublishesAndRunsWithoutLiteMapperWarnings, were cloud infrastructure blocked: ILLink's out-of-processComputeManagedAssembliestask host failed with MSB4216 and Unix-domain pipe SocketException (13), permission denied. Trimming/AOT behavior was not established, and these tests are not intrinsically Windows-only. The repository's separatewin-x64final-package release lane still requires a Windows execution environment and Windows/MSVC toolchain.The 20 focused generator tests comprised 14 intentional contract assertion failures reproducing defects and 6 passing controls/measurement cases; they are not an all-green acceptance suite. No production fixes or releases were made.