Paul Welty, PhD AI, WORK, AND STAYING HUMAN

Polymathic blog - TODO and status

Next session (2026-02-01)

URGENT - Publishing streak at risk:

  • Publish 1-2 posts this weekend to maintain 5-week streak (3-day gap ahead)
  • Add tags to all 4 scheduled posts (enable strategic planning)
  • Schedule 2nd newsletter for mid-February (close 14-day gap)

High priority - Pipeline visibility:

  • Build Slate editorial widgets (integrate with ../slate framework)
    • Pipeline status widget (upcoming, backlog, issues)
    • Analytics widget (conversion tracking: posts→signups, newsletters→book clicks)
    • Content planning widget (which topics to write based on performance)
  • Integrate Google Analytics API (which essays drive signups by tag/category)
  • Integrate Brevo API (which newsletters drive book clicks)

High priority - Backlog cleanup:

  • Review 59 backlog drafts (past dates) - publish valuable ones, archive rest
  • Run /pipeline --archive-old on drafts >1 year (once implemented)
  • Process 14-day work log backlog per new policy (7-day review, 30-day archive)

Medium priority - Strategic execution:

  • Create ROADMAP.md for Q1 2026 (audience building theme)
  • Fix content_type inconsistency in scheduled posts (“essay” vs essay)
  • Schedule 2-3 more essays for February (reach 4-6/month target)

Completed (2026-01-31)

Strategic planning and product alignment:

  • Made PM decisions on all 8 open strategic questions from PRODUCT.md
  • Added 7 decision entries to DECISIONS.md with full rationale
  • Corrected PRODUCT.md audience prioritization (professionals navigating AI era as primary)
  • Updated success metrics to track book sales, social engagement, inbound opportunities

Pipeline tool implementation:

  • Built complete /pipeline skill with 8 command modes
  • Created Python analysis script (parses 600+ files, <5 second performance)
  • First pipeline analysis revealed: 123 drafts, 4 scheduled, 59 backlog, publishing streak at risk
  • Identified critical gaps: 14-day newsletter gap, no tags on scheduled posts, 3-day publication gap ahead

Roadmap planning (incomplete):

  • Started /pm-roadmap workflow, confronted gap between controllable inputs vs uncontrollable outcomes
  • Decided to integrate pipeline dashboard into existing Slate framework
  • Complete ROADMAP.md creation (deferred - need pipeline visibility first)

Completed (2026-01-30)

Product and requirements documentation:

  • Created PRODUCT.md (vision, principles, positioning, success metrics)
  • Created DECISIONS.md (15 documented decisions with rationale)
  • Created EDITORIAL_DASHBOARD_REQUIREMENTS.md (85KB comprehensive spec)
  • Researched editorial pipeline best practices (content marketing, newsrooms, agile)
  • Analyzed current implementation in utilities project
  • Specified morning report format with 6 sections and implementation phases

Pre-commit hook enhancement:

  • Extended hook to auto-generate social media tease posts for essays
  • Integrated with queuero for LinkedIn, Mastodon, Bluesky distribution
  • Idempotent design (checks for existing tease JSON)
  • Documented in CLAUDE.md

Completed (2026-01-29)

AI and authorship essays:

  • Wrote “The death of the author” essay (4,500 words) - comprehensive philosophical argument
  • Wrote “Dismissing AI content is prejudice” (1,200 words) - direct, provocative comparison
  • Wrote “We always panic about new tools” (1,100 words) - safe historical pattern piece
  • Created newsletter 260203 referencing historical pattern essay
  • All three staying draft - publication TBD based on risk/value assessment

Agent infrastructure:

  • Added .claude/agents symlink to shared utilities agents
  • Documented three custom agents in CLAUDE.md (blog-researcher, blog-writer, hugo-prep)
  • Updated VOICE.md with 207 lines of agent usage guidelines
  • Infrastructure ready for parallel research and content creation

Completed (2026-01-28)

Newsletter system and content:

  • Created newsletter 260127 “Files are permanent. Databases are not.”
  • Learned and documented that newsletter template requires feature_post (can’t inline content)
  • Created essay: Files are permanent. Databases are not. (content/posts/files-are-permanent.md)
  • Updated NEWSLETTER_WORKFLOW.md with Step 0 (content creation requirements)
  • Documented VOICE.md requirement and humanizer availability

