Repository navigation
Description
Problem and scope
Core already has an enforced Sonar Quality Gate in its analysis workflow. Its previous tag-triggered CI publisher could bypass that result. PR #35 removed that job from current main; the replacement production publisher was subsequently activated in PR #37; its first authorized release is fully verified, including the separately authorized symbol recovery recorded below. The Core NuGet policy has now been migrated to the protected replacement workflow; the authenticated audit found no API keys or alternative matching policies. The first workflows/Core analysis-only wave was explicitly accepted with this limitation on 2026-10-23.
Follow-up to #24 and epic #238. Use the accepted app-foundation publishing controls as the behavioral reference; reuse shared support where appropriate rather than copying another project's policy, tokens or package identities.
Acceptance criteria
- Inventory Core's package IDs, publishing entry points, package owners, trusted-publisher policies and alternative credential routes; propose one reviewed package-to-policy mapping.
- Prepare a fail-closed publication design bound to the exact approved source revision, immutable release tag, successful mandatory CI run/attempt, completed Sonar task/analysis, accepted baseline, unchanged quality policy and fresh evidence. Handle superseded/rerun evidence explicitly.
- Bind validated package artifacts to the approved revision and content hashes. Reject substituted, expired, missing, wrong-revision or otherwise invalid evidence before obtaining publication credentials or uploading packages.
- Reuse the reviewed official
NuGet/loginanddotnet nuget pushapproach. Do not introduce a custom upload adapter. Analysis-only receipts (releaseEligible: false) must never authorize publication. - Prepare the transition from the legacy publisher so no alternative route can bypass the gate. Propose scoped trusted publishing, protected production approval and tag controls, allowing the single operator to approve their own run as previously agreed.
- Demonstrate positive and negative integration cases without a real package upload: failed/pending/missing gate, stale/wrong-revision/superseded evidence, tampered artifacts and the successful eligible path. Keep the accepted gate and baseline unchanged.
- Submit the implementation, rollback and controlled activation plan together for review. Distinguish prepared/tested controls from live enforcement; do not close as fully enforced while an enabled bypass remains.
Authorization boundary
This issue records planned work only. Publishing and existing Actions remain unchanged when it is created. Workflow changes, policy/credential migration, release tags and any actual upload require the appropriate later authorization. No automatic activation, merge or onboarding of other repositories is implied. Keep private operational evidence local and avoid new one-test-per-PR fragmentation.
Verified implementation status — 2026-10-24
Core PRs #31–#36 and workflows PR #31 are merged. #30 is complete. PR #35 removed only the legacy CI pack-and-publish job, updated its reviewed fingerprint and supplied the isolated tagged acceptance path. The real nuget-publish.yml remains unchanged with literal-false job/step guards.
Main 3e18daa24f74ab439e17ff5278eaf7a500a5d4a4 passed CI 40091833784 and Sonar 40091864676, both attempt 1. Independent artifact and live checks confirmed eight tests, 100% overall and new-code coverage across four new lines to cover, no findings/hotspots, and the unchanged accepted baseline, six gate conditions and 27 frozen profiles. Core has no separate E2E workflow. Readiness remains analysis-only (releaseEligible: false).
The authorized full tagged no-upload test passed:
- Preparation 40092501057, attempt 1 used one original non-release test tag on that exact main revision. It built and verified the
AndreGoepel.Core.0.0.0package and symbol archive, preserving authenticated origin and batch evidence. - Acceptance 40092632653, attempt 1 verified the full handoff, retained the acceptance intent, authenticated the stored intent, revalidated each file, read/discarded its bytes locally and retained both outcomes.
- Seven live negative invocations were rejected at their expected boundaries: altered manifest, altered package, production workflow context, consumer attempt 2, changed intent content hash, changed intent archive digest and use of the production intent verifier. The original positive handoff and intent then succeeded. These invocations did not rerun either hosted workflow or modify live Sonar policy.
- All four retained origin/batch/intent/outcome archives were independently downloaded and matched against GitHub's SHA-256 digests. The pinned verifier's completed-run inspection authenticated both successful simulated outcomes while live evidence was still fresh:
acceptanceOnly: true,outcomesAvailable: true, and false release/retry/remote-acceptance flags.cli-successmeans local simulation, not NuGet acceptance. - The separately retained protected rehearsal 40089669289 already passed all 1,086 shared offline contract tests, 11 recorder scenarios, four live pre-preparation rejections and eight versioned Core tests. Failed/pending/missing gate, stale/superseded evidence and other fault cases in that suite use simulated dependencies. They are not claimed as mutations of the central gate.
Together these records complete the positive/negative no-upload integration criterion. They do not prove real NuGet login/upload or production enforcement.
Both temporary environments required operator review, restricted refs and no administrator bypass; only the explicitly approved existing Core verification-token copies were added. Cleanup is verified: test tag and temporary environments/token copies removed, SONAR_NUGET_ACCEPTANCE_ENABLED=false, original sonar-main protection/secret metadata and rulesets unchanged. The production publication run 40092632742 was skipped. No package was uploaded. Main, tracked workflow files, Sonar policy and other repositories were not changed during execution.
Operator note: perform independent inspection from a clean exact-commit checkout. The first local inspection correctly rejected raw-source hash differences caused by mixed local line endings; a fresh checkout matched the hosted hash and passed the full inspection. No validation was bypassed and no hosted run was repeated.
Verified production controls — 2026-10-24
The separately authorized administrative cutover is complete; publishing remains inactive:
- NuGet's existing Core Trusted Publisher policy was replaced in place with
andregoepel/core, workflownuget-publish.yml, environmentnuget-publish, exact packageAndreGoepel.Core, and push-new-versions-only scope. The old Coreci.ymlmapping is absent. The refreshed authenticated inventory shows only ownerandre.goepel, no API keys, and no other matching/wildcard policy. Other package policies were unchanged. This verifies configuration; no OIDC exchange or real upload was attempted. Owner administration can introduce future credentials/policies and is not constrained by these pipeline controls. sonar-nuget-prepareaccepts onlyv*tags;nuget-publishaccepts only themainbranch. Both require reviewerandregoepel, permit the agreed self-review and prohibit administrator bypass. Approved existing Core Sonar credentials were configured and their names verified without exposing values.- Immutable NuGet release tags actively forbids updates/deletion of
refs/tags/v*, without bypass actors. An equivalent temporary rule rejected update and deletion attempts on each of two isolated non-release tags (four HTTP 422 rule rejections); both refs remained unchanged. The temporary rule and tags were removed. All original tags and the default-branch protection are unchanged. - Main is still
3e18daa24f74ab439e17ff5278eaf7a500a5d4a4. No tracked file, workflow, other repository, Sonar baseline or policy changed. No new workflow was started. Originalsonar-mainprotection and secret metadata remain unchanged.SONAR_NUGET_ACCEPTANCE_ENABLED=false; all real publisher guards remain literal false.
The production preparation trigger/guard and matching hash transition were reviewed and merged in PR #36; both preparation and publication remain disabled. Publishing activation and the first real release remain separate approvals and require fresh evidence. Existing receipts do not authorize a later release. #29 stays OPEN; external evidence storage remains deferred.
Remaining production cutover — issue stays OPEN
- Refresh the private NuGet owner/policy/API-key audit and replace the old
ci.ymlTrusted Publisher mapping under explicit authorization. The exact Core policy now requiresnuget-publish.ymland environmentnuget-publish; no API keys or alternative matching policies were present at this audit. Future owner-created routes remain outside the pipeline guarantee. - Configure and verify protected production environments and immutable release-tag controls, including the controlled update/deletion rejection test. Both environments and their explicitly approved Core Sonar credential copies are configured; all four disposable-tag mutations were rejected and test resources removed.
- Review and merge the production preparation trigger/guard and matching policy-hash transition. PR feat: prepare production NuGet tag trigger #36 is merged and exact-main CI/Sonar passed;
v*preparation and all publisher guards remain literal false. - Review and merge the separately authorized activation change, including both workflows and the matching preparation hash. PR #37 is merged as
06e816a983d27e4d215042acc47e63f1ddba2075; exact-main CI, Sonar, artifacts and production controls are verified. The protected pipeline is enabled. The first release remains separately authorized below. - Complete the separately authorized first real release. Core 1.0.6 was attempted on
06e816a: the main package was accepted, but symbol upload failed. Reconcile remote state and complete reviewed symbol-only recovery plus independent package/signature/PDB verification before closure.
Do not close #29 as fully enforced or enable publication automatically. External evidence storage remains deferred. Raw private evidence and complete local verification records are retained outside Git.
Production preparation review — PR #36
PR #36 prepares the production v* trigger, strict vX.Y.Z validation and the matching preparation workflow SHA-256 in the policy. The former test-tag route is retired; preparation is held behind a literal-false job guard until separately approved activation. The real publisher and all other Actions remain unchanged. No release tags or workflows were dispatched for this PR.
Validation for commit d27c3ce8aac655054084942b6135cedfd4e0881d: CI 40096044436, attempt 1 passed. Local Release build, 8 Core tests, 71 offline event/credential/outcome cases, formatting, actionlint and vulnerability audit passed. Local branch Sonar gate is OK with 100% coverage and no findings/hotspots; temporary local credentials were removed and the prototype returned to its prior stopped state. The latest central main analysis was checked read-only: task/gate successful, 100% overall/new coverage, accepted baseline and all 27 profiles unchanged. Central Sonar remains main-only; local branch evidence is not central PR readiness or release authorization.
PR #36 was merged by the operator as fdada154e8d9e7cdda75855d22510bccd22f1e57. Its exact-main verification is recorded below. Preparation and publishing stay disabled; #29 remains OPEN. No other repository or production control changed in this PR.
Main verification after PR #36 — 2026-10-24
Main fdada154e8d9e7cdda75855d22510bccd22f1e57 passed CI 40096599636, attempt 1 and Sonar 40096634714, attempt 1. Only the existing sonar-main deployment was approved; no workflow rerun or duplicate analysis was requested.
Independent verification matched both artifact ZIP digests, exact source and run/attempt bindings, mandatory CI steps, receipt/summary/contract/policy hashes, live task/analysis and the accepted baseline. Eight tests passed; overall and new-code coverage are 100% with four new lines to cover; no findings/hotspots. All six gate conditions and 27 frozen profiles are unchanged, with small-change exemption disabled and no authored coverage exclusions. The receipt remains analysis-only (releaseEligible: false) and expires at 14:09:12 UTC on October 24; it cannot authorize a later release.
The committed preparation hash matches its policy. Preparation stays literal false; all 18 publisher guards remain literal false. Protected production environment settings and secret metadata, original Sonar secret metadata, branch/tag rules, existing tags and repository variables match the approved setup. No new release tag, package upload, tracked edit, commit, merge, other Action/repository change or publishing activation occurred during verification.
The implementation and control preparation are complete. No additional PR is needed to retain this disabled state. The next change is the separately authorized activation review, followed by a separately approved first real release with fresh evidence. #29 remains OPEN; external evidence storage stays deferred.
Activation review — PR #37
PR #37, commit 66a63382678a20f3de614910dcde4e6b4515747c, removes the preparation activation guard and all 18 publisher job/step activation guards while preserving existing validation, success, evidence and cleanup predicates. The policy matches the committed preparation bytes (42e207024a2c8cdc3bc3994a157286e3683383ba218c8120c1252c5458fd639d). The runbook now distinguishes activation from the separately authorized first release and describes fresh evidence, protected approvals and partial-publication recovery.
PR CI 40098224639, attempt 1 passed for that exact head: format, build, 8 tests and vulnerability audit. Local Release validation, 71 offline workflow cases and actionlint passed. Local branch Sonar passed with 100% coverage and no findings/hotspots; temporary credentials were removed and the prototype stopped. Current main CI/Sonar were rechecked read-only: successful exact-main analysis, unchanged baseline, six gate conditions and 27 frozen profiles. Central Sonar remains main-only; the local scan is not central PR readiness or release authorization.
PR #37 was subsequently merged by the operator; the main verification and first-release proposal below supersede its unmerged review status. Merging the activation PR will enable future qualifying original release-tag runs through both protected environments; it does not create a tag or authorize a first release. No release tag, workflow dispatch, NuGet login or upload was performed. Other Actions, repositories and live controls remain unchanged. #29 stays OPEN for activation verification and the separately approved first real release; external evidence storage remains deferred.
Main after activation and first-release proposal — 2026-10-24
PR #37 is merged as 06e816a983d27e4d215042acc47e63f1ddba2075. CI 40098601737 and Sonar 40098642868, both attempt 1, passed on that exact main revision. CI confirms zero warnings/errors, eight tests and a clear vulnerability audit. Core has no separate E2E workflow. Only the existing analysis environment was approved.
Independent checks matched the artifact ZIP digests, source/run/attempt/CI-step bindings, contract/policy/summary hashes, live task/current analysis and accepted baseline. Analysis 2230dfac-dcfb-4264-83ad-ee39844726b2 has 100% overall and new-code coverage across four new lines, no findings/hotspots, and unchanged six gate conditions/27 frozen profiles. The clean candidate checkout matches the hosted source fingerprint. Preparation's committed byte hash matches its policy. Protected environments, secret metadata, tag/default-branch rules and repository variables match the approved controls. No preparation/publication run exists for this SHA.
Proposed first release: AndreGoepel.Core 1.0.6, original lightweight tag v1.0.6, on exactly that main commit. Version/tag/release are unused at this check; the previous published version is 1.0.5. The only library change rejects null error arguments in Result.Fail and Result.Fail<T> with ArgumentNullException (ParamName: error). Non-null behavior, public signatures, net10.0 target and zero dependencies are unchanged. Release notes explicitly call out this behavior change.
An isolated versioned local build and all eight tests passed, with no warnings/errors or known vulnerabilities. The package and symbol preview have the expected ID, 1.0.6 version, source commit and payloads. These local files are review-only, not an authenticated hosted handoff and must not be uploaded directly. The approval packet, generated-notes preview, package hashes and private control evidence are saved locally outside Git.
The current analysis-only receipt expires at 2026-10-24 14:36:31 UTC; releaseEligible remains false. After explicit release approval, recheck the exact main/version/controls and refresh CI/Sonar if necessary before creating any tag. Both protected approvals must fit within the evidence lifetime. The previously authenticated NuGet audit is from 12:44:22 UTC today; no fresh private NuGet audit or real credential exchange/upload was performed in this preparation turn.
No tag, GitHub release, preparation/publication dispatch or package upload was created. No Action, other repository or production control changed. #29 remains OPEN pending explicit approval for this version/commit and successful real package/symbol verification. External evidence storage remains deferred.
First authorized production release — partial result, 2026-10-24
The operator explicitly authorized AndreGoepel.Core 1.0.6 from exact main 06e816a983d27e4d215042acc47e63f1ddba2075. The immutable lightweight v1.0.6 tag and GitHub release were created. This execution supersedes the earlier proposal's not-yet-authorized/not-yet-created status.
Main CI 40098601737, Sonar 40098642868, tag CI 40100316471 and protected preparation 40100316402 passed, all attempt 1 on the approved revision. Existing main evidence was still fresh, so no rerun was needed. Independent source/artifact/live checks confirmed eight tests, 100% overall/new coverage, no findings/hotspots and unchanged baseline/gate/profiles. The production environment and tag protections remained unchanged.
Protected publication 40100437520, attempt 1 passed handoff validation, preserved/revalidated the intent and obtained temporary credentials via official NuGet/login. The main package PUT returned HTTP 201 at 14:02:08 UTC. The symbol invocation failed locally at 14:02:27 UTC with File does not exist after successful file existence and SHA-256 checks. Outcome preservation and cleanup passed. No retry was requested.
All four origin/batch/intent/outcome artifact digests were independently authenticated. The pinned completed-run inspector confirmed cli-success for the package and uncertain for the symbols, with no retry/release/remote-acceptance authority. NuGet initially showed the main package awaiting validation/indexing, with public package/PDB HTTP 404 at 14:16 UTC. At 14:21 UTC, the official package download returned HTTP 200: all six payload entries match the authenticated original batch, exact source/version/net10.0/zero-dependency metadata match, and dotnet nuget verify --all validates the NuGet repository signature. V3 indexing remains pending; the exact DLL-bound PDB endpoint still returns HTTP 404. The package is independently verified, but remote-symbol verification and full release acceptance are not complete.
The symbol-source defect is reproduced with the pinned SDK 10.0.402 against a credential-free loopback server: the explicit V2 --symbol-source causes legacy symbol-file lookup; using the existing V3 source without that override discovers the correct symbol endpoint and succeeds against the mock. No real second upload occurred. A minimal publisher correction, meaningful SDK regression test and separately reviewed symbol-only recovery are required; the normal first-attempt guards must not be bypassed. Preserve the original tag, release, accepted package, runs and authenticated original symbol archive.
#29 remains OPEN. Its remaining criterion now means successful reconciliation and reviewed recovery of this partial release, including independent package/signature/PDB verification. No Action, source file, NuGet policy, protection or other repository changed. Private evidence and the concrete recovery proposal are retained locally outside Git; external evidence storage remains deferred.
Core 1.0.6 symbol recovery and #29 acceptance — 2026-10-24
The separately authorized symbol-only recovery is complete. PR #38 was merged by the operator as 065b6564d4073e7a2e70aef00ff8f83d60fb2520. No tracked file, Action, other repository, NuGet policy or release tag was changed during this execution.
Main verification
- CI 40104880368 and central Sonar 40104916394, both attempt 1, passed on that exact main commit. Core has no separate E2E workflow.
- Authenticated artifact digests and independent live checks bind the source, CI jobs/steps, run attempts, policy/contract/summary hashes, completed task and current analysis. Analysis
e0973264-4d8a-4240-843e-c9f52ff23374has 8 passing tests, 100% overall/new-code coverage across 4 new lines to cover, zero findings/hotspots, and gate OK. - The accepted baseline
e3555256-765a-4247-9489-9a22f02ff42b, six gate conditions and 27 frozen profiles remain unchanged. Evidence was fresh throughout recovery; the analysis-only receipt expires at 16:03:01 UTC and never authorizes upload by itself.
Protected symbol-only execution
Recovery 40105324520, attempt 1, passed after explicit operator authorization and protected nuget-publish approval. The exact-SHA switch was enabled for this run and removed afterward; absence was read back. The original v1.0.6 tag still points to 06e816a983d27e4d215042acc47e63f1ddba2075.
The runner authenticated the original preparation/batch/intent/outcomes, reconciled the signed package and fresh main readiness, retained/reverified recovery intent, obtained credentials through official NuGet/login, revalidated before upload and pushed only the original AndreGoepel.Core.1.0.6.snupkg. All 17 ordinary publication steps were skipped. The log contains exactly one PUT, to NuGet's symbol endpoint, accepted with HTTP 201 at 15:07:31 UTC. There was no main-package push, new tag, retry or rerun.
Intent artifact 12563969586 and outcome artifact 12564527493 were independently downloaded and their archive digests verified. Their source, file hash, current readiness run/attempt and retained-intent identity match; every recovery step and cleanup succeeded. The recorded cli-success remains distinct from the independent remote verification below.
Independent remote verification
- AndreGoepel.Core 1.0.6 is indexed. Its downloaded package has a valid NuGet repository signature, correct original source/version/net10.0/zero-dependency metadata and all six payload entries identical to the authenticated original batch. Signed-package SHA-256:
c07de56345db2bafadc0eeeb78348d4b6c18b7f556a07b35163cd353e8986b35. - The public
.snupkgdownload returns HTTP 200 and exactly matches the original archive:f10463edb103b64f6d27f67efb948ccf3731136d472742d0b8eee4c5878d53dc. - Microsoft's
dotnet-symbol10.0.750501 derives the symbol key from the published DLL, validates its embedded checksum and downloads the PDB. An independent canonical-key GET also returns HTTP 200. Both PDB copies match the original hash20c2fd9d3c1f012195ab056eeabf39d61d33f693b8a7f53a212f9112a1ae7673.
The initial direct PDB probe used incorrect casing and continued to return 404 despite the PDB being available. A contemporaneous check of both URL forms confirmed that verification defect; it was not evidence of continued indexing delay. The independent verification uses the official client and canonical key. No additional upload was attempted. The one-time path is now inactive and its first-run guard is spent; removal of that path and documentation of the defect are tracked in #39. The ordinary publisher does not use that lookup.
The package and symbols are fully verified, and there is no enabled alternative publication route within the audited configuration. This completes #29's authorized first production release and recovery acceptance. Existing owner-administration limitations and finite GitHub evidence retention remain; external storage stays deferred. Keep the existing replay guard: the next ordinary release must use a subsequent reviewed commit, not the recovery SHA. Other Actions/repositories and production protection settings remain unchanged.
Activity
andregoepel commentedon Oct 24, 2026
Publisher correction and controlled 1.0.6 symbol recovery are prepared in PR #38, commit
84fc38080678ef02b56fb14c96454ceb89fd41e0..snupkgis pinned for one separately approved, main-only manual recovery with retained intent/outcomes and fresh CI/Sonar validation.CORE_106_SYMBOL_RECOVERY_SHAremains absent. No merge, manual recovery, NuGet login/upload, policy change or tag change was performed for this PR. Other Actions and repositories are unchanged.#29 remains open pending PR review, separate recovery authorization, successful symbol publication and independent remote PDB verification. The runbook documents those steps and the next-release SHA restriction.
andregoepel commentedon Oct 24, 2026
Closed after exact-main CI/Sonar, successful protected symbol-only recovery 40105324520, authenticated intent/outcomes, and independent package/signature/symbol-archive/PDB verification. Recovery switch removed; no main-package retry or tag change. Full acceptance evidence is recorded above. Cleanup of the exhausted recovery path and the lookup-casing defect is tracked separately in #39.