From c8ee606f57eb5bddb815e272a59aed8b68e0c623 Mon Sep 17 00:00:00 2001 From: Ryan Workman Date: Mon, 15 Jun 2026 21:01:39 -0600 Subject: [PATCH] Add series support and publish "ai didn't break the laws of physics" # Problem The build pipeline had no way to express ordered multi-part series. Posts inside a theme folder all sorted newest-first with no concept of reading order, and there was no mechanism for a theme to surface a human-readable series title or per-post part numbers. # Solution Extended `build.js` to recognize a theme folder as a "series" when every post in it declares an integer `part` frontmatter field. Series themes sort ascending by part number instead of descending by date, use the `series` frontmatter string as their section heading on the homepage and listing page, and render a small "Part N" kicker above each post title in every listing context. A helper `isSeriesPosts` drives all of this so the logic stays in one place. The homepage sort also promotes series sections above regular topic buckets so a curated reading path sits above unordered tag groupings. Added a `.post-part` CSS rule to render the kicker as a small, muted, uppercase label that sits cleanly above the post title without competing with it. With the infrastructure in place, published the first post in the "understanding clients in the age of ai" series: "ai didn't break the laws of physics," which covers the Entity Knowledge Base Engine rebuild at Table22, the scope-cutting discipline that made it shippable in a week, and why de-risking in throwaway prototypes was the real prerequisite for committing to permanent architecture. # Notes The series heading on the listing page and homepage is pulled from `posts[0].series`, so all posts in a series folder must carry the same `series` value. A mismatch won't error today; it will silently use whatever the first (lowest part number) post declares. --- ...6-15-ai-didnt-break-the-laws-of-physics.md | 48 ++++ build.js | 94 ++++++- css/style.css | 10 + index.html | 23 ++ ...15-ai-didnt-break-the-laws-of-physics.html | 248 ++++++++++++++++++ understanding-clients/index.html | 110 ++++++++ 6 files changed, 522 insertions(+), 11 deletions(-) create mode 100644 _posts/understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.md create mode 100644 understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.html create mode 100644 understanding-clients/index.html diff --git a/_posts/understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.md b/_posts/understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.md new file mode 100644 index 0000000..ec4bc50 --- /dev/null +++ b/_posts/understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.md @@ -0,0 +1,48 @@ +--- +title: ai didn't break the laws of physics +date: 2026-06-15 +description: a six-person engineering department kept a five-year-old revenue product alive while two of us stood up its ai-native replacement, and the lesson wasn't a clever model, it was discipline +tags: [ai, agentic-development, legacy-systems, team, architecture] +series: understanding clients in the age of ai +part: 1 +--- + +At Table22 we've been pouring time into using the latest LLMs to understand our partners better. On the engineering side, I've been building out our onboarding pipeline, which takes a single business URL and turns it into a full brand identity and a sample offering in a couple of minutes. It captures a partner's voice and tone, their vertical, their target audience, and their key differentiators in less time than it takes me to pour a cup of coffee. Architecting that pipeline has fundamentally shifted how I think about AI, well beyond our own product. + +I'm going to write a whole series on that pipeline, and on how the patterns behind each piece have worked their way into my everyday routines. First, though, let's walk the high-level shape of it. + +## background + +Table22 is a subscription platform for food, beverage, and specialty retail businesses. We handle everything from signup to fulfillment so owners can focus on what they're actually good at, which is giving their guests the best possible experience. The app is legacy. I mean real legacy, compounding decisions since 2021. + +Saddled with all that debt but small enough to pivot on a dime, we decided we could rebuild it. Not just the app, though. We decided to rebuild the foundational pillar the whole company runs on. In early spring we pushed the first commit of a new AI-powered, composable, evolving knowledge base, one that could let any Table22 employee become an expert on any partner we work with. + +## the goal + +There's a Jack Dorsey idea I keep coming back to, the [hub-and-spoke model](https://block.xyz/inside/from-hierarchy-to-intelligence). One person, one set of hands, plugged into several efforts at once, the work radiating out from a small center instead of trickling down a big org chart. We designed the **Entity Knowledge Base Engine** to be that center: a single point of evolving _knowledge_ that any app, AI agent, throwaway experiment, or person could plug into at any time. + +The engine's first job was to feed that onboarding pipeline, which had started life as a proof-of-concept stitched together across a handful of SaaS platforms. We had to codify those pieces and rebuild the pipeline as something that could expand and adapt as fast as the ground was moving under us. + +And the engine was the linchpin. Get it wrong and the whole pipeline falls over. Make it too rigid and we'd be right back here in a few months, rebuilding from scratch. The values it produced had to generate marketing materials _and_ build a sample offering _and_ extrapolate winning patterns for prospective partners, all from the same structured knowledge. That forced a shift in _how_ we thought about the whole thing. The engine was never going to be a brand-ingestion step. It was going to be the foundation. + +## where ai wasn't the magic + +This engine is the future of Table22, or at least the bet on it. The whole pivot, the entire reason we took on the monumental job of rebuilding the app, hinged on getting it right. Our entire engineering department is six people, and all of them were already busy keeping the legacy revenue product alive. The first iteration of the engine fell to two of us. I didn't have months. I didn't even have weeks. The deadline was one week. My one teammate on it is genuinely _incredible_, but two people are still two people. AI doesn't break the laws of physics, and it doesn't stop time. + +We wrote the proposal assuming each of us would be driving around four AI agents across separate git worktrees at any given time. The math looks incredible on paper. If each agent gives you six to eight times the throughput on routine implementation, four of them is twenty-four to thirty-two times. Rewrite the whole thing by Friday. + +We wrote that math down, and then we wrote down why it was a lie + +Our honest internal estimate put shipping the full spec in a week at single-digit-percent odds. I literally asked Claude to [crunch those numbers again](https://youtu.be/Xblkk5cEx3U?si=r5OhVqg7WJJuw1Hc&t=225). Sadly, the answer didn't change. The locked architecture was eight to twelve weeks of work for two engineers at an honest pace. Agents compress implementation, and that part is real. What they don't compress is everything that actually gates the work: the architectural decisions, the humans reviewing the code, the SDK integrations, the prompt authoring. + +So we did the only thing that actually works under that constraint. We cut scope. We shipped a deliberately minimal version of the loop, and we wrote down everything we left out: twenty-three items of known debt, each one named. All the first version had to do was prove the engine was worth investing in. + +That's the actual lesson, and it's the opposite of magic-thinking. AI agents let a tiny team move at a velocity that used to take a whole department. But the move wasn't a clever model. The move was discipline: knowing the bottleneck was integration and judgment rather than typing, and then shrinking the scope to fit that bottleneck instead of pretending it wasn't there. + +## where it actually paid off + +Before any of this was a product, it was a pile of throwaway prototypes, each one proving a single primitive. A service that took a URL and pulled a brand's voice, logo, and visual tokens out of it. A separate one that generated product pages. A scrappy single-page app that was really just a research spike with a frontend bolted on. Each answered one narrow question: can the AI actually do this specific extraction reliably enough to bet on it. The rebuild didn't invent any of those capabilities. It centralized the ones that had already passed the test. + +That's the part I'd push any leader reading this to actually sit with. The reason a six-person engineering department could put AI at the structural core of a rebuild isn't that AI suddenly got easy. It's that we de-risked it in disposable prototypes first, where being wrong was cheap, and only committed to permanent architecture once we knew which primitives held up. The rebuild was the payoff of a dozen small, ugly, fast experiments. + +And "at the core" is literal, not a slogan. The engine is a real, separable layer with its own database. Text goes in, structured brand knowledge comes out, versioned, with its history preserved. Other AI surfaces in the app read from it and build on top of it without ever reaching inside. That's not a chatbot stapled to a CRUD app. That's AI as a foundation. How it works, and why we built it as structured knowledge instead of reaching for a vector store, is the next post diff --git a/build.js b/build.js index 2dd7682..df6116e 100644 --- a/build.js +++ b/build.js @@ -203,6 +203,25 @@ function loadPostsForTheme(themeSlug) { ? fm.description.trim() : null; + // Series support: a theme folder is treated as a "series" when every post + // in it carries an integer `part`. `series` is the human-readable display + // title used for the section heading and landing page. Both are optional; + // a theme with no `part` fields keeps the default date-descending behavior. + const series = + typeof fm.series === "string" && fm.series.trim() + ? fm.series.trim() + : null; + let part = null; + if (fm.part != null) { + if (typeof fm.part === "number" && Number.isInteger(fm.part)) { + part = fm.part; + } else { + die( + `invalid 'part' in frontmatter (expected an integer): _posts/${themeSlug}/${entry.name}`, + ); + } + } + posts.push({ themeSlug, themeDisplay: titleCase(themeSlug), @@ -214,6 +233,8 @@ function loadPostsForTheme(themeSlug) { title: fm.title.trim(), description, tags, + series, + part, bodyMarkdown: parsed.content, }); } @@ -226,9 +247,27 @@ function collectAllPosts() { let total = 0; for (const theme of themes) { const posts = loadPostsForTheme(theme); - posts.sort((a, b) => - a.date < b.date ? 1 : a.date > b.date ? -1 : a.slug.localeCompare(b.slug), - ); + if (isSeriesPosts(posts)) { + // A series reads intro-first, so order by part ascending (tiebreak on + // date then slug) rather than the newest-first default below. + posts.sort( + (a, b) => + a.part - b.part || + (a.date < b.date + ? -1 + : a.date > b.date + ? 1 + : a.slug.localeCompare(b.slug)), + ); + } else { + posts.sort((a, b) => + a.date < b.date + ? 1 + : a.date > b.date + ? -1 + : a.slug.localeCompare(b.slug), + ); + } if (posts.length > 0) { byTheme.set(theme, posts); total += posts.length; @@ -275,6 +314,26 @@ function canonicalUrlForTheme(themeSlug) { return `${SITE_ORIGIN}/${themeSlug}/`; } +// A theme folder is a "series" when every post in it declares an integer part. +function isSeriesPosts(posts) { + return posts.length > 0 && posts.every((p) => p.part != null); +} + +// Section/landing heading for a theme: the series display title when it's a +// series, otherwise the title-cased folder name. +function sectionTitleFor(themeSlug, posts) { + return isSeriesPosts(posts) && posts[0].series + ? posts[0].series + : titleCase(themeSlug); +} + +// "Part N" kicker for a post-item, only inside a series listing. +function partLabel(post, inSeries) { + return inSeries && post.part != null + ? `\n Part ${post.part}` + : ""; +} + function renderPost(template, post) { const description = post.description || DEFAULT_DESCRIPTION; const content = renderPostBody(post); @@ -293,18 +352,22 @@ function renderPost(template, post) { } function renderThemeListing(template, themeSlug, posts) { - const themeDisplay = titleCase(themeSlug); - const description = `${themeDisplay} writing by Ryan Workman`; + const inSeries = isSeriesPosts(posts); + const heading = sectionTitleFor(themeSlug, posts); + const description = inSeries + ? `the "${heading}" series by Ryan Workman` + : `${heading} writing by Ryan Workman`; const items = posts .map((p) => { const href = `/${p.themeSlug}/${p.filename}`; + const partLine = partLabel(p, inSeries); const tagLine = p.tags.length > 0 ? `\n ${renderTagPills(p.tags)}` : ""; const desc = p.description ? `\n

