Releases: climaa/acceptance-gate
Release list
v1.3.0 — the console gets a manual, generated from what it already tests
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
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
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@16breaks 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
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
maincarry 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 carriesCloses #<ID>, which auto-closed
a still-blocked issue once. Thesandcastle-noop-issuesprotection exists at
classification time but is bypassed once a branch reaches the merge prompt with a real
PR. Open, labelledbug, 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.