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. 163 of those tasks merged in 63 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-09-05, at commit 99e817f.

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

163

Distinct tasks merged

173 merge commits from task branches, total.

Doesn't tell you: Fewer than the merge-commit count above it, because some tasks merged into main more than once: 3 as a suffixed follow-up branch after review or verify rejected the first attempt, and 7 as a second merge from the same branch.

snapshot

63 days

Calendar window

2026-07-04 → 2026-09-04.

Doesn't tell you: Says nothing about hours per day or how many agents ran concurrently — a busy day and a quiet one count the same.

snapshot

3

Follow-up branches

Opened after review or verify sent the first attempt back.

Doesn't tell you: A lower bound, not a defect count — this only counts a rejection that needed a whole new branch. Rework inside the same branch, before it ever reached review, is invisible here.

snapshot

35

"Fix …" commits on main

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

Doesn't tell you: Counts only commits whose subject literally starts with the word "Fix" — a defect fixed under a different subject line, or folded into a larger commit, doesn't show up in this count.

live

87

Published knowledge entries

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

Doesn't tell you: Counts insights, case studies, comparisons, patterns, failure modes, glossary terms, and tools — not every page on the site, and not this page either.

curated

13

Build-time invariants added after a break

Named in full below, section 03.

Doesn't tell you: A curated count, not an exhaustive one — only checks with a traceable incident behind them are counted, not 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 — TypeScript, Postgres, Terraform, and so on — for every product. Nothing else in the repo documented the real technology choices behind any of the nine; the lists were inferred, not confirmed.
Caught by
Review stage
Root cause
The claims were checkable, and once checked, they were wrong.
Guard added
`stack` became an optional field on the Product interface and was unset on all nine products; the page now renders a "What it's built with" section only when the field is actually populated, so confirming a real stack later brings the section back 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 that @resvg/resvg-js@2.6.2 doesn't actually support — documented in the library's README, missing from its type definitions. Options cross the NAPI boundary as one JSON-stringified blob, so the single unknown key silently discarded the whole font object and resvg fell back to system fonts. Invisible on a dev machine with system fonts installed; every line of text dropped on the font-less CI build image.
Guard added
A byte-hash uniqueness check in verify-seo.mjs that fails the build if any two OG images render to identical bytes, plus wiring npm test into the CI build job — it had never actually run there.
Shipped
A new light theme whose focus ring was the lime --accent token, the same value the dark theme uses. Every focusable element on every page was affected, 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: 17:1 contrast on the dark theme, 1.08:1 on the new paper-colored light theme. Not a weak indicator — no indicator, on every focusable element, on every page, in light mode.
Guard added
A new --focus-ring token (unchanged lime on dark, a darker olive on light tuned to clear 3:1 against every surface); a non-text 3:1 contrast floor plus a grep rejecting var(--accent) inside any :focus rule in verify-theme.mjs — axe-core does not check focus-ring contrast at all.
Shipped
Category filter chips on the failure-modes field guide hid the matching cards but left their category headings behind — filtering to one category could leave five empty, ruled headings on the page. Two strings also shipped literal markdown backticks onto the page and into the emitted HowTo JSON-LD.
Caught by
Review stage
Root cause
The click handler toggled visibility on the cards only, never on the heading wrapper around them; the backticked strings were plain prose Astro renders as literal text, not markdown.
Guard added
The filter logic moved into one applyFilter() function that 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 to restore the compile-time guarantee that date fields are actually present.
Shipped
The header search trigger both navigated to /search/ and opened the command palette on top of the new page — two stacked search surfaces from one click.
Caught by
Browser check after merge
Root cause
Listener ordering, not the anchor. ClientRouter registers a document-level click listener at parse time and only bails if preventDefault() was already called; the palette's own delegated handler registered later, in bubble phase, so ClientRouter ran first and navigated before the palette could stop it.
Guard added
The delegated handler moved to { capture: true }, which always runs before a bubble-phase listener regardless of registration order; verify-seo.mjs now greps the source for that option, and the why is written into README.md.
Shipped
Nine product pages rendered their content sections as styled kicker spans — visually a heading, structurally nothing. Page outline was one h1 and a single h2 from the FAQ component, across nine content sections per page.
Caught by
Review stage
Root cause
The exact failure these pages existed to fix: no heading outline for a screen reader or crawler to navigate by.
Guard added
Every section now pairs its kicker with a real h2, the same way failure-modes/[slug].astro does; outline is now one h1 plus ten h2s per page.
Shipped
A change that gave the library panel a scrolling body clipped a hover bridge positioned outside that scroll box. The bridge stayed in the CSS, but the pointer could no longer reach it. The panel then depended on a 300ms grace timer instead.
Caught by
Review stage
Root cause
The bridge lived on the panel itself, and the panel now clipped anything outside its own padding box. Its height and the real gap it had to cover had also drifted apart, from 4px to 16px.
Guard added
The bridge moved onto the non-scrolling trigger link, and its height and the panel offset now both resolve from one shared --library-panel-gap variable. verify-a11y.mjs asserts the bridge is never a pseudo-element of the scrolling panel, and that both measurements still trace back to that one 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. None of it actually shortened the list underneath.
Caught by
Review stage
Root cause
The gate matched one literal pattern. Any rewrite that kept the same three items in a different shape passed clean, whether or not the underlying list actually got shorter.
Guard added
The rewrite was redone from scratch, item by item, against the real prose instead of the regex. verify-copy.mjs now names comma deletion and disguised conjunctions as evasions, never as fixes. A ratchet holds the new counts down, so a future pass can only shrink them further.
Shipped
Giving two containers min-width: 0 made their overflow-x: auto rule finally engage, turning them into live scroll regions on every architecture page. Neither one carried a tabindex, so a keyboard user could not reach either.
Caught by
Review stage
Root cause
A scroll container that cannot shrink never actually scrolls, so the earlier accessibility sweep never saw a live region there. Fixing the overflow 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 said nothing.
Caught by
Review stage
Root cause
The constant was a guess, never checked against the label's real face. A 29-character label measured 217px in a 210px lane and the guard read it as narrow enough.
Guard added
The constant now comes from a real measurement of the rendered text: 7.5px per character. The corrected guard caught four labels genuinely over budget, and each one was shortened.
Shipped
A pass at trimming products.ts deleted concrete claims. One cut broke a FAQ answer outright, and the file sat well under its own triad allowance the whole time.
Caught by
Review stage
Root cause
Cutting a real fact and cutting a decorative one both lower the same count. Nothing separated a load-bearing claim from a headroom-buying one until a reviewer read each cut against its own product page.
Guard added
Every fact came back, and the ratchet baseline now locks in the restored counts. products.ts cannot fall below them again without a reviewer approving the drop line by line.
Shipped
261 of 408 permalink targets resolved a scroll offset of zero. A reader following 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 this layer actually added sat one level deeper, so most of them never got the offset at all.
Guard added
The offset now rides on an explicit attribute carried by the exact element each permalink points at, not on how deep that element sits. verify-seo.mjs asserts the attribute and its offset rule both still exist, and 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 silently collapsing 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 and fails it on light (or any future surface) shipping unnoticed — axe-core has no check for this at all.

