Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
release-inputs/** text eol=lf
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ jobs:
}

$version = $tagMatch.Groups['version'].Value
$majorVersion = [int]($version.Split('.')[0])
$isPrerelease = $majorVersion -eq 0 -or $version.Contains('-')
"version=$version" >> $env:GITHUB_OUTPUT
"prerelease=$($version.Contains('-').ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
"prerelease=$($isPrerelease.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT

- name: Require the tagged commit on the default branch
shell: pwsh
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Approved releases provide two self-contained downloads:
- `WinBulkTranscript-<version>-win-x64.zip` for ordinary Intel/AMD Windows 11 PCs.
- `WinBulkTranscript-<version>-win-arm64.zip` for Windows on Arm PCs.

Versions below 1.0 are preview releases and are marked as prereleases on GitHub. Their release notes and embedded matrices identify validation gaps accepted for preview; review those limitations before use. Version 1.0 and later uses the supported-release policy and requires every applicable architecture gate to pass.

Download the matching archive and `SHA256SUMS.txt` from [GitHub Releases](https://github.com/JamiesonLabUTSW/win-bulk-transcript/releases), verify it, extract the **entire** ZIP to a normal folder, and launch `WinBulkTranscript.exe` from that extracted folder. Do not run it inside the ZIP or copy the EXE alone. The bundle includes .NET and Windows App SDK dependencies; the speech model is downloaded separately on first use.

```powershell
Expand Down
18 changes: 9 additions & 9 deletions docs/release/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Release evidence

This directory records the evidence needed to turn a successful build into a supported release. It intentionally contains templates and instructions rather than claims that unrun architecture and clean-machine gates have passed. A completed tag release stores its reviewed inputs in a committed [`release-inputs/v<version>` dossier](../../release-inputs/README.md).
This directory records the evidence used to turn a successful build into a release. Version-zero releases use the preview policy; version 1 and later use the supported-release policy. A tag release stores its inputs in a committed [`release-inputs/v<version>` dossier](../../release-inputs/README.md).

## Distribution format

Expand All @@ -14,10 +14,10 @@ Complete a release test matrix before publishing an architecture. The publisher

- The matrix release version, model variant, and release source match the command and verified repository revision/tag.
- The entire source working tree is clean, every release-owned source path is tracked at `HEAD`, and `HEAD` remains unchanged through restore and publish.
- Every row for the requested architecture says exactly `Passed` and has retained evidence or notes.
- The version-derived release policy is recorded in the matrix. Preview 0.x releases permit exactly `Passed`, `Accepted risk`, or `Not applicable`; non-passing rows require concrete rationale, and `Accepted risk` requires `Approver: <name>; Date: <YYYY-MM-DD>; Decision: <explicit decision>`. Supported 1.x and later releases require exactly `Passed` for every applicable architecture row.
- The matrix has a receipt row for the exact output ZIP name. Its checksum column should say that the publisher writes the matching sidecar; a ZIP cannot contain its own final hash.
- Complete architecture-specific [release notes](release-notes-template.md), [model provenance](model-provenance-template.json), and [runtime/framework notices](runtime-framework-notices-template.txt). Their matrix headers must be the exact input filename plus SHA-256, not a generic template or an un-hashed path.
- The runtime/framework notice input must name every framework from the final `.runtimeconfig.json` and every non-lock framework package from the final `.deps.json`. It still requires legal review for completeness.
- Complete the [release notes](release-notes-template.md), [model provenance](model-provenance-template.json), and architecture-specific [runtime/framework information](runtime-framework-notices-template.txt). Their matrix headers must be the exact input filename plus SHA-256, not a generic template or an un-hashed path.
- The runtime/framework input must name every framework from the final `.runtimeconfig.json` and every non-lock framework package from the final `.deps.json`. A preview records the current terms sources; a supported 1.x release requires the stricter reviewed notice set.
- Record the literal single-file experiment result in the matrix header or linked evidence. It is future information only; do not replace the version 1 folder/ZIP artifact with that experiment.

For a tag release, set each matrix's Release source header to the exact tag, such as `v1.2.3`. The publisher verifies that the tag resolves to the checked-out revision. For an explicitly local/manual package without `-ReleaseSourceRef`, use the full output of `git rev-parse HEAD`.
Expand Down Expand Up @@ -48,13 +48,13 @@ $artifactsRoot = 'C:\release-artifacts'

The [`Release` workflow](../../.github/workflows/release.yml) runs for canonical SemVer tags such as `v1.2.3` and `v1.2.3-rc.1`. Before creating the tag:

1. Complete all x64 and ARM64 clean-machine/UAT gates and institutional release approval.
1. For a 0.x preview, record and approve any accepted validation limitations in both matrices. For a supported 1.x release, complete every x64 and ARM64 gate.
2. Copy the templates into `release-inputs/v<version>/` using the documented dossier layout. Use one shared `release-notes.md` that names both ZIPs and architecture-specific matrices/runtime notices.
3. Finalize the model license, provenance, shared release notes, and both runtime/framework notices. Hash those completed files, bind the hashes in each matrix, set `Release source` to the future tag, and ensure every applicable architecture row says exactly `Passed`.
3. Complete the model information, provenance, shared release notes, and both runtime/framework information files. Hash those files, bind the hashes in each matrix, set `Release source` to the future tag, and satisfy the version-derived matrix policy. Preview inputs are disclosure records; supported 1.x inputs follow the stricter reviewed-input policy.
4. Merge the dossier and all release changes to the repository's default branch. The tag workflow refuses a commit that is not reachable from that branch.
5. Create the tag on that exact commit and push it: `git tag v1.2.3; git push origin v1.2.3`. Do not move or reuse a release tag.

The workflow independently builds x64 and ARM64 from the tagged commit, runs the deterministic build/tests and hardening checks, invokes the same publisher used locally, and transfers only each ZIP, checksum sidecar, and release record. Its publication job revalidates the assembled six-file set and embedded metadata/hashes, writes `SHA256SUMS.txt`, generates GitHub/Sigstore build-provenance attestations, and creates a **draft** GitHub Release from the reviewed shared notes. Configure the repository's `release` environment with required reviewers so the write-capable job cannot run without approval. Also [enable immutable releases](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/establish-provenance-and-integrity/prevent-release-changes) and protect `v*` tags from unauthorized creation, update, and deletion with a [tag ruleset](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets). A human must inspect the draft assets/notes and publish it in GitHub; the workflow deliberately does not make a public release automatically. When release immutability is enabled, publishing the completed draft locks its tag and assets.
The workflow independently builds x64 and ARM64 from the tagged commit, runs the deterministic build/tests and hardening checks, invokes the same publisher used locally, and transfers only each ZIP, checksum sidecar, and release record. Every 0.x version and every version with a SemVer prerelease suffix is marked as a GitHub prerelease. Its publication job revalidates the assembled six-file set and embedded metadata/hashes, writes `SHA256SUMS.txt`, generates GitHub/Sigstore build-provenance attestations, and creates a **draft** GitHub Release from the reviewed shared notes. Configure the repository's `release` environment with required reviewers where the repository plan supports them so the write-capable job cannot run without approval. Also [enable immutable releases](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/establish-provenance-and-integrity/prevent-release-changes) and protect `v*` tags from unauthorized creation, update, and deletion with a [tag ruleset](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets). A human must inspect the draft assets/notes and publish it in GitHub; the workflow deliberately does not make a public release automatically. When release immutability is enabled, publishing the completed draft locks its tag and assets.

The release job needs only the scoped `GITHUB_TOKEN`; no signing secret is required for the current unsigned ZIPs. GitHub attestations establish which workflow and commit produced a ZIP, but they are not Authenticode signatures and do not prevent SmartScreen warnings. Consumers can verify an asset with `gh attestation verify <zip> --repo JamiesonLabUTSW/win-bulk-transcript` in addition to checking `SHA256SUMS.txt`.

Expand Down Expand Up @@ -97,11 +97,11 @@ The publisher refuses all existing final paths and atomically reserves an artifa

If publishing fails before finalization, the reservation is released and any staging output is retained for inspection. If an interruption occurs during finalization, the `.publish-reservation` file remains and some final paths may exist. Do not rerun the same version/architecture or delete that reservation until the partial output has been inspected and deliberately recovered or discarded.

## Notices and legal review
## Notices

The publisher invokes `New-ThirdPartyNotices.ps1` only after it has generated a staged `.deps.json`-bound `PUBLISH-PAYLOAD.json`. The generator refuses to overwrite an existing notice, validates that each actual lock-backed `.deps.json` package matches the App lock content hash and restored NuGet `.nupkg.metadata` content hash, and embeds available package license files plus the supplied model license/provenance. It separately lists declared runtime frameworks and non-lock runtime package libraries, then embeds the reviewed runtime/framework notice input.

This is deliberately not a claim that the package lock alone covers a self-contained release. Automation can prove filename/hash/manifest relationships and that the supplied runtime text names the discovered framework/package records. It cannot decide whether copied framework, native, or runtime files are legally complete, so legal review remains a release gate. SPDX expressions and license URLs remain references that must be reviewed against the actual payload. The checked-in [template](THIRD-PARTY-NOTICES.md) is not a shipping notice.
The package lock alone does not describe every file in a self-contained release. Automation proves filename/hash/manifest relationships and checks that the supplied runtime text names the discovered framework/package records. For a 0.x preview, the generated notice records those current disclosures. The supported 1.x policy additionally requires reviewed legal completeness. The checked-in [template](THIRD-PARTY-NOTICES.md) is not a shipping notice.

## Unsigned artifacts

Expand Down
6 changes: 3 additions & 3 deletions docs/release/THIRD-PARTY-NOTICES.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Third-party and model notices

This file is a release gate and template, not a shipping legal notice. The publisher runs [`scripts/New-ThirdPartyNotices.ps1`](../../scripts/New-ThirdPartyNotices.ps1) only against the staged final `.deps.json`, matching `PUBLISH-PAYLOAD.json`, exact App lock, selected model provenance/license, and reviewed runtime/framework notice input. Retain the generated result with the matching ZIP.
This file documents the notice generator; it is not a shipping notice. The publisher runs [`scripts/New-ThirdPartyNotices.ps1`](../../scripts/New-ThirdPartyNotices.ps1) only against the staged final `.deps.json`, matching `PUBLISH-PAYLOAD.json`, exact App lock, selected model provenance/information, and runtime/framework input. Retain the generated result with the matching ZIP.

The generator intentionally includes only package libraries that the staged `.deps.json` actually declares. It verifies lock-backed package versions/content hashes against the lock and restored NuGet metadata, embeds package-supplied license files and `NOTICE`/`ThirdPartyNotices` files, and supplies reviewed canonical MIT text when an MIT-expression package has no embedded license file. Any other expression without an embedded license file blocks release until its canonical text is reviewed and supported. It separately records declared runtime frameworks and any non-lock framework packages. A package lock is not a complete inventory of a self-contained runtime payload.

The runtime/framework notice input must name the discovered framework/package versions and carry reviewed legal attribution for the copied runtime/native payload. Automation cannot establish whether that legal text is complete, so legal review remains mandatory before distribution.
The runtime/framework input must name the discovered framework/package versions. A 0.x preview records the current terms and attribution sources. The supported 1.x policy additionally requires reviewed legal completeness for the copied runtime/native payload.

Expected direct runtime dependencies from the current candidate lock:

- Microsoft Windows App SDK `2.3.1`.
- Microsoft AI Foundry Local `1.2.4`, including its transitive dependencies.

The initial candidate model is `nemotron-speech-streaming-en-0.6b-generic-cpu:3`. Its provenance record must bind the exact artifact identifier, source URI, source license file name, and license SHA-256. Its NVIDIA model license, all applicable package license texts, and package attribution notices must accompany the version that passed the architecture test matrix. Do not infer that the application code license grants redistribution rights for the model.
The initial candidate model is `nemotron-speech-streaming-en-0.6b-generic-cpu:3`. Its provenance record binds the artifact identifier, source URI, model-information file name, and SHA-256. The model is downloaded separately rather than included in the application ZIP. Do not infer that the application code license applies to the external model.
4 changes: 3 additions & 1 deletion docs/release/release-notes-template.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# WinBulkTranscript release notes — `<version>`

Release date: `<YYYY-MM-DD>`
Release policy: `<preview for 0.x | supported for 1.x and later>`
Architecture artifacts: `WinBulkTranscript-<version>-win-x64.zip`, `WinBulkTranscript-<version>-win-arm64.zip`
Release evidence: `<links or retained paths to the completed architecture matrices and release records>`

Expand All @@ -20,6 +21,7 @@ The application downloads its configured local speech model on first use. A late

- Exact configured model variant: `<model variant>`
- Release source: `<v-prefixed release tag>`
- Release policy: `<preview | supported>`
- Matrix status: `<x64 Passed; ARM64 Passed>`
- Model provenance input: `<file name and SHA-256>`
- x64 runtime/framework notice input: `<file name and SHA-256>`
Expand All @@ -34,4 +36,4 @@ Version 1 remains a self-contained folder/ZIP deployment. A literal single-file

`<support contact, issue tracker, or distribution contact>`

Before publishing, replace every placeholder, then compute this completed file's SHA-256 and put that filename/hash in each test matrix's `Release notes source` header. The publisher verifies that the notes name the requested version and exact ZIP artifact for each architecture and retain the unsigned SmartScreen warning.
Before publishing, fill every template field, then compute this completed file's SHA-256 and put that filename/hash in each test matrix's `Release notes source` header. The publisher verifies that the notes name the requested version and exact ZIP artifact for each architecture and retain the unsigned SmartScreen warning.
22 changes: 12 additions & 10 deletions docs/release/release-test-matrix.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# Release test matrix

Release version: `TBD`
Release date: `TBD`
Release Foundry model variant: `TBD`
Release source: `TBD`
Release notes source: `TBD`
Model provenance source: `TBD`
Runtime framework notices source: `TBD`
Single-file experiment record: `TBD` (future information only; folder/ZIP deployment remains version 1)
Release version: `<semantic version without v>`
Release date: `<YYYY-MM-DD>`
Release Foundry model variant: `<exact model variant>`
Release source: `v<version>`
Release policy: `<preview | supported>`
Preview risk acceptance: `Not applicable` or `Approver: <name>; Date: <YYYY-MM-DD>; Decision: <explicit decision>`
Release notes source: `release-notes.md sha256:<lowercase SHA-256>`
Model provenance source: `model-provenance.json sha256:<lowercase SHA-256>`
Runtime framework notices source: `runtime-framework-notices.txt sha256:<lowercase SHA-256>`
Single-file experiment record: `Not recorded` or `<evidence path>` (optional future information)

The release source is the exact `v<version>` tag for a tag release (or the full commit only for an explicitly local/manual build). The Foundry model must exactly match the configured application model. Each source header must use exact format `<file name> sha256:<lowercase SHA-256>`: use [the release-notes template](release-notes-template.md), [model-provenance template](model-provenance-template.json), and [runtime/framework notices template](runtime-framework-notices-template.txt).
The release source is the exact `v<version>` tag for a tag release (or the full commit only for an explicitly local/manual build). The release policy is derived from the version and cannot be selected independently. Preview 0.x releases allow exactly `Passed`, `Accepted risk`, or `Not applicable`; every non-passing row needs a concrete rationale, and any `Accepted risk` row requires a finalized risk-acceptance header. Supported 1.x and later releases require exactly `Passed` for every applicable architecture row. The Foundry model must exactly match the configured application model. Each source header must use exact format `<file name> sha256:<lowercase SHA-256>`: use [the release-notes template](release-notes-template.md), [model-provenance template](model-provenance-template.json), and [runtime/framework notices template](runtime-framework-notices-template.txt).


Before running the publisher, replace the headers and both `<version>` placeholders. For the architecture being published, every row below must be exactly `Passed` with retained evidence in the final column. The three source headers are filename-and-hash bindings to the exact external release inputs; do not enter a repository template name or an un-hashed path.
Before running the publisher, fill the headers and both `<version>` asset names. For the architecture being published, use only statuses permitted by the version-derived policy and retain evidence or rationale in the final column. The three source headers are filename-and-hash bindings to the exact external release inputs; do not enter a repository template name or an un-hashed path.

| Area | x64 | ARM64 | Evidence / notes |
|---|---|---|---|
Expand Down
Loading