- self-hosted works technically: outbound HTTPS only, no inbound
ports, honours HTTPS_PROXY/NO_PROXY — a box behind a proxy is fine
- but it defeats the pinned-runner decision: the build host sets the
glibc floor, so a workstation runner (2.39 here) puts it back to
2.38+ and drops Ubuntu 22.04 / Debian 12 / RHEL 9
- and a workstation-built release is unattested
- records what self-hosting accepts: jobs run as the starting user,
with that user's ~/.ssh, credentials and network reach — including
hosts named in ~/.ssh/config; worst on public repos, where a
stranger's PR runs code on the runner
- if unavoidable: dedicated VM, unprivileged user, --ephemeral,
segmented network, treat .credentials as a secret
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- states plainly what does NOT trigger it: builds run on a
GitHub-hosted runner, not locally, and only on a `v*` tag push —
pushing master releases nothing
- 14 numbered steps: get the workflow onto GitHub, enable Actions,
allow ocaml/setup-ocaml, the 403/workflow-permissions fallback,
a --draft rehearsal on a throwaway tag, cleanup, then the real tag
- calls out that the rehearsal tag is EXPECTED to fail the tag/VERSION
guard, and how to rehearse the full job instead
- step 8/9: read the runner's glibc floor and reconcile
install/view.wo with it — the runner, not the dev machine, decides
who can run the release
- lists the three likely first-run failures
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- .github/workflows/release.yml: builds, verifies and publishes on a
`v*` tag. `permissions: contents: write` on the injected
GITHUB_TOKEN replaces `gh auth login`; no PAT, nothing to rotate
- runs-on ubuntu-22.04 DELIBERATELY: the build host's glibc caps which
symbol versions the binaries import, and that cap is the floor every
user needs. 22.04 (2.35) includes Ubuntu 22.04 / Debian 12 / RHEL 9;
24.04 (2.39) would exclude them
- guards that fail instead of publishing: tag vs VERSION, produced
asset name vs the filename /install links, sha256, and a smoke test
that builds a hello project with the binaries INSIDE the tarball
- reports the shipped glibc floor so the claim on /install is checkable
from a build log
- releasing.md: pipeline route up front, manual route kept; GH_TOKEN
recipe for non-GitHub CI
Not run — this repo has no CI history and Actions cannot execute
locally. Every guard's shell was dry-run here against the real dist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- gh's credential is separate from git's: SSH keys let you push but
not call the API, so a machine that pushes can still fail to release
- the five interactive prompts and what to answer, with SSH as the
protocol to match this repo's existing remote
- headless path: PAT scopes (classic repo/read:org/gist, fine-grained
Contents: read and write), --with-token from a 600 file, GH_TOKEN
for automation
- verify with `gh repo view shoneyJ/writeonce` — proves the token
reaches THIS repo, not just that it is valid
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- docs/guides/releasing.md: the steps from `just dist` to a working
download button
- pins the constraint that matters: the asset filename and tag must
match the URL /install links, or the button 404s
- includes verifying the tarball with the binaries INSIDE it, tagging
the built commit, `gh release create` with both files, the web-UI
path, and a curl check of the exact link the site uses
- notes dist/ is gitignored, the shoneyJ/shoneyj path-case difference,
and what a version bump must touch in install/view.wo
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- lexer: backtick raw text literal — content verbatim, no escape
processing, common source margin removed at lex time; `${ }` raw and
`{{ }}` auto-escaping holes
- `{{ e }}` desugars to `esc(${e})` in parser.ml — a Call on the `esc`
in scope, so types/owner/emit/.wob/VM are untouched
- WO-E004 unterminated raw literal; WO-E005 newline inside "..." —
closes a hole where a missing quote silently ate the rest of the file
- wo-html: `Component` interface, `render_all`, `Layout`, README
- framework: `ok_html` joins ok_text/ok_json in http/types.wo
- site + shop restructured to one-feature-one-module MVC (view +
controller per directory, model at the root, bootstrap-only main)
- removed the filler `pad: Int` convention — verified unnecessary for
plain classes, interface dispatch, containers and actors
- corrected recorded claims: gap #1 blocks neither the build nor the
layout; a class crosses module lines, only a free fn is scoped
- docs/guides/language-surface.md — the full grammar inventory
- story 37 landed and moved to done/
Gates: oop-accept MET, oop-e2e 116/0, woc-test 556/0, site 11/0,
web-app 46/0, fibers 10/0, db-actor 8/0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- constraints-and-grammar: gram.y PK/FK productions, pg_constraint,
RI trigger semantics; writeonce direction — @key as unique alias
(id stays THE key), ref actions (@on_delete), backlink-implies-index
(improves on postgres' not-auto-created FK index)
- indexing-and-point-lookup: AM roster + algorithms (Lehman-Yao,
linear hashing), TID = row address; writeonce gap — probe walks
slabs while idx_bucket exists; O(1) slice direction, non-goals
- card index updated; Rust-era plan-10/11/12 links unlinked (rot)
- docs/guides/database-developer-subagent.md: format, paste-ready
agent definition (doctrine/file map/gates), verification, division
of labor
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>