Design and visual updates:

  • Updated newsletter template colors from old rust to medium brown (#7a3322)
  • Added “What I’m building” section to homepage (The Work of Being, Authexis, Textorium)
  • Fixed product cards to match site design (gray on white, work-card class)
  • Removed prose wrapper causing width constraint

Featured articles system:

  • Changed featured writing section from tag-based to metadata-based (featured: true)
  • Updated three posts as featured: Why customer tools are organized wrong, Busy is not a state, Infrastructure shapes thought
  • Makes featured status explicit rather than implicit via tags

Completed (2026-01-27)

Git hooks automation:

  • Extended pre-commit hook to support essay social teasers (paulos blog tease)
  • Added LinkedIn post generation automation (paulos blog linkedin)
  • Implemented duplicate detection via queuero check-duplicates
  • Commented out new automation pending command maturity
  • Documented infrastructure for future activation

Content and workflow:

  • Published Textorium App Store launch post
  • Received and distributed 3 work logs (Textorium, Queuero, Authexis)
  • Created polymathic-h work log documenting hook automation
  • Updated editorial calendar (119 events)

Completed (2026-01-26)

Git hooks and infrastructure:

  • Refined post-commit hook to only sync changed files to Notion
  • Added output messages to post-commit hook for visibility
  • Documented Notion sync in CLAUDE.md
  • Tested hook with dozen+ commits until behavior felt right

Queuero specification:

  • Created comprehensive technical spec (785 lines)
  • Designed database schemas, CLI commands, platform integrations
  • Renamed from ‘paulos social’ to standalone ‘queuero’ tool
  • Copied spec to ../queuero project directory

Content and workflow:

  • Regenerated audio for Jan 24 dev reflection (9:56 min, 9.11 MB)
  • Added Phantasmagoria work log to blog as draft
  • Created work log for Jan 26
  • Updated editorial calendar (114 events)

Completed (2026-01-25)

Content and workflow:

  • Resolved git conflict in Jan 23 reflection post (kept local rewrite, merged remote metadata updates)
  • Rewrote Jan 23 podcast reflection with new angle on friction and automation
  • Improved description field for SEO and podcast feed display
  • Created work log documenting conflict resolution workflow and prose vs code version control
  • Completed /close workflow successfully

Future enhancements

  • Create Hugo archetype template for essays (pre-fill content_type, categories, etc.)
  • Build /review skill to list drafts and offer quick publish actions
  • Consider bidirectional content classifier (verify existing content_type assignments)

Completed (2026-01-24)

Content refinement:

  • Updated Jan 23 dev reflection with edits
  • Discovered description field dual purpose (podcast RSS + SEO meta tags)
  • Attempted audio regeneration - hit ElevenLabs quota (1,022 credits remaining, 4,673 needed)
  • Pre-commit hook failed gracefully without blocking commit (correct behavior)

Completed (2026-01-23)

Podcast infrastructure complete:

  • Created Hugo template to auto-generate polymathic.xml from posts with audio_url
  • Configured hugo.toml to output podcast RSS feed on every build
  • Feed uses description field if present, otherwise Summary
  • Removed static/polymathic.xml (now Hugo-generated)
  • Created git pre-commit hook to auto-generate audio for posts with “podcast” tag
  • Hook runs paulos podcast generate-audio on staged podcast posts
  • Audio generation is idempotent (skips if audio_url exists)
  • Updated podcast cover art with brand colors (warm brown #b54a32 on cream #FAF8F5)
  • Changed cover typography to Libre Baskerville (site’s serif font)
  • Removed “Daily” branding from cover art
  • Updated Apple Podcasts categories to Technology, Business > Management, Society & Culture > Philosophy
  • Fixed podcast owner email (ponch@ → paul@)
  • Added explicit description fields to prevent audio player HTML in RSS feed
  • Fixed XML entity escaping with transform.XMLEscape
  • Generated audio for three existing essays (build-for-the-loop, infrastructure-shapes-thought, when-teaching-stops-being-bounded)
  • Added “Podcast” to top navigation
  • Updated Apple Podcasts link (https://podcasts.apple.com/us/podcast/polymathic/id1871538054)
  • Documented complete podcast infrastructure in CLAUDE.md

Podcast RSS troubleshooting:

  • Fixed RSS timezone format (EST → -0500 for RFC 822 compliance)
  • Added per-episode author tags (itunes:author)
  • Fixed truncated episode descriptions
  • Added episode link tags pointing to blog posts
  • Updated Apple Podcasts categories (Technology → News)
  • Created Cloudflare _headers for proper RSS caching (5 min max-age)
  • Created /podcast landing page
  • Created blog post pages for both podcast episodes (Jan 21 and Jan 22)
  • Updated RSS episode links to point to blog posts
  • Verified all episode pages deployed and accessible (HTTP 200)
  • All Apple Podcasts validation errors resolved

Work log distribution:

  • Distributed Jan 22 work log as draft blog post

Completed (2026-01-22)

Pipeline skill planning:

  • Designed /pipeline skill implementation approach (pure Claude execution)
  • Explored existing infrastructure (PaulOS CLI, content classification, calendar generation)
  • Created comprehensive implementation plan at ~/.claude/plans/peppy-prancing-phoenix.md
  • Documented 8-step workflow, output formats, integration points, testing strategy
  • Ready for implementation in next session

Podcast infrastructure:

  • Created VOICE.md with Paul’s voice and brand guidelines
  • Set up podcast RSS feed at static/polymathic.xml
  • Designed and created podcast cover art (3000x3000px)
  • Feed published at https://www.paulwelty.com/polymathic.xml
  • Utilities Claude implemented /podcast skill with ElevenLabs + Cloudinary integration
  • Published first two podcast episodes (Jan 21 and Jan 22)
  • Audio hosted on Cloudinary CDN (no git storage of large mp3 files)
  • Tested RSS feed with Zapier - content_type filtering works correctly
  • Fixed Jan 21 episode metadata (date correction, removed duplicate fields)

Completed (2026-01-21)

Editorial calendar implementation:

  • Editorial calendar auto-updates on every /close workflow
  • Published at https://www.paulwelty.com/calendar.ics (79 draft posts)
  • Utilities Claude implemented paulos blog calendar command
  • Created EDITORIAL_CALENDAR_INFO.md with subscription instructions
  • Integrated calendar regeneration into /close skill (step 6)

Pipeline skill specification:

  • Created comprehensive spec for /pipeline skill
  • Designed upcoming schedule view, problem detection, content mix analysis
  • Specified quick actions (publish, archive, schedule)
  • Ready for implementation at .claude/skills/pipeline/SPEC.md

Project documentation:

  • Created CLAUDE.md for AI assistant guidance
  • Documented PaulOS CLI integration, content types, editorial calendar
  • Provided context for new Claude sessions

Completed (2026-01-21) - Content Type System

  • Add content_type to all existing posts
    • Manually tested categorization logic on 21 diverse posts (7 commits)
    • Created comprehensive spec for utilities Claude (CONTENT_TYPE_CLASSIFICATION.md)
    • Utilities Claude implemented paulos blog classify-content command
    • Automated classification of 518 remaining posts
    • Final distribution: 432 bookmarks, 50 essays, 42 work logs, 12 toots, 11 linkedin, 7 reactions, 6 newsletters, 3 articles
    • All 564 posts now have content_type for RSS-based Zapier automation

Completed (2026-01-20)

  • Verify Tuesday newsletter sends correctly at 11am EST (Campaign ID 21)
  • Check email formatting, links, unsubscribe functionality when campaign sends
  • Add newsletter attribution to friction essay

Completed (2026-01-19)

  • Draft newsletter essay: “Sometimes friction is your best friend” for Tuesday Jan 20
  • Create newsletter entry for Jan 20 referencing the friction essay
  • Complete Authexis → Hugo newsletter integration documentation
  • Build prepare_newsletter.py utility for editorial workflow
  • Schedule newsletter campaign in Brevo (Campaign ID 21, Tuesday 11am EST)
  • Fix work log category format issues (string → array)
  • Add newsletter preparation utility documentation to authexis-hugo-format.md

Current status (2025-12-30)

Site is running on poly5 theme - a pure Sass theme with zero npm dependencies.

  • 1540+ published pages
  • Hugo Pipes for CSS (Dart Sass, built-in)
  • Google Analytics 4 enabled (G-FMZXZ2XER2)
  • Cloudflare Pages deployment
  • Canonical URL: https://www.paulwelty.com/
  • Custom RSS feed with summary + full content
  • Newsletter v4 with streamlined single-article format

Recent updates (2026-01-12)

Newsletter system refactor (completed):

  • Consolidated all newsletter metadata into content/newsletters/*.md frontmatter
  • Created data/series.yaml for reusable series definitions (subtitle + closing text)
  • Updated all templates to read from .Params instead of querying posts
  • Removed newsletter tags from posts (newsletter_edition, newsletter_type, series)
  • Added series support with subtitle and closing text rendering in web and email
  • Removed JSON output format (Python script parses markdown frontmatter directly)
  • Updated Python script to parse YAML frontmatter from markdown files
  • Deleted v3 templates, JSON template files, and redundant data/newsletters/ directory
  • Newsletters now explicit: content files point to posts via feature_post parameter
  • Simplified system: one feature essay per newsletter, single source of truth
  • Tested and verified: Hugo build, email HTML, and Python script all working

Recent updates (2026-01-01)

Image handling improvements:

  • Fixed coverImage field to support external URLs (Cloudinary, etc.)
  • Both coverImage and image fields now check for http prefix
  • External URLs used directly, local paths get /images/ prefix
  • Applied fix to all templates: single.html, post-teaser.html, post-feature.html, home-latest-writing.html
  • Resolves 404 errors when using external image URLs in post frontmatter

Email template enhancements:

  • Added book promo to newsletter and confirmation email footers
  • Links to “The Work of Being” on Amazon in footer
  • Split bio into focused paragraphs (book promo + professional background)

Previous updates (2025-12-30)

Newsletter v4 launch:

  • Removed generic closing lines and “The work of being:” prefix from newsletter titles
  • Added rust divider before bio footer section
  • Updated footer with revised inline links (paulwelty.com · Work with me · Reply—I read everything)
  • Removed description from email header, keeping only slogan
  • Updated site slogan to “AI, work, and staying human”
  • Removed site description from website header (showing only name and slogan)
  • Newsletter list now sorts by edition descending with dates appended
  • Created v3 template backups for reference

New content series:

  • “The slop panic” (edition 260106, Jan 6 2026) - shorter version focusing on why only writers panicked
  • “Writing was always work” (edition 260113, Jan 13 2026) - explores writing as system-interface work
  • Both reference full essay “AI in writing: the end of a professional monopoly” via original_post field
  • Fixed list formatting for email compatibility
  • Added links to full argument at end of each shorter piece
  • Set publication dates to 2026 so future newsletters hide from list until publication

Template improvements:

  • Newsletter list displays with dates: “Title (January 6, 2025)”
  • Custom subject line support via newsletter_subject frontmatter field
  • Proper markdown bullet formatting for email rendering
  • Hide future-dated newsletters from “Past newsletters” list

Previous updates (2025-12-14)

  • Homepage redesign: combined hero + about into single section with expanded bio
  • Integrated CTA boxes (Writing, Newsletter, Work with me) into hero section
  • Header updated to “Paul Welty, PhD”
  • Tagline updated to “WORK, BEING, AND STAYING HUMAN”
  • Newsletter page with styled Brevo signup form and past editions list
  • Newsletter JSON template constructs subject as “The work of being:”</li> <li><input checked="" disabled="" type="checkbox"> Writing page with proper H1/H2/H3 hierarchy</li> <li><input checked="" disabled="" type="checkbox"> Post pages redesigned: no sidebar, meta at bottom, “More writing” footer</li> <li><input checked="" disabled="" type="checkbox"> Added hover:underline support to SCSS</li> <li><input checked="" disabled="" type="checkbox"> Combined consulting/speaking into “Work with me” page</li> <li><input checked="" disabled="" type="checkbox"> Hidden Polymathic (latest writing) section from homepage (preserved in code)</li> <li><input checked="" disabled="" type="checkbox"> Navigation redesign: sticky, reordered (Writing | Newsletter | Work with me | About | Contact), removed Home link</li> </ul> <h2 id="previous-cleanup-completed">Previous cleanup completed</h2> <ul> <li><input checked="" disabled="" type="checkbox"> Migrated from poly4 (Tailwind/npm) to poly5 (pure Sass)</li> <li><input checked="" disabled="" type="checkbox"> Removed ChatGPT citation artifacts from 44 files</li> <li><input checked="" disabled="" type="checkbox"> Fixed/drafted ~50 problem posts (duplicates, AI failures, title conflicts)</li> <li><input checked="" disabled="" type="checkbox"> Created custom 404 page</li> <li><input checked="" disabled="" type="checkbox"> Gave unique titles to conflicting posts (toots, etc.)</li> <li><input checked="" disabled="" type="checkbox"> Removed npm artifacts from repo (<code>package.json</code>, <code>package-lock.json</code>)</li> <li><input checked="" disabled="" type="checkbox"> Added custom RSS template (summary + full content)</li> <li><input checked="" disabled="" type="checkbox"> Set canonical URL to <a href="https://www.paulwelty.com">www.paulwelty.com</a></li> <li><input checked="" disabled="" type="checkbox"> Configured Cloudflare redirect (paulwelty.com → <a href="https://www.paulwelty.com">www.paulwelty.com</a>)</li> </ul> <h2 id="still-to-do">Still to do</h2> <ul> <li><input disabled="" type="checkbox"> Consider deleting old themes (poly2, poly3, poly4) after confirming poly5 works in production</li> <li><input disabled="" type="checkbox"> Review drafted posts to either fix or delete: <ul> <li>Posts with <code>draft: true</code> in <code>content/posts/</code> (duplicates, broken content)</li> <li>Drafts in <code>content/_drafts/</code></li> </ul> </li> <li><input disabled="" type="checkbox"> Consider adding: <ul> <li>Open Graph images</li> <li>Reading time estimates</li> <li>Related posts section</li> </ul> </li> </ul> <h2 id="newsletter-system---future-enhancements">Newsletter system - future enhancements</h2> <ul> <li><input disabled="" type="checkbox"> Series archive pages: Auto-generate landing pages for each series</li> <li><input disabled="" type="checkbox"> Newsletter preview page: Show draft/future newsletters with authentication</li> <li><input disabled="" type="checkbox"> Validation script: Pre-build check that all feature_posts exist</li> <li><input disabled="" type="checkbox"> Newsletter CLI: Script to create new newsletter YAML with defaults</li> <li><input disabled="" type="checkbox"> Analytics: Track which series/topics perform best</li> </ul> <h2 id="build-commands">Build commands</h2> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Local development</span> </span></span><span class="line"><span class="cl">hugo server --disableFastRender </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># Production build</span> </span></span><span class="line"><span class="cl">hugo --gc --minify </span></span></code></pre></div><h2 id="deployment-cloudflare-pages">Deployment (Cloudflare Pages)</h2> <ul> <li>Build command: <code>hugo --gc --minify</code></li> <li>Build output directory: <code>public</code></li> <li>Environment variable: <code>HUGO_VERSION</code> = <code>0.152.2</code> (or latest)</li> </ul> <p>No npm install needed - Hugo extended handles Sass natively.</p> <h2 id="theme-structure">Theme structure</h2> <pre tabindex="0"><code>themes/poly5/ ├── assets/sass/main.scss # Sass source (Hugo Pipes compiles) ├── layouts/ │ ├── _default/ │ │ ├── baseof.html # Base template with Hugo Pipes CSS │ │ ├── single.html # Post template │ │ ├── list.html # List template │ │ ├── search.html # Search page │ │ ├── about.html # About page │ │ └── rss.xml # Custom RSS feed template │ ├── partials/ │ └── 404.html # Custom 404 page └── theme.toml </code></pre> </div> <div class="newsletter-inline" style="margin: 2rem 0; padding: 1.5rem 2rem; background: #f9f7f5; border-radius: 0.5rem; border-left: 3px solid #8b6f5c;"> <div style="margin-bottom: 1rem;"> <strong style="font-size: 1.1rem; color: #1f2937;">Get essays like this in your inbox</strong> <p style="margin: 0.25rem 0 0; color: #6b7280; font-size: 0.9rem;">The work of being: AI, judgment, and staying human as work transforms.</p> </div> <div id="inline-error-message-end" style="display: none; margin-bottom: 0.75rem; padding: 0.75rem; border-radius: 0.375rem; background-color: #fef2f2; color: #991b1b; border: 1px solid #fecaca; font-size: 0.875rem;"> Your subscription could not be saved. Please try again. </div> <div id="inline-success-message-end" style="display: none; margin-bottom: 0.75rem; padding: 0.75rem; border-radius: 0.375rem; background-color: #f0fdf4; color: #166534; border: 1px solid #bbf7d0; font-size: 0.875rem;"> Your subscription has been successful. </div> <form id="sib-form-inline-end" method="POST" action="https://172ad0da.sibforms.com/serve/MUIFAGO8hGaI7AvIZM9TiA45MbFsWNWbuBmn6Bker6MxaVicbxnXa53XG5TXAe9Rsos73vJrtjkciDLGWqwhyrOy6Ilm4Zr6HKHI9eq86AVYINYW8IfOofxFXilLN3XaAlHHUy6TTbh2-gf2dZvxp9T7C4V0EuaAwJwLFCN5uMGY-MbOnNjXOH4f3LkxrjGbZGC7wWbMD1hg9hEXYQ==" data-type="subscription"> <div style="display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: flex-start;"> <input type="email" id="EMAIL_INLINE_END" name="EMAIL" autocomplete="email" placeholder="you@example.com" required style="flex: 1; min-width: 200px; padding: 0.5rem 1rem; border: 1px solid #d1d5db; border-radius: 0.375rem; font-size: 1rem;"> <button type="submit" form="sib-form-inline-end" style="padding: 0.5rem 1.25rem; background-color: #8b6f5c; color: white; font-weight: 600; border: none; border-radius: 0.375rem; cursor: pointer; white-space: nowrap;"> Subscribe </button> </div> <div style="margin-top: 0.75rem;"> <script> function handleInlineCaptchaResponseEnd() { var event = new Event('captchaChange'); document.getElementById('sib-captcha-inline-end').dispatchEvent(event); window.grecaptcha = window.turnstile; } </script> <div class="cf-turnstile" data-sitekey="0x4AAAAAACGVa4Ow4n65Sx62" id="sib-captcha-inline-end" data-callback="handleInlineCaptchaResponseEnd" data-language="en" data-size="compact"></div> </div> <input type="text" name="email_address_check" value="" style="display: none !important;"> <input type="hidden" name="locale" value="en"> </form> </div> <script> if (!window.sibFormsLoaded) { window.sibFormsLoaded = true; window.REQUIRED_CODE_ERROR_MESSAGE = 'Please choose a country code'; window.LOCALE = 'en'; window.EMAIL_INVALID_MESSAGE = window.SMS_INVALID_MESSAGE = "The information provided is invalid. Please review the field format and try again."; window.REQUIRED_ERROR_MESSAGE = "This field cannot be left blank. "; window.GENERIC_INVALID_MESSAGE = "The information provided is invalid. Please review the field format and try again."; window.translation = { common: { selectedList: '{quantity} list selected', selectedLists: '{quantity} lists selected', selectedOption: '{quantity} selected', selectedOptions: '{quantity} selected', } }; var AUTOHIDE = Boolean(0); } </script> <script defer src="https://sibforms.com/forms/end-form/build/main.js"></script> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> <div class="text-sm text-gray-400 mb-12"> <time datetime="0001-01-01">1 Jan 0001</time> </div> <hr class="border-gray-200" style="margin-bottom: 4rem;"> <div> <section class="mb-12"> <h2 class="text-2xl font-headline font-bold text-gray-900 mb-4">Featured writing</h2> <div class="space-y-8"> <div class="border-l-2 border-poly-500 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/why-customer-tools-are-organized-wrong/" class="text-poly-700 hover:underline">Why customer tools are organized wrong</a> </h3> <p class="text-gray-600 mt-1 text-[15px]">This article reveals a fundamental flaw in how customer support tools are designed—organizing by interaction type instead of by customer—and explains why this fragmentation wastes time and obscures the full picture you need to help users effectively.</p> </div> <div class="border-l-2 border-poly-500 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/infrastructure-shapes-thought/" class="text-poly-700 hover:underline">Infrastructure shapes thought</a> </h3> <p class="text-gray-600 mt-1 text-[15px]">The tools you build determine what kinds of thinking become possible. On infrastructure, friction, and building deliberately for thought rather than just throughput.</p> </div> <div class="border-l-2 border-poly-500 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/server-side-dashboard-architecture-why-moving-data-fetching-off-the-browser-changes-everything/" class="text-poly-700 hover:underline">Server-Side Dashboard Architecture: Why Moving Data Fetching Off the Browser Changes Everything</a> </h3> <p class="text-gray-600 mt-1 text-[15px]">How choosing server-side rendering solved security, CORS, and credential management problems I didn't know I had.</p> </div> </div> </section> <section class="mb-12"> <h2 class="text-2xl font-headline font-bold text-gray-900 mb-4">Books</h2> <div class="space-y-8"> <div class="border-l-2 border-gray-200 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/work-of-being/" class="text-poly-700 hover:underline">The Work of Being</a> <span class="text-xs uppercase tracking-wide text-gray-400">(in progress)</span> </h3> <p class="text-gray-600 mt-1 text-[15px]">A book on AI, judgment, and staying human at work.</p> </div> <div class="border-l-2 border-gray-200 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/practice-of-work/" class="text-poly-700 hover:underline">The Practice of Work</a> <span class="text-xs uppercase tracking-wide text-gray-400">(in progress)</span> </h3> <p class="text-gray-600 mt-1 text-[15px]">Practical essays on how work actually gets done.</p> </div> </div> </section> <section class="mb-12"> <h2 class="text-2xl font-headline font-bold text-gray-900 mb-4">Recent writing</h2> <div class="space-y-8"> <div class="border-l-2 border-poly-500 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/we-always-panic-about-new-tools-and-were-always-wrong/" class="text-poly-700 hover:underline">We always panic about new tools (and we're always wrong)</a> </h3> <p class="text-gray-600 mt-1 text-[15px]">Every time a new tool emerges for making or manipulating symbols, we panic. The pattern is so consistent it's almost embarrassing. Here's what happened each time.</p> </div> <div class="border-l-2 border-poly-500 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/dev-reflection-february-03-2026/" class="text-poly-700 hover:underline">Dev reflection - February 03, 2026</a> </h3> <p class="text-gray-600 mt-1 text-[15px]">I've been thinking about constraints today. Not the kind that block you—the kind that clarify. There's a difference, and most people miss it.</p> </div> <div class="border-l-2 border-poly-500 pl-5"> <h3 class="text-xl font-headline font-semibold"> <a href="/when-execution-becomes-cheap-ideas-become-expensive/" class="text-poly-700 hover:underline">When execution becomes cheap, ideas become expensive</a> </h3> <p class="text-gray-600 mt-1 text-[15px]">This article reveals a fundamental shift in how organizations operate: as AI makes execution nearly instantaneous, the bottleneck has moved from implementation to decision-making. Understanding this transition is critical for anyone leading teams or making strategic choices in an AI-enabled world.</p> </div> </div> <div class="mt-6"> <a href="/writing/" class="text-poly-600 hover:underline text-sm font-medium">View all writing →</a> </div> </section> </div> </article> </main> <footer class="p-8 mt-12"> <style> .footer-grid { display: grid; gap: 2rem; margin-bottom: 1.25rem; } @media (min-width: 768px) { .footer-grid { grid-template-columns: 40% 30% 30%; } } </style> <div class="footer-grid"> <div class="footer-section"> <div class="font-semibold mb-2 text-sm uppercase tracking-wide text-gray-700">The work of being</div> <p class="text-gray-600 text-sm mb-3">A newsletter on AI, judgment, and the work of staying human as work transforms. Essays for people who want to use AI without letting it flatten their work — or themselves.</p> <a href="/newsletter/" class="inline-block bg-poly-600 text-white px-4 py-2 rounded text-sm hover:bg-poly-700 transition-colors">Subscribe →</a> </div> <div class="footer-section"> <div class="font-semibold mb-2 text-sm uppercase tracking-wide text-gray-700">Contact</div> <p class="text-gray-600 text-sm mb-2">Thoughtful conversations welcome.</p> <div class="text-sm text-gray-600 space-y-1"> <div><a href="mailto:paul@paulwelty.com" class="hover:text-poly-600 transition-colors">paul@paulwelty.com</a></div> <div><a href="tel:+14049132140" class="hover:text-poly-600 transition-colors">+1-404-913-2140</a></div> </div> </div> <div class="footer-section"> <div class="font-semibold mb-2 text-sm uppercase tracking-wide text-gray-700">Elsewhere</div> <div class="flex gap-4"> <a href="https://www.linkedin.com/in/paulwelty/" class="text-gray-600 hover:text-poly-600 transition-colors" title="LinkedIn" target="_blank" rel="noopener noreferrer"> <svg class="social-icon w-5 h-5 fill-current"> <use href="#icon-linkedin"></use> </svg> </a> <a href="https://bsky.app/profile/aristotle.bsky.social" class="text-gray-600 hover:text-poly-600 transition-colors" title="Bluesky" target="_blank" rel="noopener noreferrer"> <svg class="social-icon w-5 h-5 fill-current"> <use href="#icon-bluesky"></use> </svg> </a> <a href="https://mastodon.world/@aristotle" class="text-gray-600 hover:text-poly-600 transition-colors" title="Mastodon" target="_blank" rel="me noopener noreferrer"> <svg class="social-icon w-5 h-5 fill-current"> <use href="#icon-mastodon"></use> </svg> </a> </div> </div> </div> <div class="border-t border-gray-200 pt-4 text-center text-gray-600 text-sm mt-8"> <p>© 2007-2026 Paul Welty, PhD. All rights reserved.</p> </div> </footer> </div> <script> document.addEventListener('DOMContentLoaded', function() { let searchData = []; let searchInput = document.getElementById('search-input'); let searchResults = document.getElementById('search-results'); fetch('/search.json') .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { searchData = data; }) .catch(error => { console.error('Error loading search data:', error); }); function performSearch(query) { if (!query || query.length < 2) { searchResults.classList.add('hidden'); return; } const results = searchData.filter(post => { const searchText = (post.title + ' ' + post.excerpt + ' ' + post.tags.join(' ') + ' ' + (post.category || '')).toLowerCase(); return searchText.includes(query.toLowerCase()); }).slice(0, 8); displayResults(results, query); } function displayResults(results, query) { if (results.length === 0) { searchResults.innerHTML = '<div class="p-3 text-gray-500 text-sm">No results found</div>'; searchResults.classList.remove('hidden'); return; } const resultsHTML = results.map(post => { const highlightedTitle = highlightText(post.title, query); const highlightedExcerpt = highlightText(post.excerpt.substring(0, 120) + '...', query); return ` <a href="${post.url}" class="block p-3 hover:bg-gray-50 border-b border-gray-100 last:border-b-0"> <div class="font-medium text-sm text-gray-900 mb-1">${highlightedTitle}</div> <div class="text-xs text-gray-600 line-clamp-2">${highlightedExcerpt}</div> ${post.category ? `<div class="text-xs text-poly-600 mt-1">${post.category}</div>` : ''} </a> `; }).join(''); const totalResults = searchData.filter(post => { const searchText = (post.title + ' ' + post.excerpt + ' ' + post.tags.join(' ') + ' ' + (post.category || '')).toLowerCase(); return searchText.includes(query.toLowerCase()); }).length; let viewAllHTML = ''; if (totalResults > 8) { viewAllHTML = ` <a href="/search/?q=${encodeURIComponent(query)}" class="block p-3 bg-gray-50 text-center text-sm text-poly-600 hover:text-poly-800 border-t"> View all ${totalResults} results </a> `; } searchResults.innerHTML = resultsHTML + viewAllHTML; searchResults.classList.remove('hidden'); } function highlightText(text, query) { if (!query) return text; const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); return text.replace(regex, '<mark class="bg-yellow-200">$1</mark>'); } if (searchInput) { searchInput.addEventListener('input', (e) => { performSearch(e.target.value); }); searchInput.addEventListener('focus', (e) => { if (e.target.value.length >= 2) { performSearch(e.target.value); } }); searchInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); const query = e.target.value.trim(); if (query) { window.location.href = `/search/?q=${encodeURIComponent(query)}`; } } else if (e.key === 'Escape') { searchResults.classList.add('hidden'); } }); document.addEventListener('click', (e) => { if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) { searchResults.classList.add('hidden'); } }); } const mobileMenuBtn = document.getElementById('mobile-menu-btn'); const mobileMenu = document.getElementById('mobile-menu'); const hamburgerIcon = document.getElementById('hamburger-icon'); const closeIcon = document.getElementById('close-icon'); if (mobileMenuBtn && mobileMenu) { mobileMenuBtn.addEventListener('click', () => { mobileMenu.classList.toggle('is-open'); hamburgerIcon.classList.toggle('hidden'); closeIcon.classList.toggle('hidden'); }); } }); </script> </body> </html>