Capture-phase listener grep on the search trigger

scripts/verify-seo.mjs

The header search anchor navigating to /search/ and opening the command palette on top of it at the same time — a runtime race with nothing in the built HTML to assert against otherwise.

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

scripts/verify-theme.mjs

A text color quietly failing WCAG AA against its own surface — the fix for a specific --text-dim-as-text regression (2.85:1), generalized to every text-and-surface pairing in both themes.

Search-index coverage invariant

src/lib/search-index.ts

A published page or entry with no way to find it through site search — the fix for a 39-route hole that had gone unnoticed across case studies, glossary, topics, tools, pricing, and the diagnostic pages.

Inline document-weight ceiling

scripts/verify-seo.mjs

Something large getting inlined into every page again — the search index used to add roughly 47 KB to every single page's HTML before it moved to a static endpoint.

Search-index anchor resolution

scripts/verify-seo.mjs

A search result promising a specific row and landing the reader at the top of the page instead. Every record whose URL carries a "#" must match a real id in the built HTML — the check this page itself was rejected for missing.

Typo and alias recovery fixtures

scripts/verify-search.mjs

A real query returning zero hits because it was misspelled or phrased with an industry alias instead of the record's own wording. Every alias also gets its own integrity check, so a renamed or removed target fails the build instead of quietly resolving to nothing.

External source register coverage

scripts/verify-sources.mjs

A guide citing only its own site as evidence for a claim that needs an outside source. Fixed the reference layer that gave every guide a real, named citation instead of a self-referential loop.

Markdown-twin coverage

scripts/verify-md.mjs

A page shipping with no plain-text Markdown counterpart for a crawler or an LLM to read. 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 alongside the fix. Derived straight 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 page's published read-time badge drifting from its actual word count after a later content edit, on a page that never carried one at all.

Rule-of-three density ratchet

scripts/verify-copy.mjs

A fixed rule-of-three tell creeping back in one file at a time. Also blocks a rewrite that satisfies the gate by reshaping a triad instead of genuinely cutting or splitting it.

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?

Most of the code, yes — 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 and 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's the point. Every one of the 12 incidents on this page was caught by a gate, not by the agent that wrote the code catching its own mistake. Take the gates away and this page is a list of 12 things that shipped broken instead of a list of 12 things a check caught.

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

Because a claim about how well a delivery model catches defects is only worth something if it names actual defects, not a hypothetical failure rate. 12 real incidents, with the real root cause and the real guard that came out of each one, 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 — it's private. The short SHAs on this page are evidence text, not links; 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 does; the numbers don't. One task per branch, a fixed stage sequence, and a review stage that actually looks for problems is the transferable part — see the practices this page links out to on /agentic-delivery/. Our own count of merged tasks and follow-up branches is specific to this repository.

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

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

navigate select esc close