${escapeHtml(p.description)}

` : ""; return ( - `
  • \n` + + `
  • ${partLine}\n` + ` ${escapeHtml( p.title, )}\n` + @@ -320,7 +383,7 @@ function renderThemeListing(template, themeSlug, posts) { ? `

    No posts yet.

    ` : ` `; return applyTemplate(template, { - theme_display: escapeHtml(themeDisplay), + theme_display: escapeHtml(heading), description: escapeAttr(description), canonical_url: escapeAttr(canonicalUrlForTheme(themeSlug)), post_list: postList, @@ -331,17 +394,26 @@ function renderHomepagePostList(byTheme) { if (byTheme.size === 0) { return `

    New posts coming soon.

    `; } - const themeNames = Array.from(byTheme.keys()).sort(); + // Series sections float above topic themes (a curated reading path outranks a + // topic bucket); alphabetical within each group. + const themeNames = Array.from(byTheme.keys()).sort((a, b) => { + const aSeries = isSeriesPosts(byTheme.get(a)); + const bSeries = isSeriesPosts(byTheme.get(b)); + if (aSeries !== bSeries) return aSeries ? -1 : 1; + return a.localeCompare(b); + }); const sections = themeNames.map((themeSlug) => { const posts = byTheme.get(themeSlug); - const themeDisplay = titleCase(themeSlug); + const inSeries = isSeriesPosts(posts); + const heading = sectionTitleFor(themeSlug, posts); const items = posts .map((p) => { const href = `/${p.themeSlug}/${p.filename}`; + const partLine = partLabel(p, inSeries); const tagLine = p.tags.length > 0 ? `\n ${renderTagPills(p.tags)}` : ""; return ( - `
  • \n` + + `
  • ${partLine}\n` + ` ${escapeHtml( p.title, )}\n` + @@ -354,7 +426,7 @@ function renderHomepagePostList(byTheme) { .join("\n"); return ( `

    ${escapeHtml( - themeDisplay, + heading, )}

    \n` + ` ` ); }); diff --git a/css/style.css b/css/style.css index f18647e..1646535 100644 --- a/css/style.css +++ b/css/style.css @@ -176,6 +176,16 @@ pre { border-bottom: 1px solid var(--color-divider); } +.post-part { + display: block; + margin-bottom: var(--space-1); + font-size: 0.72rem; + font-weight: var(--weight-medium); + text-transform: uppercase; + letter-spacing: 0.07em; + opacity: var(--muted-opacity); +} + .post-item:last-child { border-bottom: 0; } diff --git a/index.html b/index.html index 06bc8ae..6bfb084 100644 --- a/index.html +++ b/index.html @@ -56,6 +56,29 @@

    let's chat about tech

    +

    + understanding clients in the age of ai +

    +

    Engineering

    diff --git a/understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.html b/understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.html new file mode 100644 index 0000000..273fae0 --- /dev/null +++ b/understanding-clients/2026-06-15-ai-didnt-break-the-laws-of-physics.html @@ -0,0 +1,248 @@ + + + + + + + workman.tech — ai didn't break the laws of physics + + + + + + + + + + + + + + + + + +
    +

    workman.tech 👋🏻 hello world

    +
    +

    + <- Back +

    + +

    + ai didn't break the laws of physics
    + +

    + +

    + Understanding Clients · + aiagentic-developmentlegacy-systemsteamarchitecture +

    + +
    +

    + At Table22 we’ve been pouring time into using the latest LLMs to + understand our partners better. On the engineering side, I’ve been + building out our onboarding pipeline, which takes a single business + URL and turns it into a full brand identity and a sample offering in + a couple of minutes. It captures a partner’s voice and tone, their + vertical, their target audience, and their key differentiators in + less time than it takes me to pour a cup of coffee. Architecting + that pipeline has fundamentally shifted how I think about AI, well + beyond our own product. +

    +

    + I’m going to write a whole series on that pipeline, and on how the + patterns behind each piece have worked their way into my everyday + routines. First, though, let’s walk the high-level shape of it. +

    +

    background

    +

    + Table22 is a subscription platform for food, beverage, and specialty + retail businesses. We handle everything from signup to fulfillment + so owners can focus on what they’re actually good at, which is + giving their guests the best possible experience. The app is legacy. + I mean real legacy, compounding decisions since 2021. +

    +

    + Saddled with all that debt but small enough to pivot on a dime, we + decided we could rebuild it. Not just the app, though. We decided to + rebuild the foundational pillar the whole company runs on. In early + spring we pushed the first commit of a new AI-powered, composable, + evolving knowledge base, one that could let any Table22 employee + become an expert on any partner we work with. +

    +

    the goal

    +

    + There’s a Jack Dorsey idea I keep coming back to, the + hub-and-spoke model. One person, one set of hands, plugged into several efforts at + once, the work radiating out from a small center instead of + trickling down a big org chart. We designed the + Entity Knowledge Base Engine to be that center: a + single point of evolving knowledge that any app, AI agent, + throwaway experiment, or person could plug into at any time. +

    +

    + The engine’s first job was to feed that onboarding pipeline, which + had started life as a proof-of-concept stitched together across a + handful of SaaS platforms. We had to codify those pieces and rebuild + the pipeline as something that could expand and adapt as fast as the + ground was moving under us. +

    +

    + And the engine was the linchpin. Get it wrong and the whole pipeline + falls over. Make it too rigid and we’d be right back here in a few + months, rebuilding from scratch. The values it produced had to + generate marketing materials and build a sample offering + and extrapolate winning patterns for prospective partners, + all from the same structured knowledge. That forced a shift in + how we thought about the whole thing. The engine was never + going to be a brand-ingestion step. It was going to be the + foundation. +

    +

    where ai wasn’t the magic

    +

    + This engine is the future of Table22, or at least the bet on it. The + whole pivot, the entire reason we took on the monumental job of + rebuilding the app, hinged on getting it right. Our entire + engineering department is six people, and all of them were already + busy keeping the legacy revenue product alive. The first iteration + of the engine fell to two of us. I didn’t have months. I didn’t even + have weeks. The deadline was one week. My one teammate on it is + genuinely incredible, but two people are still two people. + AI doesn’t break the laws of physics, and it doesn’t stop time. +

    +

    + We wrote the proposal assuming each of us would be driving around + four AI agents across separate git worktrees at any given time. The + math looks incredible on paper. If each agent gives you six to eight + times the throughput on routine implementation, four of them is + twenty-four to thirty-two times. Rewrite the whole thing by Friday. +

    +

    + We wrote that math down, and then we wrote down why it was a lie +

    +

    + Our honest internal estimate put shipping the full spec in a week at + single-digit-percent odds. I literally asked Claude to + crunch those numbers again. Sadly, the answer didn’t change. The locked architecture was + eight to twelve weeks of work for two engineers at an honest pace. + Agents compress implementation, and that part is real. What they + don’t compress is everything that actually gates the work: the + architectural decisions, the humans reviewing the code, the SDK + integrations, the prompt authoring. +

    +

    + So we did the only thing that actually works under that constraint. + We cut scope. We shipped a deliberately minimal version of the loop, + and we wrote down everything we left out: twenty-three items of + known debt, each one named. All the first version had to do was + prove the engine was worth investing in. +

    +

    + That’s the actual lesson, and it’s the opposite of magic-thinking. + AI agents let a tiny team move at a velocity that used to take a + whole department. But the move wasn’t a clever model. The move was + discipline: knowing the bottleneck was integration and judgment + rather than typing, and then shrinking the scope to fit that + bottleneck instead of pretending it wasn’t there. +

    +

    where it actually paid off

    +

    + Before any of this was a product, it was a pile of throwaway + prototypes, each one proving a single primitive. A service that took + a URL and pulled a brand’s voice, logo, and visual tokens out of it. + A separate one that generated product pages. A scrappy single-page + app that was really just a research spike with a frontend bolted on. + Each answered one narrow question: can the AI actually do this + specific extraction reliably enough to bet on it. The rebuild didn’t + invent any of those capabilities. It centralized the ones that had + already passed the test. +

    +

    + That’s the part I’d push any leader reading this to actually sit + with. The reason a six-person engineering department could put AI at + the structural core of a rebuild isn’t that AI suddenly got easy. + It’s that we de-risked it in disposable prototypes first, where + being wrong was cheap, and only committed to permanent architecture + once we knew which primitives held up. The rebuild was the payoff of + a dozen small, ugly, fast experiments. +

    +

    + And “at the core” is literal, not a slogan. The engine is a real, + separable layer with its own database. Text goes in, structured + brand knowledge comes out, versioned, with its history preserved. + Other AI surfaces in the app read from it and build on top of it + without ever reaching inside. That’s not a chatbot stapled to a CRUD + app. That’s AI as a foundation. How it works, and why we built it as + structured knowledge instead of reaching for a vector store, is the + next post +

    +
    +
    +
    + +
    + © ryan workman - 2026 +
    + + hosted by + github pages + +
    + + diff --git a/understanding-clients/index.html b/understanding-clients/index.html new file mode 100644 index 0000000..b07803f --- /dev/null +++ b/understanding-clients/index.html @@ -0,0 +1,110 @@ + + + + + + + workman.tech — understanding clients in the age of ai + + + + + + + + + + + + + + + + + +
    +

    workman.tech 👋🏻 hello world

    +
    +

    + <- Back +

    + +

    understanding clients in the age of ai

    + +
    +
      +
    • + Part 1 + ai didn't break the laws of physics + +

      + a six-person engineering department kept a five-year-old revenue + product alive while two of us stood up its ai-native + replacement, and the lesson wasn't a clever model, it was + discipline +

      + aiagentic-developmentlegacy-systemsteamarchitecture +
    • +
    +
    +
    +
    + + + +