diff --git a/.github/workflows/nuget-online-convergence.yml b/.github/workflows/nuget-online-convergence.yml
index 48b291aa..78bbeb52 100644
--- a/.github/workflows/nuget-online-convergence.yml
+++ b/.github/workflows/nuget-online-convergence.yml
@@ -11,7 +11,7 @@ permissions:
jobs:
verify-convergence:
- if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.event == 'push'
+ if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_repository.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
@@ -31,13 +31,20 @@ jobs:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
+ - name: Download release metadata artifact
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
+ with:
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ github.token }}
+ name: release-meta
+ path: artifacts/ci/nuget-online-convergence/release-meta/
+
- name: Resolve release metadata
id: meta
env:
- RELEASE_TAG: ${{ github.event.workflow_run.head_branch }}
RELEASE_RUN_URL: ${{ github.event.workflow_run.html_url }}
shell: bash
- run: bash tools/ci/release/resolve_workflow_run_release_meta.sh "${RELEASE_TAG}" "${RELEASE_RUN_URL}"
+ run: bash tools/ci/release/resolve_workflow_run_release_meta.sh artifacts/ci/nuget-online-convergence/release-meta/release-meta.json "${RELEASE_RUN_URL}"
- name: Verify NuGet online convergence (all endpoints required)
shell: bash
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3e493bae..fa127b94 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -87,6 +87,9 @@ jobs:
- name: Source-based tests
run: dotnet test tests/FileTypeDetectionLib.Tests/FileTypeDetectionLib.Tests.csproj -c Release --no-build -v minimal
+ - name: Fuzz tests (release blocker)
+ run: bash tools/ci/release/run_fuzz_release_gate.sh tests/FileTypeDetectionLib.Tests/FileTypeDetectionLib.Tests.csproj
+
- name: API contract tests
run: dotnet test tests/FileTypeDetectionLib.Tests/FileTypeDetectionLib.Tests.csproj -c Release --no-build --filter "Category=ApiContract" -v minimal
@@ -126,6 +129,17 @@ jobs:
path: artifacts/nuget/naming-snt-summary.json
if-no-files-found: error
+ - name: Prepare release metadata artifact
+ shell: bash
+ run: bash tools/ci/release/write_release_meta_artifact.sh "${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.version }}" "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" "artifacts/release-meta/release-meta.json"
+
+ - name: Upload release metadata artifact
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: release-meta
+ path: artifacts/release-meta/release-meta.json
+ if-no-files-found: error
+
publish-nuget:
runs-on: ubuntu-latest
needs: version-policy
@@ -233,3 +247,38 @@ jobs:
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || github.ref_name }}
run: bash tools/ci/release/gate4_verify_postpublish.sh "${RELEASE_TAG#v}" "${{ steps.nupkg.outputs.path }}"
+
+ enforce-online-convergence:
+ runs-on: ubuntu-latest
+ needs: publish-nuget
+ if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || github.event_name == 'workflow_dispatch'
+ permissions:
+ contents: read
+ steps:
+ - name: Checkout
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+
+ - name: Download release metadata artifact
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
+ with:
+ name: release-meta
+ path: artifacts/release-meta/
+
+ - name: Resolve release metadata
+ id: meta
+ env:
+ RELEASE_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ shell: bash
+ run: bash tools/ci/release/resolve_workflow_run_release_meta.sh artifacts/release-meta/release-meta.json "${RELEASE_RUN_URL}"
+
+ - name: Verify NuGet online convergence (all endpoints required)
+ shell: bash
+ run: bash tools/ci/release/verify_nuget_online_convergence.sh "${{ steps.meta.outputs.release_version }}" "${{ steps.meta.outputs.verify_log_path }}" "${{ steps.meta.outputs.package_id }}"
+
+ - name: Upload convergence artifact
+ if: always()
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: nuget-online-convergence-sync
+ path: artifacts/ci/nuget-online-convergence/
+ if-no-files-found: error
diff --git a/Directory.Build.props b/Directory.Build.props
index c2a21c5c..49dd32a3 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -26,6 +26,6 @@
Condition="Exists('$(MSBuildThisFileDirectory)tools/analyzers/BannedSymbols.txt')" />
- 6.1.13
+ 6.1.14
diff --git a/docs/0_de/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD b/docs/0_de/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
index aeb9c4c6..6b4a98f9 100644
--- a/docs/0_de/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
+++ b/docs/0_de/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
@@ -36,7 +36,7 @@ Diese Datei mappt die repo-/governance-basierten Scorecard-Alerts ohne konkrete
| `BranchProtectionID` | `main` nur via PR + required checks | Branch Protection/Ruleset aktiv, required checks konfiguriert | `gh api repos/tomtastisch/FileClassifier/branches/main/protection` | `required_status_checks` vorhanden | direkter Push auf `main` technisch blockiert |
| `CodeReviewID` | Mindestens 1 PR-Review vor Merge | PR-Review-Policy in Branch Protection/Ruleset | `gh api repos/tomtastisch/FileClassifier/branches/main/protection --jq '.required_pull_request_reviews'` | `required_approving_review_count >= 1` | Merge ohne Review nicht möglich |
| `MaintainedID` | Nachweis aktiver Wartung | Kontinuierliche Commits/Releases + aktive CI | `gh api repos/tomtastisch/FileClassifier/commits?per_page=20` und `gh api repos/tomtastisch/FileClassifier/actions/runs?per_page=20` | in den letzten 90 Tagen Commits vorhanden | in den letzten 30 Tagen erfolgreiche Workflow-Runs vorhanden |
-| `FuzzingID` | Fuzzing-Baseline vorhanden (mind. report-only) | Workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` und `gh run list --workflow fuzzing-baseline.yml --limit 10` | Workflow existiert und ist ausführbar | mindestens ein erfolgreicher Run in den letzten 30 Tagen |
+| `FuzzingID` | Fuzzing-Baseline vorhanden (zusätzlich als Release-Blocker aktiviert) | Workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` und `gh run list --workflow fuzzing-baseline.yml --limit 10` | Workflow existiert und ist ausführbar | mindestens ein erfolgreicher Run in den letzten 30 Tagen |
| `CIIBestPracticesID` | Prozess-/Security-Baseline dokumentiert und nachvollziehbar | Audit-/Governance-Docs + CI-Evidence + Security Policy | `ls docs/audit` + `bash tools/audit/verify-security-claims.sh` | Audit-Index vorhanden und verlinkt | Security-Claims-Evidence liefert `pass` für Blocker-Claims |
## Hinweis zur Scorecard-Interpretation
diff --git a/docs/0_de/ci/002_NUGET_TRUSTED_PUBLISHING.MD b/docs/0_de/ci/002_NUGET_TRUSTED_PUBLISHING.MD
index 92f9c7ed..e3b61125 100644
--- a/docs/0_de/ci/002_NUGET_TRUSTED_PUBLISHING.MD
+++ b/docs/0_de/ci/002_NUGET_TRUSTED_PUBLISHING.MD
@@ -33,8 +33,9 @@ Die aktive Trusted-Publishing-Policy ist an folgende Identität gebunden:
- Bei Incident-Diagnose kann das Fenster über die beiden Variablen erhöht werden, ohne Workflow-Jobnamen oder Required Contexts zu ändern.
## 5. Entkoppelte Online-Konvergenz (Async)
+- Zusätzlich läuft im Release-Workflow ein synchrones Blocker-Gate `enforce-online-convergence`, das nach Publish die Endpunkt-Konvergenz strikt erzwingt (auch für `workflow_dispatch`-Releases).
- Workflow: `.github/workflows/nuget-online-convergence.yml`
-- Trigger: `workflow_run` nach erfolgreichem `Release Publish`.
+- Trigger: `workflow_run` nach erfolgreichem `Release Publish` (unabhängig davon, ob via Tag-Push oder `workflow_dispatch` ausgelöst).
- Der Async-Workflow prüft weiterhin **alle** Endpunkte als harte Konvergenzbedingungen:
- `REQUIRE_SEARCH=1`
- `REQUIRE_REGISTRATION=1`
diff --git a/docs/0_de/versioning/002_HISTORY_VERSIONS.MD b/docs/0_de/versioning/002_HISTORY_VERSIONS.MD
index 9f012a11..4e487d65 100644
--- a/docs/0_de/versioning/002_HISTORY_VERSIONS.MD
+++ b/docs/0_de/versioning/002_HISTORY_VERSIONS.MD
@@ -12,7 +12,7 @@ Heuristik für die Rückwirkungs-Zuordnung:
- `docs|test|ci|chore|tooling|refactor|fix` => Patch
Aktueller Entwicklungsstand:
-- Aktuelle Entwicklungslinie enthält `6.x` (aktueller Arbeitsstand: `v6.1.13`; Details in `docs/versioning/003_CHANGELOG_RELEASES.MD`).
+- Aktuelle Entwicklungslinie enthält `6.x` (aktueller Arbeitsstand: `v6.1.14`; Details in `docs/versioning/003_CHANGELOG_RELEASES.MD`).
Hinweis:
- Die Spalte `Keyword` verwendet den technischen Klassifizierungswert aus der Historie.
@@ -20,6 +20,7 @@ Hinweis:
| Version | Kurzbeschreibung | Commit | Keyword |
|---|---|---|---|
+| `6.1.14` | 6.1.14 Pipeline-Konvergenz geschlossen: Release-Workflow erzwingt NuGet-Online-Konvergenz jetzt fail-closed auch für `workflow_dispatch`, Release-Metadaten werden artefaktbasiert deterministisch aufgelöst und Fuzzing-Blocker-/Governance-Evidence-Dokumentation entsprechend nachgezogen | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.13` | FC-0016 abgeschlossen: In-Code-XML-Dokumentation im gesamten CSCore sprachlich auf Deutsch vereinheitlicht (inkl. konsistenter ``- und `
`-Verwendungsstruktur) und Terminologie für Audit-/Betriebskontexte konsolidiert | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.12` | FC-0015 abgeschlossen: Bilinguale Doku-Rasterstruktur in `docs/0_de` und `docs/1_en` mit identischer Dateimenge und verpflichtenden Language-Switch-Headern kanonisch eingeführt; Sync-Tooling fail-closed gehärtet (Parity/Switch-Checks + Stale-Pruning) und PR-Scope-Allowlist für die neue Struktur erweitert | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.11` | FC-0014 abgeschlossen: Archive-/Path-Policy-Kernlogik (relative Archivpfad-Normalisierung und Root-Path-Policy) in CSCore zentralisiert (`ArchivePathPolicyUtility`) und via Bridge produktiv in `ArchiveEntryPathPolicy`/`DestinationPathGuard` genutzt; Telemetrie-/Contract-Abdeckung erweitert | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
diff --git a/docs/0_de/versioning/003_CHANGELOG_RELEASES.MD b/docs/0_de/versioning/003_CHANGELOG_RELEASES.MD
index e5a3ac41..c6ea75b9 100644
--- a/docs/0_de/versioning/003_CHANGELOG_RELEASES.MD
+++ b/docs/0_de/versioning/003_CHANGELOG_RELEASES.MD
@@ -7,6 +7,15 @@
Alle Änderungen werden hier technisch dokumentiert. Die Release-Version selbst ist
der Git-Tag `vX.Y.Z` (optional `-prerelease`) als SSOT.
+## [6.1.14]
+- Added:
+ - Release-Metadaten-Artefaktpfad eingeführt (`write_release_meta_artifact.sh`) und asynchrone Auflösung für `workflow_dispatch` ergänzt.
+- Changed:
+ - NuGet-Online-Konvergenz wird im Release-Workflow jetzt fail-closed auch für `workflow_dispatch` erzwungen.
+ - Fuzzing-Release-Gate wurde gehärtet: No-Test-Erkennung mit `rg`-Fallback auf `grep`.
+- Docs/CI/Tooling:
+ - Versionskonvergenz aktiv auf `6.1.14` gesetzt (`RepoVersion`, `Version`, `PackageVersion`, Versionshistorie DE/EN).
+
## [6.1.13]
- Added:
- Sprachlich vereinheitlichte XML-In-Code-Dokumentation für die verbleibenden CSCore-Bereiche (`Model`, `Mapping`, `Utilities`) auf Deutsch abgeschlossen.
diff --git a/docs/1_en/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD b/docs/1_en/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
index ee67f52a..df7afb65 100644
--- a/docs/1_en/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
+++ b/docs/1_en/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
@@ -35,7 +35,7 @@ Map repo/governance-based Scorecard alerts without a concrete file (`no file ass
| `BranchProtectionID` | `main` only via PR + required checks | branch protection/ruleset active, required checks configured | `gh api repos/tomtastisch/FileClassifier/branches/main/protection` | `required_status_checks` present | direct push to `main` is technically blocked |
| `CodeReviewID` | at least 1 PR review before merge | PR review policy in branch protection/ruleset | `gh api repos/tomtastisch/FileClassifier/branches/main/protection --jq '.required_pull_request_reviews'` | `required_approving_review_count >= 1` | merge without review not possible |
| `MaintainedID` | evidence of active maintenance | continuous commits/releases + active CI | `gh api repos/tomtastisch/FileClassifier/commits?per_page=20` and `gh api repos/tomtastisch/FileClassifier/actions/runs?per_page=20` | commits exist in last 90 days | successful workflow runs exist in last 30 days |
-| `FuzzingID` | fuzzing baseline exists (at least report-only) | workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` and `gh run list --workflow fuzzing-baseline.yml --limit 10` | workflow exists and is executable | at least one successful run in last 30 days |
+| `FuzzingID` | fuzzing baseline exists (additionally enforced as a release blocker) | workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` and `gh run list --workflow fuzzing-baseline.yml --limit 10` | workflow exists and is executable | at least one successful run in last 30 days |
| `CIIBestPracticesID` | process/security baseline documented and traceable | audit/governance docs + CI evidence + security policy | `ls docs/audit` + `bash tools/audit/verify-security-claims.sh` | audit index exists and links | security claims evidence returns `pass` for blocker claims |
## Note on Scorecard Interpretation
diff --git a/docs/1_en/ci/002_NUGET_TRUSTED_PUBLISHING.MD b/docs/1_en/ci/002_NUGET_TRUSTED_PUBLISHING.MD
index e0cdbd9c..a530f087 100644
--- a/docs/1_en/ci/002_NUGET_TRUSTED_PUBLISHING.MD
+++ b/docs/1_en/ci/002_NUGET_TRUSTED_PUBLISHING.MD
@@ -33,8 +33,9 @@ The active trusted-publishing policy is bound to the following identity:
- For incident diagnostics, the window can be increased via these variables without changing workflow job names or required contexts.
## 5. Decoupled Online Convergence (Async)
+- The release workflow now also contains a synchronous blocker gate `enforce-online-convergence` that strictly enforces endpoint convergence after publish (including `workflow_dispatch` releases).
- Workflow: `.github/workflows/nuget-online-convergence.yml`
-- Trigger: `workflow_run` after a successful `Release Publish`.
+- Trigger: `workflow_run` after a successful `Release Publish` (independent of whether the release was triggered by tag push or `workflow_dispatch`).
- The async workflow still checks **all** endpoints as hard convergence conditions:
- `REQUIRE_SEARCH=1`
- `REQUIRE_REGISTRATION=1`
diff --git a/docs/1_en/versioning/002_HISTORY_VERSIONS.MD b/docs/1_en/versioning/002_HISTORY_VERSIONS.MD
index 07d660a1..809e852f 100644
--- a/docs/1_en/versioning/002_HISTORY_VERSIONS.MD
+++ b/docs/1_en/versioning/002_HISTORY_VERSIONS.MD
@@ -12,13 +12,14 @@ Heuristics for retroactive classification:
- `docs|test|ci|chore|tooling|refactor|fix` => patch
Current state:
-- Current release line contains `6.x` (current working state: `v6.1.13`; details in `docs/versioning/103_CHANGELOG_RELEASES.MD`).
+- Current release line contains `6.x` (current working state: `v6.1.14`; details in `docs/versioning/103_CHANGELOG_RELEASES.MD`).
Note:
- The \"short description\" column follows the original commit/PR intent text for deterministic traceability and is not normalized to a single language.
| Version | Short description | Commit | Keyword |
|---|---|---|---|
+| `6.1.14` | 6.1.14 pipeline convergence closed: release workflow now enforces NuGet online convergence fail-closed for `workflow_dispatch` too, resolves release metadata deterministically via artifact, and aligns fuzzing-blocker/governance evidence documentation | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.13` | FC-0016 completed: unified in-code XML documentation language to German across CSCore (including consistent `` and `
` structure) and consolidated terminology for audit/operational contexts | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.12` | FC-0015 completed: introduced canonical bilingual docs grid under `docs/0_de` and `docs/1_en` with identical file sets and mandatory language-switch headers; hardened sync tooling fail-closed (parity/switch checks + stale file pruning) and expanded PR-scope allowlist for the new structure | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.11` | FC-0014 completed: centralized archive/path policy core logic (archive-relative-path normalization and root-path policy) in CSCore (`ArchivePathPolicyUtility`) and enabled productive bridge usage in `ArchiveEntryPathPolicy`/`DestinationPathGuard`; telemetry and package-contract coverage extended | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
diff --git a/docs/1_en/versioning/003_CHANGELOG_RELEASES.MD b/docs/1_en/versioning/003_CHANGELOG_RELEASES.MD
index b466e6d5..dc6957da 100644
--- a/docs/1_en/versioning/003_CHANGELOG_RELEASES.MD
+++ b/docs/1_en/versioning/003_CHANGELOG_RELEASES.MD
@@ -6,6 +6,15 @@
All changes are documented here in technical terms. The release version itself is the Git tag `vX.Y.Z` (optional `-prerelease`) as SSOT.
+## [6.1.14]
+- Added:
+ - Introduced release metadata artifact path (`write_release_meta_artifact.sh`) and asynchronous resolution for `workflow_dispatch`.
+- Changed:
+ - NuGet online convergence is now enforced fail-closed in the release workflow for `workflow_dispatch` as well.
+ - Hardened fuzzing release gate: no-test detection now supports `rg` with `grep` fallback.
+- Docs/CI/Tooling:
+ - Version convergence set to `6.1.14` (`RepoVersion`, `Version`, `PackageVersion`, DE/EN version history).
+
## [6.1.13]
- Added:
- Completed German language unification for remaining CSCore XML in-code documentation areas (`Model`, `Mapping`, `Utilities`).
diff --git a/docs/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD b/docs/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
index 3c087985..2793ab20 100644
--- a/docs/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
+++ b/docs/audit/013_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
@@ -36,7 +36,7 @@ Diese Datei mappt die repo-/governance-basierten Scorecard-Alerts ohne konkrete
| `BranchProtectionID` | `main` nur via PR + required checks | Branch Protection/Ruleset aktiv, required checks konfiguriert | `gh api repos/tomtastisch/FileClassifier/branches/main/protection` | `required_status_checks` vorhanden | direkter Push auf `main` technisch blockiert |
| `CodeReviewID` | Mindestens 1 PR-Review vor Merge | PR-Review-Policy in Branch Protection/Ruleset | `gh api repos/tomtastisch/FileClassifier/branches/main/protection --jq '.required_pull_request_reviews'` | `required_approving_review_count >= 1` | Merge ohne Review nicht möglich |
| `MaintainedID` | Nachweis aktiver Wartung | Kontinuierliche Commits/Releases + aktive CI | `gh api repos/tomtastisch/FileClassifier/commits?per_page=20` und `gh api repos/tomtastisch/FileClassifier/actions/runs?per_page=20` | in den letzten 90 Tagen Commits vorhanden | in den letzten 30 Tagen erfolgreiche Workflow-Runs vorhanden |
-| `FuzzingID` | Fuzzing-Baseline vorhanden (mind. report-only) | Workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` und `gh run list --workflow fuzzing-baseline.yml --limit 10` | Workflow existiert und ist ausführbar | mindestens ein erfolgreicher Run in den letzten 30 Tagen |
+| `FuzzingID` | Fuzzing-Baseline vorhanden (zusätzlich als Release-Blocker aktiviert) | Workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` und `gh run list --workflow fuzzing-baseline.yml --limit 10` | Workflow existiert und ist ausführbar | mindestens ein erfolgreicher Run in den letzten 30 Tagen |
| `CIIBestPracticesID` | Prozess-/Security-Baseline dokumentiert und nachvollziehbar | Audit-/Governance-Docs + CI-Evidence + Security Policy | `ls docs/audit` + `bash tools/audit/verify-security-claims.sh` | Audit-Index vorhanden und verlinkt | Security-Claims-Evidence liefert `pass` für Blocker-Claims |
## Hinweis zur Scorecard-Interpretation
diff --git a/docs/audit/113_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD b/docs/audit/113_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
index ef94a9b3..d522f4fb 100644
--- a/docs/audit/113_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
+++ b/docs/audit/113_SCORECARD_GOVERNANCE_ALERT_MAPPING.MD
@@ -35,7 +35,7 @@ Map repo/governance-based Scorecard alerts without a concrete file (`no file ass
| `BranchProtectionID` | `main` only via PR + required checks | branch protection/ruleset active, required checks configured | `gh api repos/tomtastisch/FileClassifier/branches/main/protection` | `required_status_checks` present | direct push to `main` is technically blocked |
| `CodeReviewID` | at least 1 PR review before merge | PR review policy in branch protection/ruleset | `gh api repos/tomtastisch/FileClassifier/branches/main/protection --jq '.required_pull_request_reviews'` | `required_approving_review_count >= 1` | merge without review not possible |
| `MaintainedID` | evidence of active maintenance | continuous commits/releases + active CI | `gh api repos/tomtastisch/FileClassifier/commits?per_page=20` and `gh api repos/tomtastisch/FileClassifier/actions/runs?per_page=20` | commits exist in last 90 days | successful workflow runs exist in last 30 days |
-| `FuzzingID` | fuzzing baseline exists (at least report-only) | workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` and `gh run list --workflow fuzzing-baseline.yml --limit 10` | workflow exists and is executable | at least one successful run in last 30 days |
+| `FuzzingID` | fuzzing baseline exists (additionally enforced as a release blocker) | workflow `.github/workflows/fuzzing-baseline.yml` | `gh workflow view fuzzing-baseline.yml --yaml` and `gh run list --workflow fuzzing-baseline.yml --limit 10` | workflow exists and is executable | at least one successful run in last 30 days |
| `CIIBestPracticesID` | process/security baseline documented and traceable | audit/governance docs + CI evidence + security policy | `ls docs/audit` + `bash tools/audit/verify-security-claims.sh` | audit index exists and links | security claims evidence returns `pass` for blocker claims |
## Note on Scorecard Interpretation
diff --git a/docs/ci/002_NUGET_TRUSTED_PUBLISHING.MD b/docs/ci/002_NUGET_TRUSTED_PUBLISHING.MD
index f04a8490..2f986598 100644
--- a/docs/ci/002_NUGET_TRUSTED_PUBLISHING.MD
+++ b/docs/ci/002_NUGET_TRUSTED_PUBLISHING.MD
@@ -33,8 +33,9 @@ Die aktive Trusted-Publishing-Policy ist an folgende Identität gebunden:
- Bei Incident-Diagnose kann das Fenster über die beiden Variablen erhöht werden, ohne Workflow-Jobnamen oder Required Contexts zu ändern.
## 5. Entkoppelte Online-Konvergenz (Async)
+- Zusätzlich läuft im Release-Workflow ein synchrones Blocker-Gate `enforce-online-convergence`, das nach Publish die Endpunkt-Konvergenz strikt erzwingt (auch für `workflow_dispatch`-Releases).
- Workflow: `.github/workflows/nuget-online-convergence.yml`
-- Trigger: `workflow_run` nach erfolgreichem `Release Publish`.
+- Trigger: `workflow_run` nach erfolgreichem `Release Publish` (unabhängig davon, ob via Tag-Push oder `workflow_dispatch` ausgelöst).
- Der Async-Workflow prüft weiterhin **alle** Endpunkte als harte Konvergenzbedingungen:
- `REQUIRE_SEARCH=1`
- `REQUIRE_REGISTRATION=1`
diff --git a/docs/ci/102_NUGET_TRUSTED_PUBLISHING.MD b/docs/ci/102_NUGET_TRUSTED_PUBLISHING.MD
index 7eaf96f6..1934639f 100644
--- a/docs/ci/102_NUGET_TRUSTED_PUBLISHING.MD
+++ b/docs/ci/102_NUGET_TRUSTED_PUBLISHING.MD
@@ -33,8 +33,9 @@ The active trusted-publishing policy is bound to the following identity:
- For incident diagnostics, the window can be increased via these variables without changing workflow job names or required contexts.
## 5. Decoupled Online Convergence (Async)
+- The release workflow now also contains a synchronous blocker gate `enforce-online-convergence` that strictly enforces endpoint convergence after publish (including `workflow_dispatch` releases).
- Workflow: `.github/workflows/nuget-online-convergence.yml`
-- Trigger: `workflow_run` after a successful `Release Publish`.
+- Trigger: `workflow_run` after a successful `Release Publish` (independent of whether the release was triggered by tag push or `workflow_dispatch`).
- The async workflow still checks **all** endpoints as hard convergence conditions:
- `REQUIRE_SEARCH=1`
- `REQUIRE_REGISTRATION=1`
diff --git a/docs/versioning/002_HISTORY_VERSIONS.MD b/docs/versioning/002_HISTORY_VERSIONS.MD
index 47c4d2b0..ffcaf486 100644
--- a/docs/versioning/002_HISTORY_VERSIONS.MD
+++ b/docs/versioning/002_HISTORY_VERSIONS.MD
@@ -12,7 +12,7 @@ Heuristik für die Rückwirkungs-Zuordnung:
- `docs|test|ci|chore|tooling|refactor|fix` => Patch
Aktueller Entwicklungsstand:
-- Aktuelle Entwicklungslinie enthält `6.x` (aktueller Arbeitsstand: `v6.1.13`; Details in `docs/versioning/003_CHANGELOG_RELEASES.MD`).
+- Aktuelle Entwicklungslinie enthält `6.x` (aktueller Arbeitsstand: `v6.1.14`; Details in `docs/versioning/003_CHANGELOG_RELEASES.MD`).
Hinweis:
- Die Spalte `Keyword` verwendet den technischen Klassifizierungswert aus der Historie.
@@ -20,6 +20,7 @@ Hinweis:
| Version | Kurzbeschreibung | Commit | Keyword |
|---|---|---|---|
+| `6.1.14` | 6.1.14 Pipeline-Konvergenz geschlossen: Release-Workflow erzwingt NuGet-Online-Konvergenz jetzt fail-closed auch für `workflow_dispatch`, Release-Metadaten werden artefaktbasiert deterministisch aufgelöst und Fuzzing-Blocker-/Governance-Evidence-Dokumentation entsprechend nachgezogen | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.13` | FC-0016 abgeschlossen: In-Code-XML-Dokumentation im gesamten CSCore sprachlich auf Deutsch vereinheitlicht (inkl. konsistenter ``- und `
`-Verwendungsstruktur) und Terminologie für Audit-/Betriebskontexte konsolidiert | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.12` | FC-0015 abgeschlossen: Bilinguale Doku-Rasterstruktur in `docs/0_de` und `docs/1_en` mit identischer Dateimenge und verpflichtenden Language-Switch-Headern kanonisch eingeführt; Sync-Tooling fail-closed gehärtet (Parity/Switch-Checks + Stale-Pruning) und PR-Scope-Allowlist für die neue Struktur erweitert | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.11` | FC-0014 abgeschlossen: Archive-/Path-Policy-Kernlogik (relative Archivpfad-Normalisierung und Root-Path-Policy) in CSCore zentralisiert (`ArchivePathPolicyUtility`) und via Bridge produktiv in `ArchiveEntryPathPolicy`/`DestinationPathGuard` genutzt; Telemetrie-/Contract-Abdeckung erweitert | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
diff --git a/docs/versioning/003_CHANGELOG_RELEASES.MD b/docs/versioning/003_CHANGELOG_RELEASES.MD
index b22f6eb3..a39cc9f7 100644
--- a/docs/versioning/003_CHANGELOG_RELEASES.MD
+++ b/docs/versioning/003_CHANGELOG_RELEASES.MD
@@ -7,6 +7,15 @@
Alle Änderungen werden hier technisch dokumentiert. Die Release-Version selbst ist
der Git-Tag `vX.Y.Z` (optional `-prerelease`) als SSOT.
+## [6.1.14]
+- Added:
+ - Release-Metadaten-Artefaktpfad eingeführt (`write_release_meta_artifact.sh`) und asynchrone Auflösung für `workflow_dispatch` ergänzt.
+- Changed:
+ - NuGet-Online-Konvergenz wird im Release-Workflow jetzt fail-closed auch für `workflow_dispatch` erzwungen.
+ - Fuzzing-Release-Gate wurde gehärtet: No-Test-Erkennung mit `rg`-Fallback auf `grep`.
+- Docs/CI/Tooling:
+ - Versionskonvergenz aktiv auf `6.1.14` gesetzt (`RepoVersion`, `Version`, `PackageVersion`, Versionshistorie DE/EN).
+
## [6.1.13]
- Added:
- Sprachlich vereinheitlichte XML-In-Code-Dokumentation für die verbleibenden CSCore-Bereiche (`Model`, `Mapping`, `Utilities`) auf Deutsch abgeschlossen.
diff --git a/docs/versioning/102_HISTORY_VERSIONS.MD b/docs/versioning/102_HISTORY_VERSIONS.MD
index 62c324ed..dd376704 100644
--- a/docs/versioning/102_HISTORY_VERSIONS.MD
+++ b/docs/versioning/102_HISTORY_VERSIONS.MD
@@ -12,13 +12,14 @@ Heuristics for retroactive classification:
- `docs|test|ci|chore|tooling|refactor|fix` => patch
Current state:
-- Current release line contains `6.x` (current working state: `v6.1.13`; details in `docs/versioning/103_CHANGELOG_RELEASES.MD`).
+- Current release line contains `6.x` (current working state: `v6.1.14`; details in `docs/versioning/103_CHANGELOG_RELEASES.MD`).
Note:
- The \"short description\" column follows the original commit/PR intent text for deterministic traceability and is not normalized to a single language.
| Version | Short description | Commit | Keyword |
|---|---|---|---|
+| `6.1.14` | 6.1.14 pipeline convergence closed: release workflow now enforces NuGet online convergence fail-closed for `workflow_dispatch` too, resolves release metadata deterministically via artifact, and aligns fuzzing-blocker/governance evidence documentation | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.13` | FC-0016 completed: unified in-code XML documentation language to German across CSCore (including consistent `` and `
` structure) and consolidated terminology for audit/operational contexts | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.12` | FC-0015 completed: introduced canonical bilingual docs grid under `docs/0_de` and `docs/1_en` with identical file sets and mandatory language-switch headers; hardened sync tooling fail-closed (parity/switch checks + stale file pruning) and expanded PR-scope allowlist for the new structure | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
| `6.1.11` | FC-0014 completed: centralized archive/path policy core logic (archive-relative-path normalization and root-path policy) in CSCore (`ArchivePathPolicyUtility`) and enabled productive bridge usage in `ArchiveEntryPathPolicy`/`DestinationPathGuard`; telemetry and package-contract coverage extended | [unreleased](https://github.com/tomtastisch/FileClassifier/compare/main...HEAD) | patch |
diff --git a/docs/versioning/103_CHANGELOG_RELEASES.MD b/docs/versioning/103_CHANGELOG_RELEASES.MD
index 0410f216..5e5af83a 100644
--- a/docs/versioning/103_CHANGELOG_RELEASES.MD
+++ b/docs/versioning/103_CHANGELOG_RELEASES.MD
@@ -6,6 +6,15 @@
All changes are documented here in technical terms. The release version itself is the Git tag `vX.Y.Z` (optional `-prerelease`) as SSOT.
+## [6.1.14]
+- Added:
+ - Introduced release metadata artifact path (`write_release_meta_artifact.sh`) and asynchronous resolution for `workflow_dispatch`.
+- Changed:
+ - NuGet online convergence is now enforced fail-closed in the release workflow for `workflow_dispatch` as well.
+ - Hardened fuzzing release gate: no-test detection now supports `rg` with `grep` fallback.
+- Docs/CI/Tooling:
+ - Version convergence set to `6.1.14` (`RepoVersion`, `Version`, `PackageVersion`, DE/EN version history).
+
## [6.1.13]
- Added:
- Completed German language unification for remaining CSCore XML in-code documentation areas (`Model`, `Mapping`, `Utilities`).
diff --git a/src/FileTypeDetection/FileTypeDetectionLib.vbproj b/src/FileTypeDetection/FileTypeDetectionLib.vbproj
index cac25706..7843b077 100644
--- a/src/FileTypeDetection/FileTypeDetectionLib.vbproj
+++ b/src/FileTypeDetection/FileTypeDetectionLib.vbproj
@@ -7,8 +7,8 @@
true
false
Tomtastisch.FileClassifier
- 6.1.13
- 6.1.13
+ 6.1.14
+ 6.1.14
tomtastisch
Deterministic file type and MIME detection with fail-closed archive safety checks, secure extraction primitives, and reproducible hashing evidence for .NET.
filetype;mime;detection;magic-bytes;sniffing;archive;zip;tar;7z;rar;zipslip;security;hashing;sha256;deterministic;dotnet;netstandard2.0;net8;net10
diff --git a/tests/FileTypeDetectionLib.Tests/Unit/Fuzzing/FsCheckSmokeTests.cs b/tests/FileTypeDetectionLib.Tests/Unit/Fuzzing/FsCheckSmokeTests.cs
index d338117b..37e7d43e 100644
--- a/tests/FileTypeDetectionLib.Tests/Unit/Fuzzing/FsCheckSmokeTests.cs
+++ b/tests/FileTypeDetectionLib.Tests/Unit/Fuzzing/FsCheckSmokeTests.cs
@@ -3,6 +3,7 @@ namespace FileTypeDetectionLib.Tests.Unit.Fuzzing;
public sealed class FsCheckSmokeTests
{
[Fact]
+ [Trait("Category", "Fuzz")]
public void FuzzingSmoke_GeneratorProducesSamples()
{
// FsCheck-based fuzzing signal: randomized sample generation.
diff --git a/tools/ci/policies/data/pr_scope_allowlist.txt b/tools/ci/policies/data/pr_scope_allowlist.txt
index 8b485b42..36d95990 100644
--- a/tools/ci/policies/data/pr_scope_allowlist.txt
+++ b/tools/ci/policies/data/pr_scope_allowlist.txt
@@ -18,6 +18,7 @@ tools/ci/**
tools/tests/**
.github/workflows/codeql.yml
.github/workflows/release.yml
+.github/workflows/nuget-online-convergence.yml
# active documentation scope for the CSCore migration chain
@@ -34,6 +35,7 @@ docs/lang_switch_report.json
docs/001_INDEX_CORE.MD
docs/ci/001_PIPELINE_CI.MD
docs/ci/002_NUGET_TRUSTED_PUBLISHING.MD
+docs/ci/102_NUGET_TRUSTED_PUBLISHING.MD
docs/contracts/001_CONTRACT_HASHING.MD
docs/guides/003_GUIDE_PORTABLE.MD
docs/guides/004_GUIDE_MIGRATE_LEGACY_NUGET.MD
diff --git a/tools/ci/release/resolve_workflow_run_release_meta.sh b/tools/ci/release/resolve_workflow_run_release_meta.sh
index 1b594133..a368989c 100755
--- a/tools/ci/release/resolve_workflow_run_release_meta.sh
+++ b/tools/ci/release/resolve_workflow_run_release_meta.sh
@@ -1,28 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail
-release_tag="${1:-}"
+meta_json_path="${1:-}"
release_run_url="${2:-}"
+if [[ -z "${meta_json_path}" ]]; then
+ echo "Missing release metadata path argument" >&2
+ exit 1
+fi
+if [[ ! -f "${meta_json_path}" ]]; then
+ echo "Release metadata file not found: '${meta_json_path}'" >&2
+ exit 1
+fi
+if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
+ echo "GITHUB_OUTPUT is not set" >&2
+ exit 1
+fi
+
+release_tag="$(jq -r '.release_tag // empty' "${meta_json_path}")"
+release_version="$(jq -r '.release_version // empty' "${meta_json_path}")"
+package_id_from_meta="$(jq -r '.package_id // empty' "${meta_json_path}")"
+release_run_url_from_meta="$(jq -r '.release_run_url // empty' "${meta_json_path}")"
+
if [[ -z "${release_tag}" ]]; then
- echo "Missing release tag argument" >&2
+ echo "release_tag is missing in '${meta_json_path}'" >&2
exit 1
fi
if [[ ! "${release_tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "Unexpected release tag: '${release_tag}'" >&2
exit 1
fi
-if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
- echo "GITHUB_OUTPUT is not set" >&2
+if [[ -z "${release_version}" ]]; then
+ release_version="${release_tag#v}"
+fi
+if [[ "${release_version}" != "${release_tag#v}" ]]; then
+ echo "Release metadata mismatch: tag='${release_tag}' version='${release_version}'" >&2
exit 1
fi
-release_version="${release_tag#v}"
verify_log_path="artifacts/ci/nuget-online-convergence/verify.log"
-package_id="$(python3 tools/ci/bin/read_json_field.py --file tools/ci/policies/data/naming.json --field package_id)"
+package_id="${package_id_from_meta}"
+if [[ -z "${package_id}" ]]; then
+ package_id="$(python3 tools/ci/bin/read_json_field.py --file tools/ci/policies/data/naming.json --field package_id)"
+fi
if [[ -z "${package_id}" ]]; then
package_id="Tomtastisch.FileClassifier"
fi
+if [[ -z "${release_run_url}" ]]; then
+ release_run_url="${release_run_url_from_meta}"
+fi
+if [[ -z "${release_run_url}" ]]; then
+ echo "release_run_url is missing: provide as second argument or in '${meta_json_path}' (.release_run_url)" >&2
+ exit 1
+fi
mkdir -p artifacts/ci/nuget-online-convergence
{
diff --git a/tools/ci/release/run_fuzz_release_gate.sh b/tools/ci/release/run_fuzz_release_gate.sh
new file mode 100755
index 00000000..95d9515e
--- /dev/null
+++ b/tools/ci/release/run_fuzz_release_gate.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+project_path="${1:?project path required}"
+
+tmp_file="$(mktemp)"
+trap 'rm -f "${tmp_file}"' EXIT
+
+dotnet test "${project_path}" -c Release --no-build --filter "Category=Fuzz" -v minimal | tee "${tmp_file}"
+
+no_tests_pattern="Total tests:[[:space:]]*0|No test matches|Kein Test entspricht"
+if command -v rg >/dev/null 2>&1; then
+ if rg -q "${no_tests_pattern}" "${tmp_file}"; then
+ echo "Fuzz tests release gate failed: no tests were executed for filter Category=Fuzz." >&2
+ exit 1
+ fi
+else
+ if grep -Eq "${no_tests_pattern}" "${tmp_file}"; then
+ echo "Fuzz tests release gate failed: no tests were executed for filter Category=Fuzz." >&2
+ exit 1
+ fi
+fi
diff --git a/tools/ci/release/write_release_meta_artifact.sh b/tools/ci/release/write_release_meta_artifact.sh
new file mode 100755
index 00000000..69783feb
--- /dev/null
+++ b/tools/ci/release/write_release_meta_artifact.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+release_tag="${1:?release_tag required}"
+release_version="${2:?release_version required}"
+release_run_url="${3:?release_run_url required}"
+out_file="${4:-artifacts/release-meta/release-meta.json}"
+
+package_id="$(python3 tools/ci/bin/read_json_field.py --file tools/ci/policies/data/naming.json --field package_id)"
+if [[ -z "${package_id}" ]]; then
+ package_id="Tomtastisch.FileClassifier"
+fi
+
+mkdir -p "$(dirname -- "${out_file}")"
+jq -n \
+ --arg release_tag "${release_tag}" \
+ --arg release_version "${release_version}" \
+ --arg package_id "${package_id}" \
+ --arg release_run_url "${release_run_url}" \
+ '{
+ release_tag: $release_tag,
+ release_version: $release_version,
+ package_id: $package_id,
+ release_run_url: $release_run_url
+ }' > "${out_file}"