Repository navigation
Nen v0.19.0
v0.19.0 — 2026-10-04
Release unit for v0.18.3..v0.19.0. It brings interactive desktop runners, the runner trust fixes, and stricter readiness:
- the deliveries: #334, #335, #336, #342, #343, #355 and #357;
- #358, the release proposal, and #362, which folds in #355 and #357. Those two merged while #358 was open, ahead of its merge.
The compatibility floor moves to 0.19.
Added
-
runner (#335, closes #333) — interactive Windows runners, which run inside a signed-in desktop session for jobs that drive a real UI. A service runs in session 0, where such a job times out.
- Declaration:
nen/workflow.json→runners.pools[].modeisserviceorinteractive.- Absent means
serviceon Windows and Linux, andinteractiveon macOS, whose runner is a LaunchAgent. macOS+serviceandLinux+interactiveare refused.- An interactive Windows pool's labels are exactly
["self-hosted", "Windows", <arch>, "desktop"].
- Absent means
runner plan: its contract is nownen.runner.plan/v0.2, which addsmodeanddailyAccount; a v0.1 plan still reads, as a service plan.- An interactive identity must be a local account; the built-in service identities are refused.
- Every rendering says the runner is online only while that account is signed in, with an active, unlocked session.
- It also says such a runner takes every job aimed at the plain
[self-hosted, Windows, <arch>]set, not onlydesktopjobs. - When the identity is the Windows account computing the plan, the plan warns and records
dailyAccount: true.
runner script: adailyAccountplan renders only with the new--accept-daily-account, and the verb repeats that check itself on Windows.- For an interactive pool the script installs no service and asks no password.
- It resets each
Runner<N>folder to the locked root's baseline, then grants the identity Modify there. - It registers a Scheduled Task at that account's logon that runs
run.cmdin its desktop session: interactive token, priority 4, no time limit, restarted on failure. - A folder still installed as a service is refused.
- The service-form script is unchanged.
runner workflow: it fills an eighth value,@@MODE@@. A template that predates it still renders. nen's fixture template gains a desktop-session probe, which fails in session 0, and mode-aware remediation.
- Declaration:
-
runner (#336, closes #330) — on the pool's own host,
runner planreads the existingactions.runner.*services read-only (Get-CimInstance Win32_Service/systemctl).- It warns on stderr, at exit 0, when the planned account already serves a repository of different or unknown visibility, and recommends a per-repository account (
runner-<repo>). - It also names the target's own runners left on such an account.
- Nothing is refused, and nen never creates an account.
- It warns on stderr, at exit 0, when the planned account already serves a repository of different or unknown visibility, and recommends a per-repository account (
-
pr (#355, closes #345) —
nen pr mark-ready --target <owner/name> --pr <n> [--require-head <sha>] [--dry-run]is the explicit draft-to-ready transition, now thatpr readyfails a draft at row 1. Its contract isnen.pr.mark-ready/v0.1.- It reads the PR first. Exit 2 means a target that does not resolve, exit 3 a closed or merged PR, and exit 8 a head-pin mismatch. A PR already out of draft is
already-readyat exit 0, with nothing sent. - It sends
markPullRequestReadyForReviewby node id, on gh's own credential. - It reports
marked-readyonly when a read-back showsisDraft:false. A head that moved during the call ismarked-ready-head-movedat exit 8. - Every other
prflag is refused at exit 2, with no gh call.
- It reads the PR first. Exit 2 means a target that does not resolve, exit 3 a closed or merged PR, and exit 8 a head-pin mismatch. A PR already out of draft is
-
commit (#357, closes #273) —
nen commit writeandnen wc squashread the commit they just wrote back through git's own trailer parser (git cat-file commitpiped throughgit interpret-trailers --parse --unfold).- A new
injectedmember lists every refused trailer the written commit carries: a key a hook added that the policy refuses or that ends in-by/-withwithout being admitted, or a refused key the message itself carried. - Either case is exit 3. The commit is left in place, never amended, and stderr names the undo.
- On a real write,
commit write --json'strailersis read back from the commit. - USAGE § Exit codes gains one table of every verb that returns a code above 2.
- A new
Fixed
- runner (#334, closes #319) —
runner enablecertifies only aworkflow_dispatchrun on the default branch that ran the default branch's own preflight blob.runner scripttreats a configuredRunner<N>as stale when its service is absent or GitHub no longer lists it. It re-registers that runner with--replaceinstead of counting it as skipped.- For an interactive runner, which has no service, only GitHub's listing makes it stale (#335).
- gates (#342, closes #317 and #331) —
pr readyno longer counts a head as green when all of its checks were skipped; it needs at least one success.- A draft PR is not-ready at row 1.
- A queued re-run is the latest run of its check, and holds row 2 until it concludes.
pr next-blocker --json'skindgainsdraft.
- surface (#343, closes #328) — a mirror's
summary:line now parses as YAML for everydescription, block scalars included.
Breaking / consumer notes
- Repin:
"0.18"→"0.19", andv0.18.3→v0.19.0. The maintainer moved the floor for this release because changes in #334, #335, #342 and #357 alter meaning on unchanged inputs. A repository whosedependency.minimumis below0.19is refused by this build at exit 5, and must raise its minimum and repin. zheref/hatsu declares0.18(bootstrap refv0.18.2); its repin rides with zheref/hatsu#206, which adoptsmodeand@@MODE@@. - gates —
pr readyanswers not-ready where it answered ready (#342): for a head whose checks are all skipped, for a draft, and for a check whose latest run is a queued re-run. A repository whose only checks are conditional must make one job run and succeed on every head. - runner —
runner enableexits 1 for a run it accepted before (#334): apushrun, or a run on a branch other than the default. Dispatch withnen runner preflighton the default branch and pass that run id. - runner —
runner plan --json/--outnames the contractnen.runner.plan/v0.2, where it namedv0.1, and carriesmodeanddailyAccount(#335). A reader that matches the contract string must acceptv0.2.runner scriptstill reads a v0.1 plan. - commit —
commit writeandwc squashexit3when the written commit carries a refused trailer (#357). Exit 3 means the commit exists, so a caller that reads any non-zero exit as "nothing was committed" must handle it. - wc —
wc squashloadsnen/workflow.jsonon every run, before anything moves (#357). A malformed file is exit 1 even with--baseand a message with no trailers, and a broken policy with a bad message is exit 1, not 2. - surface — on repin, Hatsu's
surfaces/codex/ten/SKILL.mdreads stale until onenen surface mirror generatefor codex: itssummary:gains double quotes (#343).