ProductMay 9, 2026· 4 min read

    Tim Publishes Its Own Blog: How We're Eating Our Own Dogfood

    We just shipped the markdown loader and GitHub-repo destination. Every article from here on — including this one — flows through the same Tim Publishing pipeline we're building for customers.

    This is the first article published through Tim's new content pipeline. The infrastructure underneath it is the same infrastructure that will power customers' own blogs once we hit GA.

    What just shipped

    Three things landed today that together close the loop on automated publishing:

    1. Multi-tenant article generator (backend/scripts/generate_article.py) that takes a workspace ID, keyword, and optional cluster context and produces a quality-gated article via Gemini. Brand voice, audience persona, and topic context all come from per-workspace settings — Tim's own articles use Tim's voice, customer articles use theirs.

    2. GitHub-repo destination (backend/app/integrations/publish_destinations/github_repo.py) that commits the markdown file with YAML frontmatter, opens a PR on Tim's repo, and returns the PR URL as the preview. Azure Static Web Apps' built-in PR-preview feature gives every PR a deploy URL automatically — no extra wiring needed.

    3. Markdown loader (webapp/src/lib/blog-data.ts) that uses Vite's import.meta.glob to discover every .md file under webapp/src/content/blog/, parses the frontmatter with js-yaml, and renders the body with marked plus DOMPurify for HTML sanitization. The existing BlogArticle.tsx component renders the result with no visual change to what readers see — same italic display headline, same drop cap, same surface container, same tag chip.

    Why markdown, why git, why no CMS

    Three reasons the storage choice matters:

    Zero infrastructure cost. No headless CMS subscription. No database table. No admin UI to maintain. Git is the source of truth, and the existing GitHub + Azure SWA deploy pipeline picks up new articles on every merge to main.

    Version control built in. Every article edit is a commit. Every published version has a SHA. Rollback is git revert. This matters more at 10,000 articles than at 10.

    LLM-first publishing fits the model. When the "writer" is an LLM that opens PRs against a repo, the workflow already maps to git: branch, commit, PR, review, merge. A CMS would just be an unnecessary layer between Tim and the article. For workspaces with human writers who need a web admin, the same markdown source can be edited through TinaCMS or Decap CMS — but that's an opt-in layer, not a default cost.

    What's next

    The pipeline works end-to-end for Tim's own blog, but customer-facing publishing needs three more pieces:

    • Confirmation Loop integration so customers can approve drafts via Slack/Teams reply instead of merging GitHub PRs manually.
    • WordPress destination — the same PublishDestination interface, talking to the WordPress REST API via Pipedream-issued OAuth tokens. Adds the ~43% of the web that runs on WordPress.
    • LinkedIn syndication — the first SyndicationChannel. Once an article publishes, Tim fans out an excerpt + canonical link to the workspace's LinkedIn presence.

    Each of those is its own focused PR. The scaffolding makes them additive — no rewrites required.

    How to verify this article rendered correctly

    If you're reading this on gettim.co/blog/sample-tim-publishes-its-own-blog, the markdown loader works. The cover area, headline, tag, and date came from the YAML frontmatter at the top of this file. The body — including this paragraph — came from the markdown below the frontmatter, parsed by marked and sanitized by DOMPurify. The five FAQ pairs above will render as a structured FAQ section once the BlogArticle component picks them up from the frontmatter.

    This is the foundation. Every Tim-published article from here forward — for Tim, for customers — flows through this exact pipeline.

    Frequently asked

    Does this mean Tim wrote this article itself?expand_more

    This particular article was hand-written to verify the publishing pipeline. From the next article forward, Tim drafts the body via Gemini, opens a PR, and a human editor reviews and approves before merge.

    Where does the markdown live?expand_more

    Every article is a markdown file under webapp/src/content/blog/ in the Tim repo. Vite's import.meta.glob discovers them at build time and the BlogArticle component renders them with frontmatter-driven metadata.

    How does Tim know what to write about?expand_more

    Tim mines connected tools — Salesforce won/lost deals, Google Search Console impressions, recurring Slack questions, support inbox topics — to suggest article topics. Editors approve or override before generation.

    What happens if the LLM generates low-quality content?expand_more

    Every article passes through a quality gate: minimum 1,200 words for supporting articles (3,000 for pillar pages), at least 5 FAQ pairs, a 40–60 word definitive lead answer for featured-snippet capture, and at least one specific data point or named example. Articles that fail the gate are rejected before the PR opens.

    When will customers be able to use this?expand_more

    Tim's own blog is the dogfood validation phase. Customer-facing access goes live after the WordPress destination, LinkedIn syndication, and Confirmation Loop integration land — tracked in our Phase 0 plan.

    person
    Tim Engineering
    Drafted by Tim · Reviewed by editor

    Privacy

    We use cookies to improve your experience.

    Privacy Policy