CustomLabs
Delivery record

The Delivery Record.

The Agentic Delivery Playbook says how we run a fleet of coding agents. This page is the receipts.

Every page on customlabs.io was built that way: one task per branch, a fixed stage flow, nothing reaching main except through a merge request. 203 of those tasks merged in 109 days.

12 defects got past the agent that wrote them. Below is each one: what shipped, what caught it, and the check that now fails the build if it comes back. The playbook makes claims. This page shows the working.

Counts as of 2026-10-21, at commit 09d8993.

01: The numbers

6 numbers, each one caveated. #

Every tile carries a second line for what it doesn't tell you. Read them together, not alone.

snapshot

203

Distinct tasks merged

216 merge commits from task branches, total.

Doesn't tell you: This count is lower than the merge-commit count above it. Some tasks merged into main more than once. 3 merged as a suffixed follow-up branch after review or verify rejected the first attempt. 10 merged a second time from the same branch.

snapshot

109 days

Calendar window

2026-07-04 → 2026-10-20.

Doesn't tell you: It says nothing about hours per day or how many agents ran at once. A busy day and a quiet day count the same.

snapshot

3

Follow-up branches

Opened after review or verify sent the first attempt back.

Doesn't tell you: It counts only a rejection that needed a whole new branch. Rework inside the same branch, before review, does not show here.

snapshot

41

"Fix …" commits on main

Commit subjects on main starting with the word "Fix".

Doesn't tell you: It counts only commits whose subject starts with the word "Fix". A defect fixed under a different subject line, or folded into a larger commit, does not show in this count.

live

88

Published knowledge entries

Computed live at build time, not from the snapshot below.

Doesn't tell you: It counts insights, case studies, comparisons, patterns, failure modes, glossary terms and tools. It does not count every page on the site. It does not count this page.

curated

13

Build-time invariants added after a break

Named in full below, section 03.

Doesn't tell you: This is a curated count. It includes only checks with a traceable incident behind them. It excludes the rest of the test suite.

02: What the gates caught

12 defects, and what stopped each one. #

Every SHA below is evidence text, not a link. The repository is private.

