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 <crspeelman@gmail.com>
This commit is contained in:
2026-08-12 15:20:49 -04:00
parent ddd91c7e7c
commit 3eee0b542c
10 changed files with 149 additions and 37 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: 0BSD # 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
go.work.sum go.work.sum
+2 -2
View File
@@ -3,8 +3,8 @@
# Generated code # Generated code
Every `*.sando.go` file is an owned output of the neighboring `*.sando` source. 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 The exact generator is `himesan v1.0.0-beta.1`. Commit both files so production
`sando` runtime. builds need only ordinary Go and the small `sando` runtime.
Never hand-edit a generated neighbor. Run `himesan generate internal/views`, Never hand-edit a generated neighbor. Run `himesan generate internal/views`,
review the deterministic diff, and use `himesan check internal/views` in local review the deterministic diff, and use `himesan check internal/views` in local
+33 -23
View File
@@ -25,34 +25,43 @@ The starter demonstrates the boundary plainly:
time through `Server-Timing`. time through `Server-Timing`.
- Production imports the Apache-2.0 `sando` runtime, not the compiler. - 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 Install Go 1.25 or newer, then resolve the tiny runtime before installing the
version-shaped install command: clone the compiler and this starter side by immutable classroom compiler:
side, then use a local Go workspace as an explicit preview bridge.
```sh ```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 git clone https://gitea.speelman.ca/gamertan/sandwich-hime-tutorial.git
cd sandwich-hime-tutorial
cd sandwich-hime GOWORK=off go mod download gamertan.com/sandwich-hime/sando@v1.0.0-beta.1
go install ./cmd/himesan go install gamertan.com/sandwich-hime/cmd/himesan@v1.0.0-beta.1
cd ../sandwich-hime-tutorial
go work init .
go work edit -replace=gamertan.com/sandwich-hime/sando=../sandwich-hime/sando
./scripts/verify.sh ./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 go run ./cmd/site
``` ```
Make sure `$(go env GOPATH)/bin` is on `PATH`, or set `HIMESAN_BIN` to the Make sure Go's install directory—normally `$(go env GOPATH)/bin`—is on
compiler executable when running the verification script. `go.work` and `PATH`, or set `HIMESAN_BIN` to the full compiler path before running either
`go.work.sum` are intentionally ignored: they are local preview wiring, not a verifier. The starter deliberately runs with `GOWORK=off`: it proves the
claim that `v0.0.0` was published. 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), 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: 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 templates people edit
internal/views/*.sando.go committed generated Go internal/views/*.sando.go committed generated Go
scripts/verify.sh generation, tests, build, and dependency gate 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. The application owns the server, routing, headers, data, and deployment.
@@ -83,9 +93,9 @@ before accepting real user content.
## What the verification gate proves ## What the verification gate proves
`./scripts/verify.sh` checks committed output, generates twice and compares `./scripts/verify.sh` and `scripts/verify.ps1` check committed output, generate
digests, runs all tests and `go vet`, builds the server into a temporary twice and compare digests, run all tests and `go vet`, build the server into a
directory, and inspects its Go dependency graph. The only production temporary directory, and inspect its Go dependency graph. The only production
Sandwich Hime package allowed by that graph is Sandwich Hime package allowed by that graph is
`gamertan.com/sandwich-hime/sando`. `gamertan.com/sandwich-hime/sando`.
+1 -1
View File
@@ -4,4 +4,4 @@ module gitea.speelman.ca/gamertan/sandwich-hime-tutorial
go 1.25 go 1.25
require gamertan.com/sandwich-hime/sando v0.0.0 require gamertan.com/sandwich-hime/sando v1.0.0-beta.1
+2
View File
@@ -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=
+2 -2
View File
@@ -1,5 +1,5 @@
// Code generated by himesan; DO NOT EDIT. // 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:runtime-abi sando.v1
// himesan:source-sha256 241093d3b845d20c38e5a0fe5cf2213d6bb9114138818dbe98d8b3a99ff19d24 // himesan:source-sha256 241093d3b845d20c38e5a0fe5cf2213d6bb9114138818dbe98d8b3a99ff19d24
@@ -11,7 +11,7 @@ import (
__himesan_io "io" __himesan_io "io"
) )
var _ = __himesan_sando.ABI var _ = __himesan_sando.ABISandoV1
func Badge(label string) __himesan_sando.Component { func Badge(label string) __himesan_sando.Component {
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error { return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
+2 -2
View File
@@ -1,5 +1,5 @@
// Code generated by himesan; DO NOT EDIT. // 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:runtime-abi sando.v1
// himesan:source-sha256 2f04e0f2e6caae73ea3d419a88c53afcaf25d300e76dc522ae2f418a03a68def // himesan:source-sha256 2f04e0f2e6caae73ea3d419a88c53afcaf25d300e76dc522ae2f418a03a68def
@@ -11,7 +11,7 @@ import (
__himesan_io "io" __himesan_io "io"
) )
var _ = __himesan_sando.ABI var _ = __himesan_sando.ABISandoV1
func Home(view HomeView) __himesan_sando.Component { func Home(view HomeView) __himesan_sando.Component {
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error { return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
+2 -2
View File
@@ -1,5 +1,5 @@
// Code generated by himesan; DO NOT EDIT. // 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:runtime-abi sando.v1
// himesan:source-sha256 e56b373194349ae32dcbad4a26c1d1a3d133e335ff916cef064e12ca2901adf3 // himesan:source-sha256 e56b373194349ae32dcbad4a26c1d1a3d133e335ff916cef064e12ca2901adf3
@@ -11,7 +11,7 @@ import (
__himesan_io "io" __himesan_io "io"
) )
var _ = __himesan_sando.ABI var _ = __himesan_sando.ABISandoV1
func Layout(view LayoutView) __himesan_sando.Component { func Layout(view LayoutView) __himesan_sando.Component {
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error { return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
+89
View File
@@ -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"
+15 -4
View File
@@ -7,12 +7,23 @@ repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
cd "$repo_root" cd "$repo_root"
himesan_bin=${HIMESAN_BIN:-himesan} himesan_bin=${HIMESAN_BIN:-himesan}
expected_version=v1.0.0-beta.1
if ! command -v "$himesan_bin" >/dev/null 2>&1; then 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 exit 1
fi 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 exit 1
fi fi
@@ -53,4 +64,4 @@ if grep -Eq '^gamertan\.com/sandwich-hime$|^gamertan\.com/sandwich-hime/(cmd|int
exit 1 exit 1
fi 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"