diff --git a/CHANGELOG.md b/CHANGELOG.md index 568f168d..afdcaaf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,19 +6,19 @@ Follows [Keep a Changelog](https://keepachangelog.com/); versioning is [SemVer]( ### Added +- Versioning policy SSOT + CHANGELOG tagged-section SelfCheck (C11 L119). - ADR 0005: explicit no Workers/Vercel/edge deploy target (C11 L114) + `edge-deploy-scope` SelfCheck. - - Blocking `sandbox-boundary` SelfCheck job in `security.yml` (C04 L40; hard no-net/rootless still unpaid). -- `GET /readyz` readiness probe + process-compose probe switch; SLO stubs in observability docs. +## [0.2.0] - 2026-07-04 + +Initial public release tag (`v0.2.0`). -- Security pack (audit-v38 C04/C06 P0): `SECURITY.md`, `CODE_OF_CONDUCT.md`, `deny.toml`, `.github/workflows/security.yml` (cargo-deny + gitleaks), `.github/dependabot.yml`, `.pre-commit-config.yaml`. -- Agent-readiness docs: `docs/functional_requirements.md` (FR catalog), `PLAN.md`, `WORK_DAG.md`, `llms.txt`. -- Ops stubs: `docs/ops/runbook.md` (`make dev` / healthz :8080) and `docs/ops/observability.md` (metrics + OTel soft goal). -- `AGENTS.md` ΓÇö agent entrypoint (build/test/lint/worktree/forbidden-ops); Key files links to FR/PLAN/llms/runbook. -- `rust-toolchain.toml` ΓÇö reproducible toolchain (MSRV 1.85 via Cargo). -- `.github/PULL_REQUEST_TEMPLATE.md` and `user-friction` issue template + `docs/friction-log.md`. -- proc-compose deploy stack with sl-viewer + Makefile + quickstart (#39). +### Added - +- Desktop viewer release workflow + launch instructions. +- Packaging scaffolds for macOS `.app` and Linux portable binaries. +- Session list / search selection in the viewer. +- Domain mutation-targeted state machine and boundary tests. + diff --git a/docs/ops/distribution.md b/docs/ops/distribution.md index d95397f5..87c0f461 100644 --- a/docs/ops/distribution.md +++ b/docs/ops/distribution.md @@ -535,7 +535,7 @@ SessionLedger release and data-surface versioning follow distinct rules: | Surface | Policy | Source of truth | |---------|--------|-----------------| -| **Git tags / desktop binaries** | [SemVer](https://semver.org/) on `v*` tags (`v0.1.0` → version `0.1.0`) | Root `Cargo.toml` `version` must match the tag body before tagging; [`CHANGELOG.md`](../../CHANGELOG.md) follows Keep a Changelog | +| **Git tags / desktop binaries** | [SemVer](https://semver.org/) on `v*` tags (`v0.1.0` → version `0.1.0`) | Root `Cargo.toml` `version` must match the tag body before tagging; [`CHANGELOG.md`](../../CHANGELOG.md) + [`versioning-policy.md`](versioning-policy.md) | | **Release asset names** | Tag-derived `VER` in CI (`sl-viewer-v-`, `SessionLedger--x64.msi`, `SessionLedger--.pkg`) | [`.github/workflows/release.yml`](../../.github/workflows/release.yml) `derive version` step | | **OKF export documents** | `[major].[minor]` tuple with major-bump rejection rules | [`docs/reference/OKF-SPEC.md`](../reference/OKF-SPEC.md#13-versioning--compatibility) | | **SQLite schema** | Forward-only migrations; consumers on older schema revisions upgrade via `sl-daemon` migrate | [`docs/ops/schema-migrations.md`](schema-migrations.md) | @@ -612,3 +612,4 @@ the production path (deferred). - [`observability.md`](observability.md) — `/healthz`, `/readyz`, metrics - [`SECURITY.md`](../../SECURITY.md) — supply chain / SBOM - Issue [#66](https://github.com/KooshaPari/SessionLedger/issues/66) — signing + installers + diff --git a/docs/ops/versioning-policy.md b/docs/ops/versioning-policy.md new file mode 100644 index 00000000..5d36ef84 --- /dev/null +++ b/docs/ops/versioning-policy.md @@ -0,0 +1,27 @@ +# Versioning & compatibility policy + +Status: **C11 L119** — SemVer + Keep a Changelog cadence for desktop releases. + +Related: [`CHANGELOG.md`](../../CHANGELOG.md), [`docs/ops/distribution.md`](distribution.md) +(versioning table), [`docs/reference/OKF-SPEC.md`](../reference/OKF-SPEC.md) §13. + +## Rules + +| Surface | Policy | +|---------|--------| +| Git tags / crates | SemVer `vMAJOR.MINOR.PATCH`; root `Cargo.toml` `version` matches the tag body before tagging | +| CHANGELOG | Keep a Changelog; every tagged release gets a `## [X.Y.Z] - YYYY-MM-DD` section (not only `[Unreleased]`) | +| rust-version | Workspace `rust-version` (MSRV) is declared in root `Cargo.toml`; bump with a CHANGELOG note | +| OKF documents | Separate `[major].[minor]` rules in OKF-SPEC — independent of crate SemVer | + +## Cadence + +1. Accumulate notes under `[Unreleased]` during the wave. +2. On tag: move Unreleased bullets into a new `## [X.Y.Z] - ` section; leave an empty Unreleased stub. +3. Do not invent tagged sections for untagged commits. + +## Machine verification + +```powershell +pwsh ./scripts/versioning-policy-check.ps1 -SelfCheck +``` diff --git a/scripts/versioning-policy-check.ps1 b/scripts/versioning-policy-check.ps1 new file mode 100644 index 00000000..4cf3e55b --- /dev/null +++ b/scripts/versioning-policy-check.ps1 @@ -0,0 +1,59 @@ +<# +.SYNOPSIS + Machine-check C11 L119 versioning / CHANGELOG tagged-section anchors. + +.PARAMETER SelfCheck + Explicit docs/path smoke. + +.EXAMPLE + pwsh ./scripts/versioning-policy-check.ps1 -SelfCheck +#> +[CmdletBinding()] +param([switch]$SelfCheck) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$repoRoot = Split-Path -Parent $PSScriptRoot +$changelog = Join-Path $repoRoot "CHANGELOG.md" +$policy = Join-Path $repoRoot "docs/ops/versioning-policy.md" +$dist = Join-Path $repoRoot "docs/ops/distribution.md" +$script = Join-Path $repoRoot "scripts/versioning-policy-check.ps1" +$wrapper = Join-Path $repoRoot "tests/versioning_policy.rs" + +function Assert-File([string]$Path,[string]$Label) { + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "Missing $Label at '$Path'." } +} +function Write-Check([string]$Label,[bool]$Ok) { + $mark = if ($Ok) { "PASS" } else { "FAIL" }; Write-Host " [$mark] $Label"; return $Ok +} +function Assert-Contains([string]$Doc,[string]$Needle,[string]$Label,[string]$Context="document") { + $ok = $Doc.Contains($Needle); [void](Write-Check -Label $Label -Ok $ok) + if (-not $ok) { throw "$Context missing required anchor: '$Needle'" } +} + +Write-Host "Versioning policy check (C11 L119)" +if ($SelfCheck) { Write-Host "Mode: SelfCheck (CHANGELOG tagged section + policy doc)" } + +Assert-File $changelog "CHANGELOG.md" +Assert-File $policy "versioning-policy.md" +Assert-File $dist "distribution.md" +Assert-File $script "versioning-policy-check.ps1" +Assert-File $wrapper "versioning_policy.rs" + +$cl = Get-Content -LiteralPath $changelog -Raw +$pol = Get-Content -LiteralPath $policy -Raw +$d = Get-Content -LiteralPath $dist -Raw + +Assert-Contains $cl "## [Unreleased]" "Unreleased section" "CHANGELOG.md" +Assert-Contains $cl "## [0.2.0]" "tagged 0.2.0 section" "CHANGELOG.md" +Assert-Contains $cl "Keep a Changelog" "Keep a Changelog mention" "CHANGELOG.md" +Assert-Contains $pol "C11 L119" "policy L119 marker" "versioning-policy.md" +Assert-Contains $pol "SemVer" "SemVer" "versioning-policy.md" +Assert-Contains $pol "Keep a Changelog" "Keep a Changelog" "versioning-policy.md" +Assert-Contains $pol "rust-version" "rust-version" "versioning-policy.md" +Assert-Contains $pol "scripts/versioning-policy-check.ps1" "SelfCheck script ref" "versioning-policy.md" +Assert-Contains $d "CHANGELOG.md" "distribution links CHANGELOG" "distribution.md" +Assert-Contains $d "versioning-policy.md" "distribution links versioning policy" "distribution.md" + +Write-Host "Versioning policy SelfCheck passed (C11 L119 tagged CHANGELOG + policy SSOT)." diff --git a/tests/versioning_policy.rs b/tests/versioning_policy.rs new file mode 100644 index 00000000..c2dd46a3 --- /dev/null +++ b/tests/versioning_policy.rs @@ -0,0 +1,29 @@ +//! Hermetic `SelfCheck` for C11 L119 versioning policy / tagged CHANGELOG. +//! Local: `pwsh ./scripts/versioning-policy-check.ps1 -SelfCheck` + +use std::path::PathBuf; +use std::process::Command; + +fn repo_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) +} + +#[test] +fn versioning_policy_self_check_validates_anchors() { + let script = repo_root().join("scripts/versioning-policy-check.ps1"); + assert!(script.is_file(), "expected versioning policy check at {}", script.display()); + let output = Command::new("pwsh") + .args(["-NoProfile", "-File", script.to_str().expect("utf-8"), "-SelfCheck"]) + .output() + .unwrap_or_else(|e| panic!("spawn pwsh: {e}")); + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "versioning-policy-check.ps1 failed\nstdout:\n{stdout}\nstderr:\n{stderr}" + ); + assert!( + stdout.contains("Versioning policy SelfCheck passed"), + "expected success line, got:\n{stdout}" + ); +}