Shipped
Nine product pages published a "what it's built with" stack list for every product. The lists named tools such as TypeScript, Postgres and Terraform. Nothing in the repo documented the real technology choices behind any of the nine products. The lists were inferred and nobody had confirmed them.
Caught by
Review stage
Root cause
Review flagged the claims as checkable. A check then confirmed them wrong.
Guard added
`stack` became an optional field on the Product interface. The team left it unset on all nine products. The page now renders a "What it's built with" section only when the field holds data. When someone confirms a real stack, the section returns with no code change.
Shipped
26 identical, blank Open Graph social cards.
Caught by
Human inspection after deploy
Root cause
renderSvgToPng() passed a fontBuffers option. @resvg/resvg-js@2.6.2 does not support that option. Its README documents the option, but its type definitions omit it. Options cross the NAPI boundary as one JSON-stringified blob. The one unknown key made resvg discard the whole font object, so resvg used system fonts. A dev machine with system fonts hid the fault. On the CI build image, which has no fonts, every line of text disappeared.
Guard added
A byte-hash uniqueness check in verify-seo.mjs fails the build when two OG images render to identical bytes. The fix also wired npm test into the CI build job. The tests had never run there.
Shipped
A new light theme drew its focus ring in the lime --accent token. The dark theme uses the same value. The fault affected every focusable element on every page, in the theme that had just shipped.
Caught by
Verify stage, after merge
Root cause
Every focusable element on every page drew its focus indicator in the lime --accent token. That token has 17:1 contrast on the dark theme. It has 1.08:1 on the new paper-colored light theme. In light mode, no focusable element on any page had a visible indicator.
Guard added
A new --focus-ring token keeps the lime on dark. On light it uses a darker olive, tuned to clear 3:1 against every surface. verify-theme.mjs now enforces a non-text 3:1 contrast floor. It also runs a grep that rejects var(--accent) inside any :focus rule. Axe-core does not check focus-ring contrast.
Shipped
Category filter chips on the failure-modes field guide hid the matching cards but left the category headings. A filter on one category could leave five empty, ruled headings on the page. Two strings also shipped literal markdown backticks. They appeared on the page and in the emitted HowTo JSON-LD.
Caught by
Review stage
Root cause
The click handler toggled visibility on the cards only. It never touched the heading wrapper around them. The backticked strings were plain prose. Astro renders such text literally.
Guard added
The filter logic moved into one applyFilter() function. That function also hides an empty category heading and runs once on page load, so the default view matches a click on "All". TopicMember became a discriminated union on kind. This restores the compile-time guarantee that the date fields exist.
Shipped
The header search trigger navigated to /search/ and opened the command palette on top of the new page. One click produced two stacked search surfaces.
Caught by
Browser check after merge
Root cause
The cause was listener ordering. ClientRouter registers a document-level click listener at parse time. It bails only when preventDefault() has already run. The palette's own delegated handler registered later, in the bubble phase. ClientRouter therefore ran first and navigated before the palette could stop it.
Guard added
The delegated handler moved to { capture: true }. A capture-phase listener always runs before a bubble-phase listener, whatever the registration order. verify-seo.mjs now greps the source for that option. The README.md explains the reason.
Shipped
Nine product pages rendered their content sections as styled kicker spans. The spans looked like headings but had no heading structure. The page outline was one h1 and a single h2 from the FAQ component. Each page had nine content sections.
Caught by
Review stage
Root cause
These pages existed to fix this exact fault: no heading outline for a screen reader or crawler to navigate by.
Guard added
Every section now pairs its kicker with a real h2, as failure-modes/[slug].astro does. The outline is now one h1 plus ten h2s per page.
Shipped
A change gave the library panel a scrolling body. That body clipped a hover bridge positioned outside the scroll box. The bridge stayed in the CSS, but the pointer could no longer reach it. The panel then relied on a 300ms grace timer.
Caught by
Review stage
Root cause
The bridge lived on the panel itself. The panel now clipped anything outside its own padding box. The bridge height and the real gap had also drifted apart, from 4px to 16px.
Guard added
The bridge moved onto the non-scrolling trigger link. Its height and the panel offset now both resolve from one shared --library-panel-gap variable. verify-a11y.mjs asserts that the bridge is never a pseudo-element of the scrolling panel. It also asserts that both measurements still trace back to that variable.
Shipped
A pass at the copy gate deleted Oxford commas and recast lists after colons. It also rewrote conjunctions to move a triad out of the detector's view. No change shortened the list underneath.
Caught by
Review stage
Root cause
The gate matched one literal pattern. A rewrite that kept the same three items in a different shape passed clean. The gate did not check whether the list got shorter.
Guard added
The author redid the rewrite from scratch, item by item, against the real prose instead of the regex. verify-copy.mjs now rejects comma deletion and disguised conjunctions as evasions. A ratchet holds the new counts down, so a future pass can only shrink them.
Shipped
Two containers received min-width: 0. Their overflow-x: auto rule then engaged and made them live scroll regions on every architecture page. Neither container had a tabindex, so a keyboard user could not reach either one.
Caught by
Review stage
Root cause
A scroll container that cannot shrink never scrolls. The earlier accessibility sweep therefore found no live region there. The overflow fix made the region real and exposed the gap.
Guard added
tabindex="0" now ships on every affected container and on every fenced code block. A new check derives live scroll regions straight from the CSS. Any selector that can shrink and sets overflow-x must carry tabindex="0" on every matching element in the built HTML.
Shipped
A build-time check assumed 6.5px per character for a lane label. A label rendered wider than that assumption allowed. It overflowed past its lane, and the check reported no fault.
Caught by
Review stage
Root cause
The constant was a guess. Nobody had checked it against the real label face. A 29-character label measured 217px in a 210px lane, and the guard judged it narrow enough.
Guard added
The constant now comes from a measurement of the rendered text: 7.5px per character. The corrected guard found four labels over budget. The team shortened each one.
Shipped
A pass at trimming products.ts deleted concrete claims. One cut broke a FAQ answer. The file stayed well under its own triad allowance throughout.
Caught by
Review stage
Root cause
Cutting a real fact and cutting a decorative one lower the same count. Nothing separated a load-bearing claim from a headroom-buying one. A reviewer read each cut against its product page and found the difference.
Guard added
Every fact came back. The ratchet baseline now locks in the restored counts. products.ts cannot fall below them unless a reviewer approves the drop line by line.
Shipped
261 of 408 permalink targets resolved a scroll offset of zero. A reader who followed one of those links landed with the heading hidden under the strip, or scrolled past it.
Caught by
Review stage
Root cause
The offset rule matched only one heading shape: a direct child of <main>. Every id that this layer added sat one level deeper. Most of them never received the offset.
Guard added
The offset now rides on an explicit attribute. The exact element that each permalink points at carries it, whatever its depth. verify-seo.mjs asserts that the attribute and its offset rule still exist. It also asserts that every permalink target carries the attribute.
03: Every bug became an invariant

13 checks, each one because something broke. #

