Releases: valthon/zigapagos
Release list
v0.3.0
Added
- Publish native arm64 release archives for Linux and macOS, with matching npm
packages and shell-installer support. Apple Silicon and Linux arm64 installs
now receive binaries built and exercised on their native runner architecture. zigapagos release --summary: after a build, print on stdout an inventory of the files it
emitted, grouped by category — pages, page aliases and alternatives, page assets, site
assets, build assets, SPA shells, SPA routing manifests and the SPA 404 fallback. Every entry
is recorded where the file is written, andtests/summary/summary.shcompares the printed set
against the emitted tree, so the report cannot describe a tree the build did not produce. A
build with rendering errors prints a one-line refusal instead of an inventory — on stdout too,
so--summary >fileanswers on the same stream whatever the build's outcome.- A shell installer,
curl -fsSL https://valthon.github.io/zigapagos/install.sh | sh, now the
headline install method on the README and the download page. It installs a complete
zigapagos — the binary, the@z/runtimetree it renders islands and SPAs through, and Bun and
ZigBase when the host has neither — under~/.local/share/zigapagos, with a generated launcher
in~/.local/bin. Nosudo, no edits to shell startup files, and nothing written until each
download has been verified against the release's published SHA-256 sums. It is idempotent: a
second run installs alongside the first and repoints the launcher.--version,--prefix,
--bin-dir,--no-bunand--no-zigbasecover the rest. Windows hosts are refused with the
same wording the npm package uses, rather than being given an emulated build that looks native. - A
runtime.tar.xzrelease asset: the@z/runtimetree with its dependencies vendored. This is
what makes an install outside npm able to render an island at all — the per-target archives
carry the binary alone, and the sidecar, bundlers and slicers are scripts inside that tree. It
is staged by the same code that stages npm's copy (npm/stage-runtime.mjs), so the two
channels ship the same files by construction. zigapagos releasebuilds the per-site islands runtime slice. The second
pass over the built island bundles (/islands/_runtime.js) used to run only
as a build-graph step, so a toolchain-free build silently shipped the full
shared runtime to every island page.zigapagos releaseemits host config and the strict-CSP artifacts. The
per-namespace server config (.spamarker +zigbase.static_routes.zig,
nginx.nginx.conf,.htaccess) and the site-widecsp.{nginx.conf,apache.conf,zigbase.txt}
are written over the finished output tree. Every npm-path build until now
shipped a tree with neither, which loses SPA deep-link fallback and serves a
CSP that blocks the site's own inline import map.zigapagos release --source-maps, replacingOptions.source_maps. Still
opt-in and off by default.docs/runtime-dependencies.md— what the standalone binary needs at run
time, stated once instead of inferred. A table covering every command and the
external programs it requires; whenzigapagos releaseactually needs Bun
(the condition is the configuration, not whether the site has islands — with
ZIGAPAGOS_RUNTIME_DIRset, a site with none still spawns the sidecar); how
the pinned ZigBase is resolved, cached and fetched, including thecurland
tarthe fetch shells out to; and what each distribution supplies. Notably:
a release archive carries the binary alone, so islands and SPAs built from one
need an@z/runtimetree pointed at byZIGAPAGOS_RUNTIME_DIR—@z/runtime
isprivate: trueand cannot be installed from npm on its own.tests/meta/runtime-deps-doc.shchecks that page against the sources every
claim came from: the command table againstsrc/main.zig'sCommandenum,
the ZigBase pin and cache path againstsrc/cli/zigbase.zig, the environment
variable againstsrc/cli/release.zig,@z/runtime's privacy against
runtime/package.json, the binary-only release archive against
build/release.zig, and every flag the page names against the file that
parses it.
Changed
- Build the x86_64 macOS archive natively on
macos-15-intelinstead of
cross-compiling it on an arm64macos-latestrunner. - Release and development builds now share the checked-in Wuffs translation
shims instead of release builds invokingzig translate-c. This removes a
hand-maintained divergence and avoids the Zig 0.16 translation crash that
blocked native arm64 artifacts. docs/runtime-dependencies.md's distribution table gains aninstall.shcolumn, and its
gate (tests/meta/runtime-deps-doc.sh) gains a rule that fails the build if that column
ever describes a script or an asset that no longer exists.zigapagos initnow scaffolds only the frontmatter a page needs..author
and.draftare gone from every template and.dateremains only on the blog
posts and devlog years whose listing layouts render one —.titleand
.layoutare the only required fields, and the scaffold no longer models the
optional ones as obligatory. The sample homepage and the quick start now say
which fields have defaults.zigapagos migrate --helpnow states outright that the command converts
nothing: it reads the Astro project, writes aMIGRATION.mdworklist, and
the port itself is manual —--scaffoldbeing the one exception, and only
for islands. The README bullet and the site's overview page said or implied
otherwise.zigapagos devis zero-config. Run it in a site directory with no
arguments and it works:--sitedefaults topublic, the same directory a barezigapagos release
writes to;- the rebuild command defaults to this binary's own
release, resolved by
absolute path rather than by name onPATH, so an npm install and a
downloaded release tarball both work (it waszig build, which named a
toolchain a standalone user never installed); - the island/SPA source directories to watch are derived from the entries
releasediscovers, so a component edit rebuilds without--watch-dir; - a missing
zigbaseis fetched from the pinned release into the cache
(SHA256-verified) instead of failing with instructions.--no-download
restores the previous behaviour for offline machines and for CI that pins
its own binary.zigapagos e2eis unchanged: it still fetches only on
--download-zigbase, because an unannounced network fetch in CI is a
surprise rather than a convenience.
zigapagos initnow points a new site atzigapagos devrather than at the
bare command.
Removed
-
The bundled live server is gone, along with its
--proxyreverse-proxy
mode, theserveandserversubcommands, and the bare-command entry point
that started it (issue #56).zigapagosis a standalone executable, and a
standalone executable has no default action: run bare it now prints its help
and exits 0, which is whatnpx zigapagosdoes too. An argument that names no
command prints the same menu and exits non-zero. -
The consumer zig-build API is gone.
zigapagos.website(),zigapagos.e2e()
andzigapagos.dev(), the option types (Options,Island,Spa,
BuildAsset,E2eOptions,DevOptions) and the wholebuild/half that
served them no longer exist. A site is built by RUNNING thezigapagos
binary;zig buildbuilds zigapagos itself and nothing else. Nothing in a
zigapagos project needs a Zig toolchain, abuild.zig, abuild.zig.zonor a
.pathdependency on this repository any more.The replacements, all of which already existed:
was now zigapagos.website(b, .{ .islands = …, .spas = … })zigapagos release --island=SRC --spa='SRC|BASE'zigapagos.e2e(b, opts, .{})+zig build e2e -- CMDzigapagos e2e --site=DIR -- CMDzigapagos.dev(b, opts, .{})+zig build devzigapagos devOptions.source_maps = truezigapagos release --source-mapsOptions.not_found--spa-not-found=NAMEOptions.build_assets--build-asset=NAME PATH [--install=P | --install-always=P] -
zigapagos release --spa-chunks=and--spa-slice=are removed. They existed
to handreleasebundles the build graph had already produced; it now builds
them itself. -
zigapagos init --from-astro --zigapagos-pathis removed with the
build.zig.zonit filled in. The importer scaffolds abuild.shinstead.
Fixed
zigapagos releasenow honoursZIGAPAGOS_HOT_ISLANDS, passing--hotto
the island bundle driver when it is set. Nothing on thereleasepath read
the variablezigapagos devsets, so a dev rebuild produced non-hot island
bundles and an island hot-swap silently reset everyuseStateinstead of
preserving it.- A CLI report no longer overwrites the command's own stderr when both streams are redirected
to one file (cmd >f 2>&1).explain,doctor,validateandmigrate --doctorbuilt
their buffered stdout writers withIo.File.writer, which writes positionally from an offset
of its own, so the end-of-command flush landed on top of bytes stderr had already committed —
silently corrupting the merged output. They now usewriterStreaming. - Island bundles are minified. The build graph passed
--minifyto the
shared runtime, both runtime slicers and every SPA bundle, and to islands
alone did not. The four islands on this project's own marketing site shrink
from 4113 to 2094 bytes. - The island-sidecar spawn diagnostics no longer send you to a
build.zig
that does not exist. All three ENOENT messages ended by pointing at the
consumer build API's.islandstable, which is gone; they now name the flag
that actually configures each input (--bun, `--isla...
v0.2.0
Added
- npm distribution.
npx zigapagosnow scaffolds, serves and builds a content
site with no Zig toolchain. Three packages, released together at
build.zig.zon's version:@zigapagos/cli-<platform>carrying the prebuilt
binary,@zigapagos/cli(canonical) resolving the right one at run time through
optionalDependencies, and the unscopedzigapagosas a thin alias sonpx zigapagosworks. Prebuilt for macOS x64 and Linux x64 — the two targets
build/release.zigships. Every other host is refused with the reason rather
than the bare fact, and arm64 (macOS or Linux) is refused rather than served the
x64 binary:npm installfails withEBADPLATFORMbecause the launcher packages
declare theos/cputhey have binaries for, so an unsupported host cannot end
up with an install that looks clean and has no binary in it. Everything builds
fromnpm i zigapagosalone — content, islands, native SPAs andzigapagos dev:
@zigapagos/cliships the@z/runtimesources and the Bun SSR sidecar, and
declaresbun,typescriptand@zigbase/serveras optional dependencies, so
the tools it shells out to are installed rather than asked for. npm puts
node_modules/.binonPATH, and the launcher appends it to the child's, so the
zigbase locator finds the server with no flag, no global install and nothing
downloaded.--omit=optionalstill builds; it losesdev's server and the SPA
runtime slice. The remaining difference from a Zig build is caching, not
capability. The published READMEs say so. Seenpm/README.md. - The zigbase dependency is the scoped
@zigbase/server, at exactly the
pinned_versioninsrc/cli/zigbase.zig(currently ZigBasev0.12.0) — the same
release--download-zigbasefetches, sozigapagos devruns one zigbase however
it was installed.npm/check-toolchain.mjsfails the build if those two ever
disagree. zigapagos doctor [DIR]: audits a BUILT output tree (defaultpublic, read-only — never
builds, never touches site source) for authoring mistakes that are only visible in the final
emitted HTML. Ships two checks:abs-url-meta(a root-relative Open Graph / Twitter / canonical
URL — crawlers can't resolve it, so this is anerror) anddangling-internal-link(a
root-relativehref/srcwith no file behind it in the tree, including under--url-prefix—
awarn, since a client-routed SPA route legitimately has no file). Exit code: anyerror
finding, or a file doctor could not read, exits non-zero;warn-only findings exit 0 unless
--strictis passed.zigapagos validate [OPTIONS]: a fast, in-memory subset ofzigapagos release's checks (issue
#45). Parses and analyzes the site — frontmatter/Ziggy schema, SuperMD parse, layout resolution,
content-side$link.page/asset references, output-URL collisions, template SuperHTML/Scripty
parse, the:directive lint, and template RENDER errors (a failing Scripty expression, a
$site.page(...)naming no page) — WITHOUT bundling islands, spawning the Bun sidecar, or
writing an output tree. It does not cover island SSR, the typed island props check, SPA route
enumeration/spec checks, asset installation, or CSS minification — those stayrelease-only, so
a greenvalidateis a subset guarantee, not a greenrelease. Measured (this repo's
examples/tsx-site, warm caches): a content-only edit loop goes fromzig build's ~2s to
validate's ~0.02–0.03s — and unlikezig build,validateneeds nobun,node_modules,
build.zig, or consumer build graph, and never writes the output tree.zigapagos explain <route>: resolves one output route to its content source, layoutextends
chain (for a route that is one of a page'salternatives, that alternative's OWN layout, not the
page's), effective frontmatter (after schema defaults), islands (as declared in the markup, not
SSR-verified), page-owned assets (referenced vs. pruned), and EMITTED PATHS relative to the
output directory (issue #47). Runs the same kind of fast in-memory build asvalidate. Content
routes only — a memory build never prerenders SPAs, so a client-routed SPA route is not covered;
the miss message says so.zigapagos languages: lists every code-fence language registered for syntax
highlighting.zigapagos release --format=jsonemits build diagnostics as NDJSON on stderr — one
minified JSON object per line,{"code","severity","file","line","col","message","help"}
— instead of the historical multi-line prose. The consumer this is for is an unattended
agent: it can now tell which diagnostic fired without pattern-matching English. Default
is--format=textand text mode is byte-for-byte unchanged.- The diagnostic
codeis the stability guarantee;messageandhelpexplicitly are not.
src/diag-codes.frozenis the append-only ledger that makes that a gate rather than a
promise — a code is never renamed and never reused for a different meaning after
retirement, enforced against the enum on every build. zigapagos explain-code <CODE>prints the long form of any code: what condition produced
the diagnostic and what to change in the source.zigapagos explain-codewith no argument lists
every registered code with a one-line summary. Every code is required by the compiler to
have both, so the listing cannot go stale relative to what the build emits.- The two
:directive lints get one code each rather than a shared one —
ZP_TEMPLATE_ELSE_DIRECTIVEandZP_TEMPLATE_BRANCHING_WITHOUT_END_TAG— because they
are unrelated failures with unrelated fixes andcodeis what a consumer switches on. docs/diagnostics.mdis the consumer contract: the wire schema, what is and is not
stable, and an explicit inventory of what is not converted and why — including the
rule that matters most, skip a stderr line that does not parse as JSON rather than
failing the run, since the Bun sidecar and the usage-menu path legitimately write
prose to the same stream.- Islands can now be embedded directly in
.smdcontent, not only in
layouts: inside a fenced code block whose fence info is=html(SuperMD's
existing validated raw-HTML escape hatch), use the hyphenated
<z-island src="…" client:load :props='…'></z-island>spelling — the
islands pass treats it identically to<island>in a layout (SSR,
data-z-props, the import map, the runtime script, thetscprops gate,
and the dev island-usage manifest all apply unchanged). The hyphen is
required: superhtml's.html-mode validator (used to vet the fence body)
rejects a non-hyphenated custom element name per the HTML spec, unlike the
lax.superhtmllayout mode where<island>has always worked. See
docs/islands.md, "Islands in content (.smd)". - Opt-in
auto_heading_idssite setting (Site/MultilingualSiteinzigapagos.ziggy):
injects a GitHub-compatible slugidinto every heading that doesn't already carry an
explicit$heading.id(...)/$section.id(...), so a same-page#anchoror cross-page
/page#anchorlink written against a doc's existing GitHub rendering keeps working
without hand-writing an id on every heading. Off by default; an explicit id always wins
and is never overwritten. Seedocs/migration/astro-to-zigapagos.md's "Heading anchors:
auto_heading_ids" section. $site.asset(...).absLink()/$page.asset(...).absLink(): likelink(),
but always returns an absolute URL (host_url+url_path_prefix+ asset
path), and installs the asset the same waylink()does. Use it for URLs
consumed outside the page itself —og:*/twitter:*meta tags, canonical
links, feeds — sincelink()'s output is root-relative and scrapers do not
resolve those (#25)..asset_fingerprint = trueinzigapagos.ziggyinstalls every linked site asset under a
content-hashed filename (assets/style.css→/style.a1b2c3d4.css), and every seam that
prints a site-asset URL —.link()/.absLink(), thecontent directives, and
spa.headhrefs — resolves to that name through one shared formatter, so an installed file
and a link to it cannot drift apart. A changed file is a changed URL, which is what lets a
deploy putCache-Control: immutableon the asset tree. Opt-in and release-only;
static_assetsentries, build assets, page assets and the in-memory live server keep verbatim
names. Seedocs/assets.md.--allow-missing-pages(zigapagos releaseand the live server; for azigapagos dev
loop setallow_missing_pagesin yourbuild.zig, sincedevre-runs your rebuild
command rather than building the site itself — the tolerance is identical either way,
so a green dev preview and a CI release agree):
tolerate a$link.page/$link.sibling/$link.sub(content) or
$site.page(...)(template) reference to a page that doesn't exist YET, instead of
hard-failing the build. The reference renders as the real,url_prefix-awarehref
the target page will have once it's written (a 404 until then), and the build log gets
a warning naming the ref and the computed href instead of a fatal error. This is the
fix for incremental authoring: previously, adding a navigation link before its target
page existed broke the entire build (one dangling link → zero pages built), which is
exactly what "site under construction" always looks like.- A relative
.aliasesentry that basenames as404.html,robots.txt, orsitemap.xml
now prints a build-time warning showing where it actually resolves. Alias resolution
itself is unchanged — a relative entry still joins to the page's own output directory,
exactly as before; this only flags the common mistake of meaning a site-wide override
(e.g."/404.html"to replace the SPA fallback) but writing the bare relative form
instead. - A layout route now receives its matched child as a `childr...
v0.1.1
Internal
-
Changelog entries are now recorded as one fragment file per change in
changelog.d/,
assembled into a version section byscripts/assemble-changelog.shat release, so
parallel pull requests never conflict onCHANGELOG.md. Seechangelog.d/README.md. -
Four byte-identical private copies of
escapeRegExpin build-time TypeScript are
gone, replaced by the right tool for each of the two contexts they were serving.
The three JavaScript call sites (lint-island-imports.ts,react-alias.ts,
sidecar/bundle-island.ts) now use the standardRegExp.escape, which is
specified for exactly the ECMAScriptRegExpposition they feed. The fourth,
emit-host-config.ts, emits ApacheRewriteRulepatterns — PCRE, a different
dialect — so it gets a purpose-namedescapePcrewhose contract matches its
output language and which keeps a deployed.htaccessreadable
(^app/.*$, not^\x61pp/.*$). -
Generated Apache config now has a validation net rather than only literal-string
greps:emit-host-config.test.tsruns each emittedRewriteRulepattern through
a real Perl-compatible regex engine and asserts it matches the URLs it should and
rejects the near-misses an unescaped.would have swallowed.buildAllowgained
the metacharacter-escaping test it never had. -
Eleven of the fourteen test scripts
tests/meta/unrun-scripts.txtinventoried as knowingly
unrun now run in CI: the non-browserexamples/tsx-site/test/*.sh— island SSR and the
bundle/import-map wiring, SSR↔CSR parity, byte-parity against a rawbun build, depfile
incrementality, the props-check gate in both directions,migrate --doctor, and the four SPA
prerender scripts (routing manifest, nginx/zigbase host config, code splitting, baked flag
defaults, guarded routes, nested layouts) — plus the live-server smoke test.They are a step in the existing
e2e-dev-loopjob rather than atests/<area>/shim, and the
distinction is the whole point. Every one of them runszig buildinsideexamples/tsx-site,
i.e. a full consumer build of zigapagos-as-a-dependency, ande2e-dev-loopis the only job
that already pays for one — itstests/serve/dev.shstep drives that project's own
zig build dev. A shim would have put them ine2e-rest, which builds the repo and not the
example, buying a cold ~265s consumer build and making that job the run's critical path.
Measured against the warm tree the job already has, the eleven cost 49s in CI (29s
locally) against the 468s thedev.shstep above them takes.serve.shalone was
inventoried at 76.1s; behinddev.shit is ~5s, which is the placement argument in one
number.The list is literal, not a glob, for the opposite reason
e2e-restuses a glob: a new sibling
in that directory should NOT be adopted onto the PR path automatically — it might be the next
one that needs a browser or four minutes. Being unnamed there is exactly what makes
script-coverage.shstop and ask. -
tests/meta/script-coverage.shno longer counts a script as CI-run because a workflow
comment names it. Its rule (b) was a plaingit grepover.github/workflows/, so prose
saying "these two are deliberately not run here" would have vouched for precisely the scripts
it was disclaiming — and, since both are also inventoried, would have failed the gate with
"run by CI but also listed". Rule (b) now applies the same non-comment filter rule (c) already
had. The two Playwright paths are spelled in full in that comment on purpose: they pin the
filter, because removing it turns the gate red by name.(
site/test/build.shand the two Playwright scripts were the three still inventoried at this
point; all three were wired up before this release shipped — see the entry below for where
each ended up and why.) -
The
typescriptdevDependency moves 5.9.3 → 6.0.3, the final JavaScript-based TypeScript
line. The compiler API thatruntime/scripts/slice-host.tsand
runtime/sidecar/hot-transform.tsparse with is fully present, so neither needed
re-platforming, and the runtime suite is unchanged at 617 passing.site/bun.lockand
examples/tsx-site/bun.lockare regenerated in step: each embeds its own copy of
@z/runtime's resolved dependencies and bun does not refresh them for a linked package on a
plain install, so left alone they would have kept the props-check gate running 5.9.3 while the
runtime was tested on 6.0.3. -
TypeScript 7.x is capped out via a Dependabot
ignoreon>=7.0.0. 7.0 is the Go rewrite and
its npm package no longer ships the JavaScript compiler API —import ts from "typescript"
resolves tolib/version.cjsand yields only{version, versionMajorMinor}, taking the
runtime suite to 566 passing / 51 failing. The cap is deliberately a version bound rather than
a major-block, which is what let 6.x through. It lifts when a 7.x ships a usable programmatic
API (7.1 at the earliest). -
The
tsconfig.jsonfiles were audited against TypeScript 6.0's deprecation list and needed no
changes: none usesbaseUrl,outFile,downlevelIteration,target: es5,
moduleResolution: node|node10|classic,module: amd|umd|system|none, or an explicitly false
esModuleInterop/allowSyntheticDefaultImports/alwaysStrict. No source file uses the
modulenamespace keyword or importassertsyntax.ignoreDeprecationsis therefore not
needed, and the config surface is already clean for whatever 7.x removes. -
Dependabot no longer groups major version bumps with routine ones. The
bungroups for
runtime/are restricted to minor and patch, and a newruntime-majorsgroup collects every
major into its own pull request, so a breaking major can no longer block unrelated updates
from merging.github-actionsdeliberately keeps its single group: everyuses:is pinned to
a bare major tag, so majors are the only update it can produce and splitting would reintroduce
per-action pull-request spam. -
happy-domand@happy-dom/global-registratormove to 20.11.0. -
tests/meta/unrun-scripts.txtis empty. All 36 tracked test scripts are now run by CI;
the inventory that started at 14 rows and was cut to 3 is at 0. The file stays because the
gate reading it is the point, not the list.site/test/build.shmoved intopages.yml, betweenBuild siteandUpload artifact. That
makes it a deploy gate: a failed assertion fails the build job, the artifact is never
uploaded, anddeploy(whichneeds: build) never runs, so the previous good deployment
stays live. It is nearly free there — the workflow has already builtsite/, so the script's
own install and build are warm no-ops and the five greps measured 1.7s — against ~120s in any
ci.ymljob, becausesite/is a third consumer project with its own.zig-cachethat
nothing else warms. The residual gap is stated rather than glossed:pages.ymltriggers on
push tomainand manual dispatch only, so these assertions gate the deploy and not the PR.examples/tsx-site/test/{hydrate,spa_slice}.shmoved into a newbrowser-e2e.ymlon a
nightlyschedule:plusworkflow_dispatch:. Scheduled rather than PR-gating because each is
~125s on top of a ~265s cold consumer build plus a ~150MB browser install, and because what
they catch — a real-browser hydration or runtime-slicing regression — arrives with a
runtime/srcchange or a dependency bump, unattended. Each script gets its own matrix runner
(fail-fast: false):spa_slice.shopens byrm -rfing.zig-cacheandzig-out, so the
two cannot share a build, and separate runners make that hazard structurally impossible rather
than merely avoided.One correction to the plan the inventory carried: the install step is
playwright install --with-deps chrome, notchromium. All ten*_playwright.pyhelpers
launch withchannel="chrome", which on Linux resolves to/opt/google/chrome/chrome— the
bundled Chromium build satisfies none of them, and the run would have died at browser launch
after paying for the whole consumer build. -
tests/meta/script-coverage.shgained a self-test,tests/meta/script-coverage.test.sh,
in the shape ofscripts/check-allocator-contracts.test.sh: seven cases against throwaway git
repos in$TMPDIR. That gate has shipped three defects already — a self-vouching inventory, a
pipefail+grep -qSIGPIPE race, and a comment filter applied to one rule but not the
other — and every one made it pass when it should have failed.Two of the cases exist because emptying the inventory silently removed the only thing pinning
the comment filter. That filter is what stops a script a workflow merely mentions in prose
from counting as run, and it was pinned by accident: while the two Playwright scripts were
inventoried, dropping the filter made the gate see them as both CI-run and listed and fail by
name. With the inventory empty, removing the filter now breaks nothing in the tree —
confirmed by deleting the filter line and watching the real gate still pass 36/36. Case 5
makes the pin deliberate; case 6 is its guard rail, that comment-awareness has not become
"never believe a workflow". -
contract/test/drift.sh— the test that proves the cross-tier codegen gate is not vacuous —
was itself vacuous, and now runs in CI. Its Case B asserted only thattsc --noEmitexited
non-zero, which a compiler that fails to launch also does:contract/has no
node_modules, sobun x tscresolvedtscoffPATH, hit mise's shim, and died with
No version is set for shim: tsc— exit 1, nothing type-checked,PASS Case Bprinted. Cases
A and B now assert on the diagnostic text (theexperiments→variantshunk in api-check's
staged diff; both assignability directions of the_assert.tstripwire, and no diagnostic
from anywhere else), and a new Case D ...