From 3eee0b542c086982423d1a7229c26fb637968bbd Mon Sep 17 00:00:00 2001 From: Cole Speelman Date: Wed, 12 Aug 2026 15:20:49 -0400 Subject: [PATCH] feat: teach the public v1 beta Resolve the immutable Beta 1 runtime first, verify the exact compiler identity on Linux and Windows, and commit generated outputs from the released ABI. Material implementation and review were assisted by OpenAI Codex. Cole Speelman reviewed the public tutorial and accepts human responsibility. Signed-off-by: Cole Speelman --- .gitignore | 2 +- GENERATED_CODE.md | 4 +- README.md | 56 ++++++++++++--------- go.mod | 2 +- go.sum | 2 + internal/views/badge.sando.go | 4 +- internal/views/home.sando.go | 4 +- internal/views/layout.sando.go | 4 +- scripts/verify.ps1 | 89 ++++++++++++++++++++++++++++++++++ scripts/verify.sh | 19 ++++++-- 10 files changed, 149 insertions(+), 37 deletions(-) create mode 100644 go.sum create mode 100644 scripts/verify.ps1 diff --git a/.gitignore b/.gitignore index e0d15d7..434973d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # SPDX-License-Identifier: 0BSD -# The current preview runtime is connected through a local workspace only. +# Local workspaces must not override the published tutorial dependencies. go.work go.work.sum diff --git a/GENERATED_CODE.md b/GENERATED_CODE.md index ab66b5a..a4f3443 100644 --- a/GENERATED_CODE.md +++ b/GENERATED_CODE.md @@ -3,8 +3,8 @@ # Generated code Every `*.sando.go` file is an owned output of the neighboring `*.sando` source. -Commit both files so production builds need only ordinary Go and the small -`sando` runtime. +The exact generator is `himesan v1.0.0-beta.1`. Commit both files so production +builds need only ordinary Go and the small `sando` runtime. Never hand-edit a generated neighbor. Run `himesan generate internal/views`, review the deterministic diff, and use `himesan check internal/views` in local diff --git a/README.md b/README.md index ec2509e..cacc873 100644 --- a/README.md +++ b/README.md @@ -25,34 +25,43 @@ The starter demonstrates the boundary plainly: time through `Server-Timing`. - Production imports the Apache-2.0 `sando` runtime, not the compiler. -## Run the current source preview +## Walk the path with Beta 1 -Sandwich Hime does not have immutable public release tags yet. Do not invent a -version-shaped install command: clone the compiler and this starter side by -side, then use a local Go workspace as an explicit preview bridge. +Install Go 1.25 or newer, then resolve the tiny runtime before installing the +immutable classroom compiler: ```sh -mkdir sandwich-hime-walk -cd sandwich-hime-walk - -git clone https://gitea.speelman.ca/gamertan/sandwich-hime.git git clone https://gitea.speelman.ca/gamertan/sandwich-hime-tutorial.git - -cd sandwich-hime -go install ./cmd/himesan - -cd ../sandwich-hime-tutorial -go work init . -go work edit -replace=gamertan.com/sandwich-hime/sando=../sandwich-hime/sando - +cd sandwich-hime-tutorial +GOWORK=off go mod download gamertan.com/sandwich-hime/sando@v1.0.0-beta.1 +go install gamertan.com/sandwich-hime/cmd/himesan@v1.0.0-beta.1 ./scripts/verify.sh +GOWORK=off go run ./cmd/site +``` + +On Windows PowerShell, use the native verifier: + +```powershell +git clone https://gitea.speelman.ca/gamertan/sandwich-hime-tutorial.git +Set-Location sandwich-hime-tutorial +$env:GOWORK = "off" +go mod download gamertan.com/sandwich-hime/sando@v1.0.0-beta.1 +go install gamertan.com/sandwich-hime/cmd/himesan@v1.0.0-beta.1 +.\scripts\verify.ps1 go run ./cmd/site ``` -Make sure `$(go env GOPATH)/bin` is on `PATH`, or set `HIMESAN_BIN` to the -compiler executable when running the verification script. `go.work` and -`go.work.sum` are intentionally ignored: they are local preview wiring, not a -claim that `v0.0.0` was published. +Make sure Go's install directory—normally `$(go env GOPATH)/bin`—is on +`PATH`, or set `HIMESAN_BIN` to the full compiler path before running either +verifier. The starter deliberately runs with `GOWORK=off`: it proves the +application resolves the published Apache-2.0 runtime rather than a neighboring +development checkout. The verification scripts also reject any compiler or +runtime version other than `v1.0.0-beta.1`. + +Beta 1 is intended for classrooms, learning, prototypes, and evaluation. Its +interfaces may still change before final v1. Linux and Windows have been +maintainer-tested; macOS is provisional while native maintainer testing is +pending. Useful Mac compatibility reports are welcome on canonical Gitea. Open [http://127.0.0.1:8080/?name=Hime-san](http://127.0.0.1:8080/?name=Hime-san), refresh it, and watch the request number and UTC time change. Then try: @@ -74,6 +83,7 @@ internal/views/views.go typed template contracts internal/views/*.sando templates people edit internal/views/*.sando.go committed generated Go scripts/verify.sh generation, tests, build, and dependency gate +scripts/verify.ps1 the same gate for native Windows PowerShell ``` The application owns the server, routing, headers, data, and deployment. @@ -83,9 +93,9 @@ before accepting real user content. ## What the verification gate proves -`./scripts/verify.sh` checks committed output, generates twice and compares -digests, runs all tests and `go vet`, builds the server into a temporary -directory, and inspects its Go dependency graph. The only production +`./scripts/verify.sh` and `scripts/verify.ps1` check committed output, generate +twice and compare digests, run all tests and `go vet`, build the server into a +temporary directory, and inspect its Go dependency graph. The only production Sandwich Hime package allowed by that graph is `gamertan.com/sandwich-hime/sando`. diff --git a/go.mod b/go.mod index 21f615a..cfb19bc 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,4 @@ module gitea.speelman.ca/gamertan/sandwich-hime-tutorial go 1.25 -require gamertan.com/sandwich-hime/sando v0.0.0 +require gamertan.com/sandwich-hime/sando v1.0.0-beta.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..633c126 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +gamertan.com/sandwich-hime/sando v1.0.0-beta.1 h1:WHe6LnVKvOz7CQr9OIWI78pCzIFIAGgoXlGfjYVAVoU= +gamertan.com/sandwich-hime/sando v1.0.0-beta.1/go.mod h1:awOXo4t2zNxxgH60qkTpMOdhYnultm9AfpjlN9gOEis= diff --git a/internal/views/badge.sando.go b/internal/views/badge.sando.go index 0c7303c..1da49b1 100644 --- a/internal/views/badge.sando.go +++ b/internal/views/badge.sando.go @@ -1,5 +1,5 @@ // Code generated by himesan; DO NOT EDIT. -// himesan:compiler 0.1.0-dev +// himesan:compiler v1.0.0-beta.1 // himesan:runtime-abi sando.v1 // himesan:source-sha256 241093d3b845d20c38e5a0fe5cf2213d6bb9114138818dbe98d8b3a99ff19d24 @@ -11,7 +11,7 @@ import ( __himesan_io "io" ) -var _ = __himesan_sando.ABI +var _ = __himesan_sando.ABISandoV1 func Badge(label string) __himesan_sando.Component { return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error { diff --git a/internal/views/home.sando.go b/internal/views/home.sando.go index 3ea42a1..9ba53d8 100644 --- a/internal/views/home.sando.go +++ b/internal/views/home.sando.go @@ -1,5 +1,5 @@ // Code generated by himesan; DO NOT EDIT. -// himesan:compiler 0.1.0-dev +// himesan:compiler v1.0.0-beta.1 // himesan:runtime-abi sando.v1 // himesan:source-sha256 2f04e0f2e6caae73ea3d419a88c53afcaf25d300e76dc522ae2f418a03a68def @@ -11,7 +11,7 @@ import ( __himesan_io "io" ) -var _ = __himesan_sando.ABI +var _ = __himesan_sando.ABISandoV1 func Home(view HomeView) __himesan_sando.Component { return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error { diff --git a/internal/views/layout.sando.go b/internal/views/layout.sando.go index 53c15f0..8bf01ef 100644 --- a/internal/views/layout.sando.go +++ b/internal/views/layout.sando.go @@ -1,5 +1,5 @@ // Code generated by himesan; DO NOT EDIT. -// himesan:compiler 0.1.0-dev +// himesan:compiler v1.0.0-beta.1 // himesan:runtime-abi sando.v1 // himesan:source-sha256 e56b373194349ae32dcbad4a26c1d1a3d133e335ff916cef064e12ca2901adf3 @@ -11,7 +11,7 @@ import ( __himesan_io "io" ) -var _ = __himesan_sando.ABI +var _ = __himesan_sando.ABISandoV1 func Layout(view LayoutView) __himesan_sando.Component { return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error { diff --git a/scripts/verify.ps1 b/scripts/verify.ps1 new file mode 100644 index 0000000..84f4f0f --- /dev/null +++ b/scripts/verify.ps1 @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: 0BSD + +[CmdletBinding()] +param() + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$repoRoot = Split-Path -Parent $PSScriptRoot +Set-Location $repoRoot +$env:GOWORK = "off" + +$himesan = if ($env:HIMESAN_BIN) { $env:HIMESAN_BIN } else { "himesan" } +$expectedVersion = "v1.0.0-beta.1" +if (-not (Get-Command $himesan -ErrorAction SilentlyContinue)) { + throw "himesan was not found; install v1.0.0-beta.1 or set HIMESAN_BIN" +} + +function Assert-LastExitCode([string]$Step) { + if ($LASTEXITCODE -ne 0) { + throw "$Step failed with exit code $LASTEXITCODE" + } +} + +function Get-GeneratedDigest { + $lines = Get-ChildItem internal/views -Recurse -File -Filter *.sando.go | + Sort-Object FullName | + ForEach-Object { + $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName).Hash.ToLowerInvariant() + "$($_.FullName):$hash" + } + return ($lines -join "`n") +} + +$versionLine = (& $himesan version | Select-Object -First 1) +Assert-LastExitCode "himesan version" +if ($versionLine -notmatch '^himesan ([^ ]+) ') { + throw "could not parse himesan version output: $versionLine" +} +if ($Matches[1] -ne $expectedVersion) { + throw "himesan version is $($Matches[1]); expected $expectedVersion" +} + +$runtimeVersion = (go list -m -f '{{.Version}}' gamertan.com/sandwich-hime/sando | Select-Object -First 1) +Assert-LastExitCode "sando runtime version" +if ($runtimeVersion -ne $expectedVersion) { + throw "sando runtime version is $runtimeVersion; expected $expectedVersion" +} + +& $himesan check internal/views +Assert-LastExitCode "himesan check" +$before = Get-GeneratedDigest +& $himesan generate internal/views +Assert-LastExitCode "first himesan generate" +$afterFirst = Get-GeneratedDigest +& $himesan generate internal/views +Assert-LastExitCode "second himesan generate" +$afterSecond = Get-GeneratedDigest +& $himesan check internal/views +Assert-LastExitCode "final himesan check" + +if ($before -ne $afterFirst -or $afterFirst -ne $afterSecond) { + throw "generated output was stale or nondeterministic" +} + +go test ./... +Assert-LastExitCode "go test" +go vet ./... +Assert-LastExitCode "go vet" + +$buildDir = Join-Path ([System.IO.Path]::GetTempPath()) ("sandwich-hime-tutorial-" + [guid]::NewGuid().ToString("N")) +New-Item -ItemType Directory -Path $buildDir | Out-Null +try { + go build -trimpath -o (Join-Path $buildDir "site.exe") ./cmd/site + Assert-LastExitCode "go build" +} finally { + Remove-Item -LiteralPath $buildDir -Recurse -Force -ErrorAction SilentlyContinue +} + +$dependencies = @(go list -deps ./cmd/site) +Assert-LastExitCode "go list -deps" +if ($dependencies -notcontains "gamertan.com/sandwich-hime/sando") { + throw "production dependency graph does not contain the sando runtime" +} +if ($dependencies | Where-Object { $_ -match '^gamertan\.com/sandwich-hime$|^gamertan\.com/sandwich-hime/(cmd|internal)(/|$)' }) { + throw "production dependency graph contains the Sandwich Hime compiler" +} + +Write-Output "verified Beta 1 identity, deterministic generation, tests, vet, build, and runtime-only production dependencies" diff --git a/scripts/verify.sh b/scripts/verify.sh index 487425f..bf5d9fb 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -7,12 +7,23 @@ repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) cd "$repo_root" himesan_bin=${HIMESAN_BIN:-himesan} +expected_version=v1.0.0-beta.1 if ! command -v "$himesan_bin" >/dev/null 2>&1; then - echo "himesan was not found; install it from the neighboring Sandwich Hime checkout or set HIMESAN_BIN" >&2 + echo "himesan was not found; install v1.0.0-beta.1 or set HIMESAN_BIN" >&2 exit 1 fi -if [[ ! -f go.work ]]; then - echo "go.work is missing; follow the README preview bridge commands first" >&2 + +export GOWORK=off + +actual_version=$("$himesan_bin" version | awk 'NR == 1 { print $2 }') +if [[ "$actual_version" != "$expected_version" ]]; then + echo "himesan version is $actual_version; expected $expected_version" >&2 + exit 1 +fi + +runtime_version=$(go list -m -f '{{.Version}}' gamertan.com/sandwich-hime/sando) +if [[ "$runtime_version" != "$expected_version" ]]; then + echo "sando runtime version is $runtime_version; expected $expected_version" >&2 exit 1 fi @@ -53,4 +64,4 @@ if grep -Eq '^gamertan\.com/sandwich-hime$|^gamertan\.com/sandwich-hime/(cmd|int exit 1 fi -echo "verified deterministic generation, tests, vet, build, and runtime-only production dependencies" +echo "verified Beta 1 identity, deterministic generation, tests, vet, build, and runtime-only production dependencies"