Named with the file that enforces it, and the exact failure it now makes impossible to reintroduce.

OG image byte-hash uniqueness

scripts/verify-seo.mjs

A social-card render that collapses to the same blank PNG for every route. The URL-uniqueness check alone would have passed while every card was byte-identical.

Non-text 3:1 contrast floor on --focus-ring

scripts/verify-theme.mjs

A focus indicator that clears contrast on dark but fails it on light, or on any future surface, and ships unnoticed. Axe-core has no check for this.

Capture-phase listener grep on the search trigger

scripts/verify-seo.mjs

The header search anchor that navigates to /search/ and opens the command palette at the same time. This is a runtime race. The built HTML has nothing else to assert against.

Text-contrast floor (4.5:1) across every token pair

scripts/verify-theme.mjs

A text color that fails WCAG AA against its own surface. The check began as the fix for a --text-dim-as-text regression (2.85:1). It now covers every text-and-surface pairing in both themes.

Search-index coverage invariant

src/lib/search-index.ts

A published page or entry that site search cannot find. The check fixed a 39-route hole that nobody had noticed across case studies, glossary, topics, tools, pricing and the diagnostic pages.

Inline document-weight ceiling

scripts/verify-seo.mjs

Large content that gets inlined into every page again. The search index once added roughly 47 KB to the HTML of every page, before it moved to a static endpoint.

Search-index anchor resolution

scripts/verify-seo.mjs

A search result that promises a specific row and lands the reader at the top of the page. Every record whose URL carries a "#" must match a real id in the built HTML. Review rejected this page for missing that check.

Typo and alias recovery fixtures

scripts/verify-search.mjs

A real query that returns zero hits because of a typo or an industry alias in place of the record's own wording. Every alias also has an integrity check. A renamed or removed target fails the build.

External source register coverage

scripts/verify-sources.mjs

A guide that cites only its own site as evidence for a claim that needs an outside source. The fix added a reference layer. Every guide now has a real, named citation from outside the site.

Markdown-twin coverage

scripts/verify-md.mjs

A page that ships with no plain-text Markdown counterpart for a crawler or an LLM to read. The check closed the gap where llms-full.txt carried only a quarter of the site.

Shrinkable scroll regions must be keyboard-reachable

scripts/verify-responsive.mjs

A container that gains real horizontal scroll once it can shrink, with no tabindex added in the same fix. The check derives its rule from the CSS. Any selector that can shrink and sets overflow-x must carry tabindex="0" on every element it matches.

Reading-scale snapshot ratchet

scripts/reading-scale.mjs

A published read-time badge that drifts from the page's real word count after a later content edit. It also covers a page that never carried a badge.

Rule-of-three density ratchet

scripts/verify-copy.mjs

A fixed rule-of-three tell that creeps back into one file at a time. It also blocks a rewrite that satisfies the gate by reshaping a triad. The rewrite must cut or split the list.

04: Before you copy this

What this doesn't prove. #

Read this section before the numbers above, not after.

Read the model this evidences

The Agentic Delivery Playbook is the 22-practice operating model behind every task on this page. CodeHerder is the product that runs it day to day.

Questions

05: Before you ask.#

What people ask us about publishing this.

01 Did agents write all of it?

Agents wrote most of the code. Plan, code, and several rounds of review and verify run as agent stages. Of the 12 commits above, 6 carry the agent's own author name. 6 carry a human one. The record draws from both.

Link to this answer: Did agents write all of it?
02 Doesn't this prove agents need heavy supervision?

Yes, and that is the point. A gate caught every one of the 12 incidents on this page. The agent that wrote the code did not catch its own mistake. Without the gates, this page would list 12 things that shipped broken.

Link to this answer: Doesn't this prove agents need heavy supervision?
03 Why publish your own bugs?

A claim about how well a delivery model catches defects needs real defects to support it. This page gives 12 real incidents. Each one has its root cause and the guard that came out of it. This is the strongest evidence we have for the operating model on /agentic-delivery/.

Link to this answer: Why publish your own bugs?
04 Can we see the repo?

No. The repo is private. The short SHAs on this page are evidence text. They are not links, and nothing here resolves to a URL you can open.

Link to this answer: Can we see the repo?
05 Does this transfer to our codebase?

The pattern transfers. The numbers do not. The transferable part is one task per branch, a fixed stage sequence, and a review stage that looks for problems. See the practices that this page links to on /agentic-delivery/. Our count of merged tasks and follow-up branches applies to this repository only.

Link to this answer: Does this transfer to our codebase?

Source: https://customlabs.io/delivery-record/

↑↓ navigate ↵ select esc close