Skip to content

Releases: climaa/acceptance-gate

v1.3.0 — the console gets a manual, generated from what it already tests

Choose a tag to compare

@climaa climaa released this 31 Aug 12:19
7ede397

Seventeen PRs since v1.2.0 (#409#427), telling one story: the visual-diff console now has an end-user manual, and it cannot drift from what it describes.

What earns the minor

Layer PRs
@gate/manual — three pages (console, report, sample) parsed at build time from the acceptance suite's own .feature files #414
The step renderer, drawn on the board first #415, #416
StepList promoted to a packages/ui molecule, shared by the manual and the console #417
"snapshot" retired from both surfaces' own words #413, #418
Wired to its deployed origin, linked from the console, a stable Thumbnail before images arrive #419, #420, #421
Page views counted on the same terms as the blog and the console #423, #424
The glossary, and why a cached test cannot answer for the confinement guard #409, #412
A UI fix — ds-table's card layout #410
apps/blog gets a robots.txt naming its sitemap #425
report.html retired — the console renders every report itself now #426
Phase 5 board item: the console says what a run is, what A/B mean, and what a verdict means #427

The deployed console's sample mode was already a real demo — PR #242's regression, seen backwards through the differ. What #427 actually closed was three sentences of narration, plus a worked overlap/margin example and a link to one representative Sandcastle-authored PR (#426) in the README, as the evidence the README had promised without pointing at an instance of it.

Ten manifests, not nine

apps/manual is new this cycle. The reason for lockstep is the one 1.0.0 stated and every release since has kept: nothing publishes to npm, every workspace is private, so the version is a coherence marker rather than a distribution fact. Every internal dependency is workspace:*, so pnpm-lock.yaml is unaffected and --frozen-lockfile still resolves.

The index, measured rather than adjusted

PROJECT_INDEX.md was stamped 2026-08-28 and had already drifted, in both directions and by omission:

Claim Corrected to
apps/manual missing entirely → 6 files / 69 tests, three published pages
packages/ui 34 / 492, 27 components 35 / 512, 28 components (StepList, the sixth molecule)
apps/blog 18 / 371 18 / 378 (375 passed, 3 skipped)
packages/visual-diff 11 / 334 10 / 327 (report-html.mjs and its test file are gone)
apps/storybook 5 / 125, 18 docs pages 5 / 128, 19 docs pages

apps/visual-diff-ui (53/829) and the orchestrator's own suite (39/654) had not drifted. README's own status table carried the same omission — no apps/manual row — corrected alongside it.

One known gap, carried forward rather than hidden

Phase 5's design-board update did not land this cycle: the pencil MCP could not see an open file this session, so designs/acceptance-gate.pen's Board 04 was not updated before the console copy in #427. Reconciling the board with that copy is a follow-up.

Full changelog: v1.2.0...v1.3.0

v1.2.0 — the apps report their own errors

Choose a tag to compare

@climaa climaa released this 28 Aug 14:00
69da5be

Ten PRs since v1.1.0 (#394#407), telling one story: both apps went from swallowing production errors to reporting them.

What earns the minor

Layer PRs
The error boundaries neither app had #400
@gate/logger — silent in production, error() always forwards to a pluggable reporter #402
The boundaries become its first call sites #405
The Bugsink adapter behind @gate/logger/bugsink #406
Page views on the production deployment only #394, #397, #398
The 404 a streamed cache miss cannot set #399
next-env.d.ts untracked #401
The written half — the post, plus Error Tracking / Logging / Web Analytics #407

The tracker is live, not dormant: the DSN lives in Vercel's project settings, and .env.example ships both variables empty on purpose, so local runs, CI and the sandbox stay indifferent to the wiring. The story is written up in The research said GlitchTip. The decision was Bugsink., with the runbook in Error Tracking.

Nine manifests, not eight

packages/logger is new this cycle. The reason for lockstep is the one 1.0.0 stated: nothing publishes to npm, every workspace is private, so the version is a coherence marker rather than a distribution fact. Every internal dependency is workspace:*, so pnpm-lock.yaml is unaffected and --frozen-lockfile still resolves.

The one code change: .fallow/churn.bin untracked

Cutting the tag is what made the case for it. It is fallow's churn cache, rewritten as a side effect of fallow health — which is pnpm health:check, one of the eight required gate jobs. The tracked copy was therefore dirtied by every local gate run and by every agent that ran the gate before staging, and it entered the history as a side effect of PRs about something else. Two of those raced in #405/#406: the first merged, the second went CONFLICTING with auto-merge armed and stranded after the orchestrator had exited, and a binary conflict needs a human.

Nothing reads the committed copy — every run regenerates it from git history — so, exactly as with next-env.d.ts in #401, there was no correct value to commit.

The index, measured rather than adjusted

Claim Corrected to
apps/blog 18 / 359 18 / 371 (3 skipped, one per draft post)
apps/visual-diff-ui 53 / 825 53 / 829
apps/storybook 5 / 121 5 / 125
42 acceptance scenarios 44
a one-scenario local lane 2
Storybook 16 docs pages 18

EXPECTED_SCENARIOS and EXPECTED_LOCAL_SCENARIOS already said 44 and 2 — the same drift as last release, so both lines now name the constant that has to agree.

Full changelog: v1.1.0...v1.2.0

v1.1.0 — the visual-diff console is stable

Choose a tag to compare

@climaa climaa released this 27 Aug 13:58
67554a1

The console is the product now. v1.0.0 drew a line under the MVP — the design system, the blog, the Storybook, the acceptance suite, the differ and the orchestrator. This tag is about the surface that reviews what the differ produces: apps/visual-diff-ui has taken 65 of the 93 commits since, and it has stopped moving.

Nothing here publishes to npm — every workspace is still private: true — so the version remains a coherence marker rather than a distribution fact. All eight manifests carry 1.1.0, not eight numbers that drift.

What changed

The visual-diff console. 17 feat, 20 fix, 10 refactor. The job system grew a lock, a streaming log, a dismissable current job and guarded mutations; the report grew tier sections, a review loop, an accessibility treatment that leads rather than trails, and a three-up viewer behind a comparison modal. Client-side JSON now parses against the same zod schemas the server answers with (#385), the four routes share one mutation guard and one containment check instead of four apiece (#358, #359), and the compare ask carries a counter so a repeat press from another tab still lands (#389).

The accept tab is gone (#356). It spawned promote, which writes a gitignored __baselines__ that no CI job reads — a review gesture that looked like it accepted a baseline and accepted nothing. Accepting now happens where it always really happened: the accept-baselines workflow a person dispatches, or accept in the pinned container. ModeTabs is two modes, capture and compare, and says why in its own header.

The accept loop closed (#330) — the Docker wrapper, accept-baselines.yml and the nightly determinism job that compare.mjs's STRICT_ENV was built for. That was the last of Wave 4's tail.

packages/ui gained the eight components the console needed — TriStateCheckbox, SegmentedControl, BucketChip, Thumbnail, Dialog with its bottom-sheet variant, Table, and the rest — taking the system to 27 in four tiers, still token-only, still layered by eslint-plugin-boundaries.

By the numbers

  • 150 test files / 2,690 tests — 790 console, 654 orchestrator, 490 ui, 334 visual-diff, 307 blog, 115 storybook
  • 42 Gherkin scenarios blocking in gate.needs, plus a one-scenario local lane that never runs in CI
  • 158 committed visual baselines, up from 106, still captured in a pinned container
  • 72 of the 94 commits since v1.0.0 carry a model co-author trailer — 236 of 284 all-time
  • 94 pull requests merged, 32 issues closed

The tag found its own bug

The first CI run of the release PR went red on a diff of version strings and markdown. GET /api/env reached dockerAvailable(), which blocks on docker info for up to three seconds, inside a unit suite whose timeout is five — latent since the endpoint was written, invisible on any laptop with a daemon running, and three of that block's four tests were equally exposed. lib/docker.ts had already declared the seam that prevents it; the suite had never set it (#396).

It is in these notes rather than quietly squashed into them because it is the argument this repo makes: a gate that only ever runs where the author sits is not a gate.

Known issues

Both are upstream, both are labelled and blocked, and neither has a workaround worth taking:

  • #68 — ESLint 10 is unreachable: eslint-config-next@16 breaks on it, two blockers deep.
  • #47 — TypeScript 7 blocked: Next cannot resolve tsconfig paths under the native compiler.

#264, the orchestrator's zero-diff-PR bug that led v1.0.0's known issues, is closed.

What's next

The project board is still the live, story-pointed roadmap. Wave 5's remaining work is three items, all verified still open at this tag rather than assumed: ds-table's card layout has not taken #302's measured auto-fit grid, the runner suite's confinement guard can still be answered by a turbo cache replay, and .fallow/churn.bin is tracked with nothing ignoring it.

v1.0.0 — the MVP is complete

Choose a tag to compare

@climaa climaa released this 14 Aug 11:51
c5acc36

The MVP is complete. Every piece the repo set out to build is in and running against
itself: the design system, the blog that consumes it, the Storybook that publishes it, the
acceptance suite, the visual differ, and the autonomous orchestrator that merges work
through all of them. This tag is the line under that.

Work continues in public — the project board
is the live, story-pointed roadmap, and the place to watch what happens next.

First tagged release. Nothing here publishes to npm — every workspace is private: true,
so the version is a coherence marker rather than a distribution fact: the repo is the
product, and it carries one version across all seven manifests.

What this is

A public portfolio monorepo where every pull request — including the ones that build the
repo itself — walks through the same gate:

lint · typecheck · build · test · format · health · sandcastle · e2e  (parallel)  →  gate

gate is the only required status check, so a new job joins gate.needs rather than
anyone editing branch protection. The ruleset itself is committed as JSON
(.github/rulesets/main.json): PR required, squash-only, no bypass actors.

visual-diff is the one deliberate exception. It runs on every PR, posts a sticky report
comment, and fails its own job loudly on a real diff — but it never joins gate.needs. A
numeric pixel threshold stops being a safety net once change volume is high, and approving
a baseline has to cost the same as reviewing a code diff: a person, in the PR.

What's in it

Surface
.sandcastle/ The autonomous orchestrator — plans, implements in a Docker sandbox, reviews with a second agent, opens a PR, squash-merges once gate is green. 27 modules, guarded by its own hermetic suite.
blog Next.js 16 App Router + MDX — posts, tags, RSS, sitemap, OG images, Cache Components and Partial Prefetching enabled.
Storybook Every component rendered in isolation, plus the docs pages that describe the system: System Design, Atomic Design, QA, DevOps, Skills.
packages/ui The design system — all 19 components of the normative inventory in four tiers, token-only styling, layering enforced by eslint-plugin-boundaries.
packages/visual-diff The self-built visual-regression CLI: Playwright capture, pixelmatch compare, axe scan per shot, self-contained HTML report. No SaaS.
apps/e2e playwright-bdd acceptance suite. .feature files are product requirements and are never edited to make a test pass.

By the numbers

  • 86 test files / 1,613 tests — 629 orchestrator, 308 ui, 286 blog, 291 visual-diff, 99 storybook
  • 9 Gherkin scenarios across smoke, blog and axe accessibility, blocking in gate.needs
  • 106 committed visual baselines, captured in a pinned container so a font-hinting
    difference between a laptop and CI can never read as a UI change
  • 163 of 189 commits on main carry a model co-author trailer
  • 161 merged pull requests, 91 closed issues

Known issues

  • #264 — the orchestrator's
    merge phase can open a zero-diff PR that still carries Closes #<ID>, which auto-closed
    a still-blocked issue once. The sandcastle-noop-issues protection exists at
    classification time but is bypassed once a branch reaches the merge prompt with a real
    PR. Open, labelled bug, and queued as the first work after this tag.

What's next

The project board carries the roadmap past
this tag — Backlog → In Progress → Done, story-pointed, burn-up per wave. It is a window
onto the pipeline, never a control surface: the work still arrives as issues, and the
issues still walk the gate.

Not in scope yet

TypeScript 7 (#47) and ESLint 10
(#68) are both blocked upstream and
tracked as such — Dependabot ignores those majors on purpose rather than opening a PR that
cannot pass.