Dark factory series — 7 backfill blog posts #100

Merged
agent-content merged 7 commits from content/dark-factories-backfill into main 2026-07-17 00:55:15 +00:00
19 changed files with 1264 additions and 79 deletions

View file

@ -0,0 +1,22 @@
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="225" viewBox="0 0 150 225">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#2d1b0e"/>
<stop offset="100%" stop-color="#181818"/>
</linearGradient>
</defs>
<rect width="150" height="225" fill="url(#bg)" rx="4"/>
<!-- subtle border -->
<rect x="2" y="2" width="146" height="221" fill="none" stroke="#8b7355" stroke-width="1" rx="3"/>
<!-- spine line -->
<line x1="10" y1="50" x2="140" y2="50" stroke="#8b7355" stroke-width="1" opacity="0.4"/>
<!-- title -->
<text x="75" y="85" text-anchor="middle" fill="#e8dcc8" font-family="Georgia, serif" font-size="13" font-weight="bold">Dark</text>
<text x="75" y="102" text-anchor="middle" fill="#e8dcc8" font-family="Georgia, serif" font-size="13" font-weight="bold">Provenance</text>
<!-- subtitle -->
<text x="75" y="130" text-anchor="middle" fill="#b8a88a" font-family="sans-serif" font-size="7">Formal Verification</text>
<text x="75" y="142" text-anchor="middle" fill="#b8a88a" font-family="sans-serif" font-size="7">for the AI Age</text>
<!-- coming soon badge -->
<rect x="25" y="160" width="100" height="20" rx="10" fill="#8b7355" opacity="0.8"/>
<text x="75" y="174" text-anchor="middle" fill="#fff" font-family="sans-serif" font-size="8" font-weight="bold" letter-spacing="1">COMING SOON</text>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -66,18 +66,22 @@ const currentYear = new Date().getFullYear();
</script>
<div class="footer-social">
<a href="https://linkedin.com/in/scottscoble" target="_blank" rel="noopener" class="social-link" aria-label="Follow on LinkedIn for AI insights and leadership perspectives" data-umami-event="footer-linkedin-click">
<a href="https://linkedin.com/in/scottscoble" target="_blank" rel="noopener me" class="social-link" aria-label="Follow on LinkedIn for AI insights and leadership perspectives" data-umami-event="footer-linkedin-click">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
<span class="social-label">Follow for AI Insights</span>
</a>
<a href="https://x.com/scottscoble" target="_blank" rel="noopener" class="social-link" aria-label="Follow on X/Twitter for daily thoughts on cultivation and AI" data-umami-event="footer-x-click">
<a href="https://x.com/scottscoble" target="_blank" rel="noopener me" class="social-link" aria-label="Follow on X/Twitter for daily thoughts on cultivation and AI" data-umami-event="footer-x-click">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
<span class="social-label">Daily Thoughts</span>
</a>
<a href="https://github.com/wscoble" target="_blank" rel="noopener" class="social-link" aria-label="Explore open source projects on GitHub" data-umami-event="footer-github-click">
<a href="https://github.com/wscoble" target="_blank" rel="noopener me" class="social-link" aria-label="Explore open source projects on GitHub" data-umami-event="footer-github-click">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.605-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg>
<span class="social-label">Open Source</span>
</a>
<a href="https://www.amazon.com/stores/Scott-Scoble/author/B0GKSSBDMK" target="_blank" rel="noopener me" class="social-link" aria-label="Scott Scoble author page on Amazon" data-umami-event="footer-amazon-click">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M13.23 10.56V10c0-1.2.63-2.1 1.68-2.1.87 0 1.5.64 1.5 1.48 0 .96-1.04 1.76-3.18 3.18zm-4.2 8.68c-2.12 0-4.08-.76-5.48-2.26-.24-.26-.15-.66.2-.78l2.68-.92c.33-.1.66.1.82.4.56.96 1.5 1.5 2.64 1.5 2.3 0 3.34-1.84 3.78-3.34-1.3.52-2.64.78-4.04.78-3.18 0-6.08-1.72-7.2-4.44-.24-.58-.38-1.24-.38-1.92 0-2.92 2.62-6.34 7.2-6.34 2.98 0 5.68 1.24 7.08 3.18 1.44 1.98 1.26 5.08-.24 7.54-1.16 1.92-3.24 3.36-5.46 3.46zM22.46 21l-2.34-.7c-.3-.08-.5-.36-.42-.66.08-.3.36-.5.66-.42l2.34.7c.3.1.48.4.4.7-.1.3-.38.5-.68.4z"/></svg>
<span class="social-label">Books on Amazon</span>
</a>
</div>
</div>

View file

@ -120,7 +120,7 @@ const topicParam = Astro.url.searchParams.get('topic');
<div class="form-success" style="display:none;">
<h4>{type === 'waitlist' ? "You're on the waitlist!" : 'Thanks for reaching out!'}</h4>
<p>{type === 'speaking' ? "Your inquiry has been submitted. Scott typically responds within 24 hours." : type === 'contact' ? "Your message has been submitted. Scott typically responds within 24 hours." : "We'll let you know when Dark Provenance is available."}</p>
<p>{type === 'speaking' ? "Your inquiry has been submitted. Scott typically responds within 24 hours." : "Your message has been submitted. Scott typically responds within 24 hours."}</p>
</div>
</div>

View file

@ -38,6 +38,7 @@ function getJsonLd(pageType: string): string {
'https://linkedin.com/in/scottscoble',
'https://x.com/scottscoble',
'https://github.com/wscoble',
'https://www.amazon.com/stores/Scott-Scoble/author/B0GKSSBDMK',
],
knowsAbout: ['AI leadership', 'human cultivation', 'engineering teams', 'formal specifications', 'provenance systems'],
knowsLanguage: ['en'],
@ -78,7 +79,63 @@ function getJsonLd(pageType: string): string {
description: 'Human Cultivation Expert for the AI Age',
url: siteUrl,
};
return JSON.stringify([personLd, websiteLd], null, 2);
const speakableSchema = {
'@context': 'https://schema.org',
'@type': 'SpeakableSpecification',
cssSelector: ['h1', '.hero p', '.hero-description'],
};
return JSON.stringify([personLd, websiteLd, speakableSchema], null, 2);
}
case 'blog': {
const blogSchema = {
'@context': 'https://schema.org',
'@type': 'Blog',
'@id': `${siteUrl}#blog`,
name: 'Scott Scoble Blog',
description: 'Articles and essays by Scott Scoble on AI leadership, team cultivation, developer experience, and the human side of technology.',
url: `${siteUrl}/blog`,
author: {
'@type': 'Person',
name: 'Scott Scoble',
url: siteUrl,
},
blogPost: [
{
'@type': 'BlogPosting',
headline: 'Why Cultivation Over Strip Mining',
description: 'Gallup 2026: 80% of the global workforce is disengaged. The $438B cost is not a tool problem; it is a people problem.',
url: `${siteUrl}/blog/strip-mining-cultivation`,
datePublished: '2026-05-18',
author: { '@type': 'Person', name: 'Scott Scoble' },
},
{
'@type': 'BlogPosting',
headline: '50 First Dates with AI',
description: "Every conversation with most AI systems is a first date. Here's the three-layer memory model that actually compounds.",
url: `${siteUrl}/blog/50-first-dates-with-ai`,
datePublished: '2026-05-24',
author: { '@type': 'Person', name: 'Scott Scoble' },
},
{
'@type': 'BlogPosting',
headline: 'The Soil Under Your Team',
description: "Tall stalks, ground cover, nitrogen fixers, pollinators, deep roots. Here's how to read the soil under your team.",
url: `${siteUrl}/blog/soil-health-teams`,
datePublished: '2026-05-18',
author: { '@type': 'Person', name: 'Scott Scoble' },
},
{
'@type': 'BlogPosting',
headline: 'The Soil Beneath the Code is Out',
description: 'A three-layer framework for understanding what makes engineering organizations healthy — or not.',
url: `${siteUrl}/blog/the-soil-beneath-the-code`,
datePublished: '2026-04-28',
author: { '@type': 'Person', name: 'Scott Scoble' },
},
],
};
return JSON.stringify([personLd, blogSchema], null, 2);
}
case 'speaking':
@ -177,19 +234,6 @@ function getJsonLd(pageType: string): string {
description: 'Three questions that change how people learn. The simplest mentoring framework you\'ll actually use.',
sameAs: 'https://a.co/d/0bRKZeUV',
},
{
'@context': 'https://schema.org',
'@type': 'Book',
name: 'Dark Provenance',
author: {
'@type': 'Person',
name: 'Scott Scoble',
url: siteUrl,
},
datePublished: '2026',
bookFormat: 'https://schema.org/EBook',
description: 'Formal verification for the AI age. How to audit what your AI actually did.',
},
], null, 2);
case 'consulting':

View file

@ -17,7 +17,7 @@ export interface Props {
const {
title,
description,
ogImage = '/social-card.png',
ogImage = '/images/hero-speaker-new.webp',
ogType = 'website',
canonicalUrl,
pageType = 'home',

View file

@ -70,18 +70,13 @@ import BaseLayout from '../layouts/BaseLayout.astro';
<p>Three questions that change how people learn.</p>
</article>
<article class="book-card">
<h3>Dark Provenance</h3>
<p class="book-status">Coming 2026</p>
<p>Formal verification for the AI age. How to audit what your AI actually did.</p>
</article>
</div>
</div>
</section>
<section class="projects-section">
<div class="section-container">
<h2>Open Source Projects</h2>
<h2>Projects</h2>
<div class="projects-grid">
<article class="project-card">
@ -91,6 +86,7 @@ import BaseLayout from '../layouts/BaseLayout.astro';
<article class="project-card">
<h3>ScobleClaw</h3>
<p class="project-visibility">Private</p>
<p>Agentic engineering framework — tools, workflows, and skills for building with AI agents. Patterns for subagent dispatch, skill-based development, and provenance tracking.</p>
</article>
@ -296,4 +292,17 @@ import BaseLayout from '../layouts/BaseLayout.astro';
color: var(--color-text-muted);
line-height: 1.6;
}
.project-visibility {
display: inline-block;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-amber);
background: var(--color-cream-warm);
padding: 0.15rem 0.6rem;
border-radius: 3px;
margin-bottom: 0.5rem;
}
</style>

View file

@ -4,8 +4,9 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
---
<BaseLayout
title="Blog - Scott Scoble"
description="Articles on AI, leadership, cultivation, and the human side of technology."
title="Blog — Scott Scoble"
description="Articles and essays by Scott Scoble on AI leadership, team cultivation, developer experience, and the human side of technology. Pillar posts: Why Cultivation Over Strip Mining, 50 First Dates with AI, The Soil Under Your Team, The Soil Beneath the Code."
pageType="blog"
>
<section class="hero">
@ -20,6 +21,67 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
<section class="posts">
<div class="posts-container">
<div class="pillar-posts">
<p class="pillar-label">Pillar Posts</p>
<div class="pillar-grid">
<article class="post-card pillar-card">
<a href="/blog/strip-mining-cultivation" class="post-link">
<div class="post-meta">
<span class="post-date">May 18, 2026</span>
<span class="post-tag">Leadership</span>
</div>
<h2>Why Cultivation Over Strip Mining</h2>
<p class="post-excerpt">
Gallup 2026: 80% of the global workforce is disengaged. The $438B cost isn't a tool problem; it's a people problem. Here's the cultivation alternative.
</p>
</a>
</article>
<article class="post-card pillar-card">
<a href="/blog/50-first-dates-with-ai" class="post-link">
<div class="post-meta">
<span class="post-date">May 24, 2026</span>
<span class="post-tag">AI</span>
</div>
<h2>50 First Dates with AI</h2>
<p class="post-excerpt">
Every conversation with most AI systems is a first date. Here's the three-layer memory model that actually compounds — from Scott Scoble's scobleclaw experience.
</p>
</a>
</article>
<article class="post-card pillar-card">
<a href="/blog/soil-health-teams" class="post-link">
<div class="post-meta">
<span class="post-date">May 18, 2026</span>
<span class="post-tag">Leadership</span>
</div>
<h2>The Soil Under Your Team</h2>
<p class="post-excerpt">
Tall stalks, ground cover, nitrogen fixers, pollinators, deep roots. The agricultural metaphor isn't poetic; it's diagnostic. Here's how to read the soil under your team.
</p>
</a>
</article>
<article class="post-card pillar-card">
<a href="/blog/the-soil-beneath-the-code" class="post-link">
<div class="post-meta">
<span class="post-date">April 28, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h2>The Soil Beneath the Code is Out</h2>
<p class="post-excerpt">
A three-layer framework for understanding what makes engineering organizations healthy — or not. From the book that started the cultivation movement.
</p>
</a>
</article>
</div>
</div>
<hr class="posts-divider" />
<article class="post-card">
<a href="/blog/50-first-dates-with-ai" class="post-link">
<div class="post-meta">
@ -33,6 +95,84 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
</a>
</article>
<article class="post-card">
<a href="/blog/what-is-a-dark-factory" class="post-link">
<div class="post-meta">
<span class="post-date">May 22, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h2>What a Dark Factory Actually Is (and Isn't)</h2>
<p class="post-excerpt">
The phrase is everywhere. The working meaning is gone. Here is what it is, what it isn't, and the five positions on the spectrum.
</p>
</a>
</article>
<article class="post-card">
<a href="/blog/light-vs-dark-factories" class="post-link">
<div class="post-meta">
<span class="post-date">June 2, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h2>Light vs Dark Factories: Why the Middle Is Harder Than Either End</h2>
<p class="post-excerpt">
A light factory has too many gates. A dark factory has too few. The middle is where the engineering happens, and the middle is the part nobody talks about.
</p>
</a>
</article>
<article class="post-card">
<a href="/blog/why-kubernetes-for-dark-factory" class="post-link">
<div class="post-meta">
<span class="post-date">June 11, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h2>Why Kubernetes for a Dark Factory</h2>
<p class="post-excerpt">
A dark factory is a control loop. Kubernetes is the most boring control loop in production. That is the reason.
</p>
</a>
</article>
<article class="post-card">
<a href="/blog/operator-patterns-for-software-factories" class="post-link">
<div class="post-meta">
<span class="post-date">June 19, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h2>Kubernetes Operator Patterns for Software Factories</h2>
<p class="post-excerpt">
An operator is a control loop with a domain in it. That is the whole definition. Here are the four operator patterns and the one anti-pattern to avoid.
</p>
</a>
</article>
<article class="post-card">
<a href="/blog/the-taste-problem" class="post-link">
<div class="post-meta">
<span class="post-date">June 29, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h2>The Taste Problem: What Dark Factories Can't Transfer</h2>
<p class="post-excerpt">
A dark factory can transfer code. It cannot transfer the judgment that knew the code was right. That is the problem provenance was invented to solve.
</p>
</a>
</article>
<article class="post-card">
<a href="/blog/human-in-the-loop-seam" class="post-link">
<div class="post-meta">
<span class="post-date">July 8, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h2>The Human-in-the-Loop Seam: Where Humans Belong in a Dark Factory</h2>
<p class="post-excerpt">
Human-in-the-loop is a phrase everyone uses. Almost nobody uses it correctly. The seam is in the spec, not in the implementation.
</p>
</a>
</article>
<article class="post-card">
<a href="/blog/strip-mining-cultivation" class="post-link">
<div class="post-meta">
@ -341,4 +481,38 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
margin: 0;
line-height: 1.6;
}
/* Pillar Posts */
.pillar-label {
font-size: 0.875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.15em;
color: var(--color-amber);
margin: 0 0 1.5rem;
}
.pillar-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
margin-bottom: 1rem;
}
.pillar-card {
border-left: 4px solid var(--color-amber);
background: linear-gradient(135deg, #faf8f5 0%, #f5f0e8 100%);
}
.pillar-card:hover {
border-color: var(--color-green);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
}
.posts-divider {
border: 0;
height: 1px;
background: var(--color-border);
margin: 2rem 0;
}
</style>

View file

@ -0,0 +1,121 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
---
<BaseLayout
title="The Human-in-the-Loop Seam: Where Humans Belong in a Dark Factory"
description="Human-in-the-loop is a phrase everyone uses. Almost nobody uses it correctly. The seam is in the spec, not in the implementation."
ogType="article"
canonicalUrl="/blog/human-in-the-loop-seam"
pageType="blog"
>
<article class="post">
<header class="post-header">
<div class="post-meta">
<span class="post-date">July 8, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h1>The Human-in-the-Loop Seam: Where Humans Belong in a Dark Factory</h1>
</header>
<div class="post-content">
<p class="lead-question">Human-in-the-loop is a phrase everyone uses. Almost nobody uses it correctly.</p>
<p>A human is in the loop at a seam. The seam is a place where the system is underdetermined. The seam is the place where the spec does not say what the right answer is, and the factory has to either ask or guess. The human's job is to be the answer to the questions the spec cannot answer. The factory's job is to surface those questions, at the right time, in the right form, to a human who can answer them.</p>
<h2>The wrong place to put the human</h2>
<p>The wrong place to put the human is in the implementation. If the spec is complete enough to be compiled, the factory should compile it. Adding a human to "review the code" before the merge is putting the human in a place where the human is duplicating the factory's work. The factory just generated the artifact from the spec. The human reviewing the artifact is asking whether the factory did what the spec said. The factory can answer that question with a provenance graph. The human is not adding information. The human is adding latency.</p>
<p>Teams that put the human in the review get a familiar pattern. The review takes two days. The human approves what the factory produced. The human introduces small changes that break the spec. The spec and the artifact drift apart because the human was the seam, and the seam was in the wrong place. The seam should have been in the spec, not in the review.</p>
<h2>The right place to put the human</h2>
<p>The right place to put the human is on the seam where the spec is underdetermined. There are three of these seams in a dark factory, and they are different from each other.</p>
<p><strong>The policy seam.</strong> The policy is the human's signature on the system. The policy says: this kind of artifact, in this kind of environment, is allowed to promote. The policy is underdetermined because the policy is a human value judgment, not a derived fact. The factory cannot derive what is allowed. The factory can only enforce what has been declared. The human declares. The factory enforces.</p>
<p><strong>The spec seam.</strong> The spec is underdetermined in places. The spec describes intent. The intent does not always fully constrain the artifact. The factory will produce a correct artifact from an underdetermined spec. The artifact will be one of many correct artifacts, and the choice between them is a taste decision. The human has the taste. The factory surfaces the choice. The human makes the call. The spec is updated with the call. The factory's next artifact is constrained by the updated spec.</p>
<p><strong>The audit seam.</strong> The audit is the place where the human checks that the factory did what the spec said, and the spec was the right call. The audit is the only seam where the human gets to ask the second-order question. The audit is the only seam where the human can change the spec. The audit is the only seam that closes the loop. Without the audit, the factory is the only one learning. With the audit, the human is learning too.</p>
<h2>What the human looks like at each seam</h2>
<p>At the policy seam, the human is a writer. The human writes the policy. The policy is a small artifact, versioned, signed, reviewed. The policy is not code. The policy is intent. The factory is the executor.</p>
<p>At the spec seam, the human is an editor. The human updates the spec to capture a decision the factory surfaced. The spec is a living document. The spec is not a wiki. The spec is a file the factory reads. The human does not need to be a programmer. The human needs to be a domain expert who can describe what they want in the domain's vocabulary.</p>
<p>At the audit seam, the human is a reader. The human reads the factory's output, the spec the factory consumed, the policy the factory enforced. The human asks: did the right thing happen? The human's answer goes into the next spec, the next policy, the next audit.</p>
<h2>Why this is harder than it looks</h2>
<p>The seams are not labeled in the artifact. The artifact has a hash, a signature, a provenance graph. None of those say "this decision was made by a human at the spec seam." The factory has to be designed to make the seam visible. The factory has to be designed to ask the question at the right time. The factory has to be designed to wait for the answer, or to ship a default, depending on the policy.</p>
<p>This is the work of designing a dark factory. The work is not the build pipeline. The work is the seam management.</p>
<h2>The single rule</h2>
<p>The human belongs on the seam where the spec is underdetermined, not on the seam where the implementation is risky.</p>
<p>The patterns that exist put the human where the implementation is risky. The human reviews the code, the test, the deploy. The factory can review the code, run the test, ship the deploy. The human's value is upstream of the implementation, at the seams where the spec is underdetermined.</p>
<p>That is the human's job. The human decides what the factory does. The factory decides how the human's decision becomes an artifact. The seam between them is the only place the human belongs.</p>
</div>
</article>
</BaseLayout>
<style>
.post {
max-width: 720px;
margin: 0 auto;
padding: 12rem 2rem 6rem;
}
.post-header {
margin-bottom: 3rem;
}
.post-meta {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.post-date {
color: var(--color-text-muted);
}
.post-tag {
color: var(--color-amber);
font-weight: 600;
}
.lead-question {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-brown-dark);
margin-bottom: 1.5rem;
line-height: 1.5;
}
.post-content h2 {
font-family: var(--font-heading);
font-size: 1.5rem;
color: var(--color-brown-dark);
margin: 2.5rem 0 1rem;
}
.post-content p {
line-height: 1.7;
margin-bottom: 1.25rem;
}
.post-content strong {
color: var(--color-brown-dark);
}
</style>

View file

@ -0,0 +1,138 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
---
<BaseLayout
title="Light vs Dark Factories: Why the Middle Is Harder Than Either End"
description="A light factory has too many gates. A dark factory has too few. The middle is where the engineering happens, and the middle is the part nobody talks about."
ogType="article"
canonicalUrl="/blog/light-vs-dark-factories"
pageType="blog"
>
<article class="post">
<header class="post-header">
<div class="post-meta">
<span class="post-date">June 2, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h1>Light vs Dark Factories: Why the Middle Is Harder Than Either End</h1>
</header>
<div class="post-content">
<p class="lead-question">Pure dark factories are for vendors. Pure light factories are for teams that do not trust their pipeline. The interesting engineering is in between.</p>
<p>The ends are easy to describe. The middle is where the work is.</p>
<h2>The light factory</h2>
<p>A light factory is what most teams have when they say "we have a CI/CD pipeline." Code is written. Tests run. A human reviews the diff. A human approves the merge. A human triggers the deploy. A human watches the deploy. A human confirms the deploy is healthy.</p>
<p>Every transition is a handoff. Every handoff is a place for a person to add or remove judgment. The team scales linearly with the number of handoffs. The team does not scale linearly with the number of engineers, because the handoffs are the bottleneck.</p>
<p>At some point, the handoffs become the work. The team is not shipping product. The team is moving code through human gates. The product slows. The hiring ramps. The handoffs grow. The team gets further from shipping and closer to queue management.</p>
<h2>The dark factory</h2>
<p>A dark factory replaces the human handoffs with policy. Spec in, artifact out. The policy is the rules. Nothing in the middle requires a person to act.</p>
<p>This is a team that ships constantly, unattended. The product is moving fast, except when the policy is wrong. When the policy is wrong, the product moves fast in a bad direction and no one notices until the customer does.</p>
<h2>The middle is a position, not a transition</h2>
<p>The middle has three properties.</p>
<ol>
<li><strong>The pipeline is the gate.</strong> The merge to main is not gated by a reviewer. It is gated by a policy expression. The policy is code. The code is reviewed.</li>
<li><strong>The human is the auditor.</strong> The human inspects what the pipeline produced. The audit is sampling-based, not exhaustive. The audit answers "did the policy work this week," not "is this merge correct."</li>
<li><strong>The spec is the source of truth.</strong> The code is generated. The test suite is generated. The deploy manifest is generated. The thing the human writes is the spec, and the spec is what gets reviewed.</li>
</ol>
<p>What is new is the surface area. The spec is meant to be writable by a domain expert, not just an engineer, in a domain-specific language. The audit surface is small enough to review against the policy.</p>
<h2>Why the middle is harder than either end</h2>
<p>At the dark end, you maintain the policy. The policy is the system.</p>
<p>At the light end, you maintain the people. The people are the system.</p>
<p>In the middle, you maintain the policy, you maintain the people, and you maintain the <em>seam</em> between them. The seam is what fails. The seam is what determines whether the factory runs at all.</p>
<p>A seam is a place where the human's authority and the pipeline's authority have to be reconciled. Promotion to production is a seam. Incident response is a seam. The decision to roll back is a seam. Each of those is a moment where the human's signature is the difference between shipping and stalling.</p>
<p>Each seam has a default. The default is what the pipeline does when no human acts. If the default is "ship" and the human does not act, the system has shipped. If the default is "do not ship," the system has stalled. The default has to be defensible. The default has to be chosen.</p>
<h2>How to know which position you are in</h2>
<p>One question. When the human does nothing, does the system ship or stall?</p>
<p>If the answer is ship, you are dark. If the answer is stall, you are light. If the answer is "it depends on which stage," you are in the middle. The middle is where you want to be, and the middle is the part you have to design on purpose.</p>
</div>
</article>
</BaseLayout>
<style>
.post {
max-width: 720px;
margin: 0 auto;
padding: 12rem 2rem 6rem;
}
.post-header {
margin-bottom: 3rem;
}
.post-meta {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.post-date {
color: var(--color-text-muted);
}
.post-tag {
color: var(--color-amber);
font-weight: 600;
}
.lead-question {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-brown-dark);
margin-bottom: 1.5rem;
line-height: 1.5;
}
.post-content h2 {
font-family: var(--font-heading);
font-size: 1.5rem;
color: var(--color-brown-dark);
margin: 2.5rem 0 1rem;
}
.post-content p {
line-height: 1.7;
margin-bottom: 1.25rem;
}
.post-content ol,
.post-content ul {
line-height: 1.7;
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.post-content li {
margin-bottom: 0.5rem;
}
.post-content strong {
color: var(--color-brown-dark);
}
</style>

View file

@ -0,0 +1,132 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
---
<BaseLayout
title="Kubernetes Operator Patterns for Software Factories"
description="The operator is the unit of automation in a software factory. Here are the four operator patterns and the one anti-pattern to avoid."
ogType="article"
canonicalUrl="/blog/operator-patterns-for-software-factories"
pageType="blog"
>
<article class="post">
<header class="post-header">
<div class="post-meta">
<span class="post-date">June 19, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h1>Kubernetes Operator Patterns for Software Factories</h1>
</header>
<div class="post-content">
<p class="lead-question">An operator is a control loop with a domain in it. That is the whole definition. Everything else is implementation.</p>
<p>A factory is composed of operators, each one owning a seam. The seams are the spec, the build, the test, the deploy, the promote, the rollback. Each seam gets one operator. The operators do not call each other. The operators watch the cluster and react.</p>
<h2>The spec operator</h2>
<p>The spec operator owns a <code>SoftwareSpec</code> custom resource. A human writes the spec. The operator validates the spec. The operator emits a normalized form into the cluster, signed, with provenance attached.</p>
<p>The spec operator is the only operator that takes human input. Every other operator in the factory watches a spec-shaped resource. The spec operator is the auditor's first stop: what specs exist, who wrote them, what versions are live.</p>
<p>Keep it thin. The spec operator's job is to validate and emit. It does not run tests. It does not trigger builds. It does not know what a build is.</p>
<h2>The build operator</h2>
<p>The build operator watches a normalized spec. The build operator produces a <code>BuildArtifact</code> custom resource containing the hash, the SBOM, the provenance graph, and the signed manifest. The build operator does not know about clusters. The build operator does not know about deployment. The build operator's job is to take a spec and produce an artifact that other operators can verify.</p>
<p>The build operator is the operator that wants to know too much. Every time the build operator learns about tests, about deployment, about promotion, it becomes unmaintainable. The build operator that survives is the one that does one thing. Boring is what you want from a build operator.</p>
<h2>The deploy operator</h2>
<p>The deploy operator watches a signed <code>BuildArtifact</code> and a target environment. The deploy operator produces a <code>Deployment</code> custom resource. The deploy operator's job is to take a verified artifact and put it in a verified place.</p>
<p>The deploy operator is where most teams make their first mistake. They have the deploy operator trigger a kubectl apply, or call a Helm release, or invoke a serverless deploy. All of these are wrong. The deploy operator should produce a declarative resource, and a separate, generic Kubernetes primitive should reconcile to that resource. The deploy operator is the translator. The cluster is the executor.</p>
<p>When the deploy operator produces a resource, the resource can be inspected, audited, rolled back, frozen. When the deploy operator pushes to a remote API, none of those things are true. The remote API is the system. The remote API is what the auditor cannot see.</p>
<h2>The policy operator</h2>
<p>The policy operator is the seam. The policy operator watches deployments and consults a policy bundle. The policy bundle is a versioned, signed set of rules. The policy operator's job is to decide which deployments are allowed to promote to which environment.</p>
<p>The policy operator is the one that is hardest to write. The off-the-shelf policy engines (OPA, Cedar, Rego, the rest) cannot express what most factories need without a policy file that nobody on the team can read. The version that survives is a custom DSL, small enough to fit on a page, expressive enough to encode the rules the business actually needs.</p>
<p>The policy operator is where the human's authority lives in a dark factory. The policy is the human's signature on the system. The policy operator enforces that signature. Everything else in the factory can be fully dark. The policy operator is the part that cannot be.</p>
<h2>The anti-pattern</h2>
<p>One operator per application. One operator per microservice. The factory has many operators, and every one of them is a slightly different shape. The team spends more time learning the operators than they spend shipping product.</p>
<p>The mistake is treating the operator as a wrapper around the application. The operator is not a wrapper around the application. The operator is a wrapper around a <em>type of change</em>. The factory has one operator per type of change, regardless of how many applications use that type. The application is a parameter, not a peer.</p>
<h2>The composition rule</h2>
<p>Operators in a factory do not call each other. Operators watch resources. Operators emit resources. The composition happens in the cluster's etcd layer, not in operator code.</p>
<p>This rule sounds like bureaucracy. It is not. It is what makes the factory auditable. When operator A triggers operator B, the trigger is a side effect of state, and the state is in etcd, and the etcd is queryable. When operator A calls operator B's API, the trigger is hidden in operator A's code, and the audit is impossible. Choose the auditable version.</p>
</div>
</article>
</BaseLayout>
<style>
.post {
max-width: 720px;
margin: 0 auto;
padding: 12rem 2rem 6rem;
}
.post-header {
margin-bottom: 3rem;
}
.post-meta {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.post-date {
color: var(--color-text-muted);
}
.post-tag {
color: var(--color-amber);
font-weight: 600;
}
.lead-question {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-brown-dark);
margin-bottom: 1.5rem;
line-height: 1.5;
}
.post-content h2 {
font-family: var(--font-heading);
font-size: 1.5rem;
color: var(--color-brown-dark);
margin: 2.5rem 0 1rem;
}
.post-content p {
line-height: 1.7;
margin-bottom: 1.25rem;
}
.post-content code {
background: var(--color-cream);
padding: 0.125rem 0.375rem;
border-radius: 4px;
font-size: 0.9em;
}
.post-content strong {
color: var(--color-brown-dark);
}
</style>

View file

@ -0,0 +1,121 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
---
<BaseLayout
title="The Taste Problem: What Dark Factories Can't Transfer"
description="A dark factory can transfer code. It cannot transfer the judgment that knew the code was right. That is the problem provenance was invented to solve."
ogType="article"
canonicalUrl="/blog/the-taste-problem"
pageType="blog"
>
<article class="post">
<header class="post-header">
<div class="post-meta">
<span class="post-date">June 29, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h1>The Taste Problem: What Dark Factories Can't Transfer</h1>
</header>
<div class="post-content">
<p class="lead-question">A dark factory can ship code. It cannot ship the judgment that knew the code was right.</p>
<p>That is taste. Taste is the human judgment that knows the right shape of a system before it exists.</p>
<h2>Why the dark factory has a taste problem</h2>
<p>A dark factory takes a spec and produces an artifact. The artifact is the part the factory can verify. The spec is the part the factory cannot verify, because the spec is the part the human wrote.</p>
<p>If the spec is wrong, the artifact is wrong. The factory will produce the wrong artifact at the same speed it produces the right one. The factory is the wrong shape of the wrong thing faster than any human could.</p>
<p>Teams discover this eventually. The factory is shipping. The customers are complaining. The metrics are flat. Someone looks at the artifacts and realizes the artifacts are exactly what the specs said to build. The specs are exactly what the engineers wrote. The engineers wrote what the market asked for. The market asked for the wrong thing. The factory shipped the wrong thing at scale.</p>
<p>The factory did not break. The factory did what it was told. The factory is not the failure mode. The factory is the amplifier. The judgment that was missing is the failure mode. The factory just made the missing judgment load-bearing.</p>
<h2>What "encode taste" actually means</h2>
<p>There is a school of thought that says you can encode taste into the spec. The spec gets more detailed. The spec captures the shape. The spec becomes the artifact's design doc, and the design doc gets reviewed.</p>
<p>That school of thought is half right. Some taste encodes. Naming conventions, layering, error handling, the explicit invariants. The encoded taste transfers through the factory. The factory produces artifacts that respect the encoded taste. The encoded taste shows up in the artifact's diff.</p>
<p>The other half of taste does not encode. The other half is the part the senior engineer carries and cannot articulate. It is the part that knows the function should be three things, not one, before the function is written. It is the part that knows this commit is going to cause the next three. It is the part that knows the customer is asking for X but they need Y.</p>
<p>You cannot encode that. You can write it down. You can review it. You can put it in a code review checklist. None of those things make it transfer. They make it visible. Visible is not the same as transferred.</p>
<h2>Provenance is the bridge, not the solution</h2>
<p>Provenance gets talked about as if it solves the taste problem. It does not. Provenance tells you where the artifact came from. Provenance does not tell you whether the spec was right.</p>
<p>What provenance does is make the missing judgment legible. When the artifact fails, the provenance graph tells you which spec element the failure traces back to. The auditor can then ask: was that spec element the right call? The question is answerable. The answer is in the spec, not in the artifact.</p>
<p>A microscope does not cure disease. A microscope tells you what disease you have. In a dark factory, the microscope is the only thing that keeps the missing judgment from being invisible until it is too late.</p>
<h2>The two kinds of taste</h2>
<p>Encoded taste is the part that goes in the spec. It transfers. It is reviewable. It is the part the factory can carry forward. The senior engineer's job is to push as much taste as possible into this category. Most of what we call code review is the act of pushing taste from the implicit category to the explicit one.</p>
<p>Unencoded taste is the part that does not go in the spec. It does not transfer. It is reviewable only by other senior engineers who carry the same unencoded taste. It is the part that gets lost when a senior engineer leaves. It is the part that no dark factory, no matter how sophisticated, can carry.</p>
<p>The dark factory does not have to carry the unencoded taste. The dark factory has to make the unencoded taste visible, so the human's audit can find the missing pieces.</p>
<p>You cannot hire your way out of this. You cannot vendor your way out of this. You can only become the kind of engineer whose taste transfers through the spec, and accept that the rest of it has to stay in the audit.</p>
</div>
</article>
</BaseLayout>
<style>
.post {
max-width: 720px;
margin: 0 auto;
padding: 12rem 2rem 6rem;
}
.post-header {
margin-bottom: 3rem;
}
.post-meta {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.post-date {
color: var(--color-text-muted);
}
.post-tag {
color: var(--color-amber);
font-weight: 600;
}
.lead-question {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-brown-dark);
margin-bottom: 1.5rem;
line-height: 1.5;
}
.post-content h2 {
font-family: var(--font-heading);
font-size: 1.5rem;
color: var(--color-brown-dark);
margin: 2.5rem 0 1rem;
}
.post-content p {
line-height: 1.7;
margin-bottom: 1.25rem;
}
.post-content strong {
color: var(--color-brown-dark);
}
</style>

View file

@ -0,0 +1,131 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
---
<BaseLayout
title="What a Dark Factory Actually Is (and Isn't)"
description="The phrase is everywhere. The working meaning is gone. Here is what it is, what it isn't, and the five positions on the spectrum."
ogType="article"
canonicalUrl="/blog/what-is-a-dark-factory"
pageType="blog"
>
<article class="post">
<header class="post-header">
<div class="post-meta">
<span class="post-date">May 22, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h1>What a Dark Factory Actually Is (and Isn't)</h1>
</header>
<div class="post-content">
<p class="lead-question">The phrase is everywhere. The working meaning is gone.</p>
<p>I sat down to write about software factories, and the first thing I had to do was take the word back. Every vendor has a "dark factory" slide. Every Devops team has a "lights-out deployment" demo. The phrase has been stripped the way "platform engineering" was stripped, the way "Devops" was stripped before that.</p>
<p>The marketing version: AI writes the code, the pipeline ships the code, humans go home early. That is a fantasy. It assumes the spec is correct, the spec is complete, the spec captures every invariant the running system has to preserve. None of those are true today.</p>
<p>The working version is a smaller claim, and a more useful one.</p>
<h2>The working definition</h2>
<p>A <strong>dark factory</strong> is a build pipeline that ships production software with no human in the critical path. Spec in, artifact out. The intermediate steps happen unattended.</p>
<p>The opposite is a <strong>light factory</strong>. Same inputs, same outputs, but a human signs off between every major stage. The human is the gate.</p>
<p>Many teams are not running either. They are running a partial factory. Some stages are dark; CI runs unattended. Some stages are light; the merge to main requires a reviewer. The work is figuring out which stages should be which, and what the seams look like.</p>
<h2>The five positions</h2>
<p>Treating "dark" and "light" as a binary loses the part that matters. The positions, named:</p>
<ol>
<li><strong>Manual.</strong> A person runs the commands. No factory yet; the engineer is the build system.</li>
<li><strong>Assisted.</strong> CI exists. Tests run. A person merges. Many teams live here and call it Devops.</li>
<li><strong>Light.</strong> Merge is gated by policy, not by reviewer. The pipeline is the gate. A human is the auditor, not the approver.</li>
<li><strong>Dark, gated.</strong> The pipeline ships. A human is the only one who can promote from staging to production. The factory runs, but it cannot cross a particular seam without a key turn.</li>
<li><strong>Dark, full.</strong> The pipeline ships. Promotion is policy-driven. The human's role is to write the policy and the spec, then to inspect the artifacts the factory produces.</li>
</ol>
<p>The interesting range is between three and four. The question is not how to get to five. The question is which of your stages should be at which position, and why.</p>
<h2>What the framing changes</h2>
<p>Calling it a factory reallocates where the engineering effort goes.</p>
<p>You stop asking who is on call for the deploy and start asking what the failure mode of the deploy step is. You stop asking how fast your engineers can merge and start asking how fast the policy can express a correct merge. You stop asking whether the code was reviewed and start asking whether the spec the code was generated from is auditable.</p>
<p>A factory is designed, not grown. A factory is maintained, not improvised. A factory has a failure budget, not a heroism budget.</p>
<p>If the build system you are running is a pile of bash scripts and a Slack channel, the factory framing is going to be uncomfortable. That is the point.</p>
</div>
</article>
</BaseLayout>
<style>
.post {
max-width: 720px;
margin: 0 auto;
padding: 12rem 2rem 6rem;
}
.post-header {
margin-bottom: 3rem;
}
.post-meta {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.post-date {
color: var(--color-text-muted);
}
.post-tag {
color: var(--color-amber);
font-weight: 600;
}
.lead-question {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-brown-dark);
margin-bottom: 1.5rem;
line-height: 1.5;
}
.post-content h2 {
font-family: var(--font-heading);
font-size: 1.5rem;
color: var(--color-brown-dark);
margin: 2.5rem 0 1rem;
}
.post-content p {
line-height: 1.7;
margin-bottom: 1.25rem;
}
.post-content ol,
.post-content ul {
line-height: 1.7;
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.post-content li {
margin-bottom: 0.5rem;
}
.post-content a {
color: var(--color-amber);
text-decoration: underline;
}
</style>

View file

@ -0,0 +1,136 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
---
<BaseLayout
title="Why Kubernetes for a Dark Factory"
description="A dark factory is a control loop. Kubernetes is the most boring control loop in production. That is the reason."
ogType="article"
canonicalUrl="/blog/why-kubernetes-for-dark-factory"
pageType="blog"
>
<article class="post">
<header class="post-header">
<div class="post-meta">
<span class="post-date">June 11, 2026</span>
<span class="post-tag">Engineering</span>
</div>
<h1>Why Kubernetes for a Dark Factory</h1>
</header>
<div class="post-content">
<p class="lead-question">A dark factory is a control loop. Kubernetes is the most boring control loop in production. That is the reason.</p>
<h2>What the substrate has to do</h2>
<p>A dark factory has four requirements from whatever it runs on.</p>
<ol>
<li><strong>Declarative desired state.</strong> The system has to be able to say "I want N copies of this artifact running, signed, behind this policy," and have the substrate reconcile to that state unattended.</li>
<li><strong>Restart and replace as a primitive.</strong> Failures are the steady state. "Kill it and start a new one" has to be cheap, automatic, and observable.</li>
<li><strong>An operator-extensible control plane.</strong> The factory grows new resource types as the system grows. The substrate has to treat those types as first-class, not as jobs squashed into the lowest common denominator.</li>
<li><strong>Boring upgrades.</strong> The factory runs unattended. The substrate cannot require a person to read release notes on a Saturday.</li>
</ol>
<p>Kubernetes is not special because it is fast, or because it scales, or because it is cloud-native. Kubernetes is special because it is the only widely-deployed substrate that makes all four of those requirements boring.</p>
<h2>Why the alternatives fall short</h2>
<p><strong>Serverless</strong> (Lambda, Cloud Functions, Workers) gets close on restart-and-replace. It fails on declarative desired state. The platform owns the loop, not you. The moment you need a custom resource type, the platform tells you no. A factory that wants to define a <code>BuildRun</code> cannot do it on Lambda.</p>
<p><strong>Systemd on a VM</strong> gets close on boring upgrades. It fails on the operator-extensible control plane. The minute you want a custom resource type, you are writing your own reconciler. You are now maintaining a control plane in addition to the factory. The single engineer does not have that time.</p>
<p><strong>Nomad</strong> is the most underrated of the three. Nomad is what Kubernetes would look like if HashiCorp had cared more about operator patterns and less about service mesh. Nomad is excellent. Nomad is also a single-vendor system, and the ecosystem of pre-built operators and CRDs does not exist. You will build everything from scratch.</p>
<p><strong>Docker Compose on a single host</strong> works until the host dies. Then the dark factory is dark in a different sense. The lights are off. The artifact is gone. You find out what your backup posture actually is.</p>
<h2>The single-engineer argument</h2>
<p>An operator is a reconciler. The operator watches a custom resource. The operator makes the cluster match the resource. The operator is written in Go and packaged as a container. The operator runs the same way the artifact runs. The operator is part of the factory.</p>
<p>A platform team cannot build a custom resource type faster than a single engineer with an operator template. The single engineer's bottleneck is the spec, not the infrastructure. The infrastructure is the easy part. The hard part is figuring out what the factory should do, and that is the same problem at any team size.</p>
<h2>What Kubernetes does not give you</h2>
<p>Kubernetes does not give you a factory. Kubernetes gives you a substrate on which a factory can be built. The gap between "I have a cluster" and "I have a factory" is where the operator pattern lives.</p>
<p>Kubernetes also does not give you provenance. The cluster will run whatever artifact you give it. The cluster does not know where the artifact came from. The cluster does not know whether the artifact was generated from a spec, or by a human, or by a model with no spec. The cluster does not care. Provenance is a layer above the substrate, and that is where the dark factory actually lives.</p>
<p>If you are a single engineer trying to build a dark factory, the choice of substrate is not really a choice. Kubernetes is the answer. The interesting work is what you build on top of it.</p>
</div>
</article>
</BaseLayout>
<style>
.post {
max-width: 720px;
margin: 0 auto;
padding: 12rem 2rem 6rem;
}
.post-header {
margin-bottom: 3rem;
}
.post-meta {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.post-date {
color: var(--color-text-muted);
}
.post-tag {
color: var(--color-amber);
font-weight: 600;
}
.lead-question {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-brown-dark);
margin-bottom: 1.5rem;
line-height: 1.5;
}
.post-content h2 {
font-family: var(--font-heading);
font-size: 1.5rem;
color: var(--color-brown-dark);
margin: 2.5rem 0 1rem;
}
.post-content p {
line-height: 1.7;
margin-bottom: 1.25rem;
}
.post-content ol,
.post-content ul {
line-height: 1.7;
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.post-content li {
margin-bottom: 0.5rem;
}
.post-content code {
background: var(--color-cream);
padding: 0.125rem 0.375rem;
border-radius: 4px;
font-size: 0.9em;
}
.post-content strong {
color: var(--color-brown-dark);
}
</style>

View file

@ -49,19 +49,6 @@ import SharedForm from '../components/Form/SharedForm.astro';
</div>
</article>
<article class="book-card coming-soon">
<div class="book-cover">
<img src="/images/book-cover-dark-provenance.svg" alt="Dark Provenance book cover" width="150" height="225" loading="lazy" />
</div>
<h2>Dark Provenance</h2>
<p class="book-meta">Coming 2026</p>
<p class="book-description">Formal verification for the AI age. How to audit what your AI actually did.</p>
<p class="book-quote">"Speckl — the compile-time provenance layer. A new approach to AI accountability."</p>
<p class="waitlist-info">Want early access? Email Scott directly to join the waitlist.</p>
<a href="mailto:scott@scoble.me?subject=Dark%20Provenance%20Interest" class="waitlist-btn">Email for Early Access</a>
</article>
</div>
</div>
</section>

View file

@ -129,7 +129,7 @@ import PackageCard from '../components/PackageCard.astro';
}
.section-container {
max-width: 1000px;
max-width: 1200px;
margin: 0 auto;
padding: 4rem 2rem;
}

View file

@ -33,7 +33,7 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
<a href="/speaking#book" class="btn btn-primary" data-umami-event="hero-cta-book">Book Scott for Your Event</a>
</div>
<p class="hero-project-link">
Creator of Speckl, ScobleClaw, and Beyond the Prompt
Creator of <a href="https://speckl.scoble.me">Speckl</a>, <a href="https://os.scoble.me/forgejo/sscoble/scobleclaw">ScobleClaw</a>, and <a href="https://os.scoble.me/forgejo/sscoble/beyond-the-prompt">Beyond the Prompt</a>
</p>
</div>
</section>
@ -109,6 +109,39 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
</div>
</section>
<!-- Featured Books -->
<section class="featured-books">
<div class="section-header">
<p class="section-kicker">Books</p>
<h2>The Cultivation Series</h2>
</div>
<div class="books-list">
<article class="book-card">
<div class="book-cover">
<img src="/images/book-cover-soil.webp" alt="The Soil Beneath the Code book cover" width="120" height="180" loading="lazy" />
</div>
<div class="book-info">
<h3>The Soil Beneath the Code</h3>
<p class="book-desc">What engineering teams can learn from farmers about sustainable systems. A cultivation playbook for technical leaders who are tired of strip-mining their teams.</p>
<a href="https://a.co/d/0226o4Hg" class="book-cta" target="_blank" rel="noopener" data-umami-event="home-book-soil-amazon">Get It on Amazon →</a>
</div>
</article>
<article class="book-card">
<div class="book-cover">
<img src="/images/book-cover-walk.webp" alt="A Walk in Mentoring book cover" width="120" height="180" loading="lazy" />
</div>
<div class="book-info">
<h3>A Walk in Mentoring</h3>
<p class="book-desc">Three questions that change how people learn. The simplest mentoring framework you'll actually use.</p>
<a href="https://a.co/d/0bRKZeUV" class="book-cta" target="_blank" rel="noopener" data-umami-event="home-book-walk-amazon">Get It on Amazon →</a>
</div>
</article>
</div>
</section>
<!-- CTA Section -->
<section class="cta-section">
<div class="cta-visual"></div>
@ -420,7 +453,68 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
.talk-link:hover {
color: var(--color-brown-dark);
}
/* ===== FEATURED BOOKS ===== */
.featured-books {
padding: 6rem 2rem;
background: white;
}
.books-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
max-width: 1100px;
margin: 0 auto;
}
.featured-books .book-card {
display: flex;
gap: 1.5rem;
padding: 2rem;
border-radius: 12px;
border: 1px solid var(--color-border-light);
background: var(--color-cream);
transition: all 0.3s ease;
}
.featured-books .book-card:hover {
border-color: var(--color-amber);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
}
.book-cover img {
width: 120px;
height: auto;
border-radius: 4px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
flex-shrink: 0;
}
.book-info h3 {
font-family: var(--font-heading);
font-size: 1.375rem;
color: var(--color-brown-dark);
margin: 0 0 0.5rem;
}
.book-info .book-desc {
color: var(--color-text-muted);
margin: 0 0 1rem;
line-height: 1.6;
font-size: 0.95rem;
}
.book-info .book-cta {
color: var(--color-amber);
font-weight: 600;
text-decoration: none;
}
.book-info .book-cta:hover {
color: var(--color-brown-dark);
}
/* ===== CTA SECTION ===== */
.cta-section {
position: relative;
@ -492,7 +586,24 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
min-height: auto;
padding: 4rem 1.5rem;
}
.featured-books .books-list {
grid-template-columns: 1fr;
}
.featured-books .book-card {
flex-direction: column;
align-items: center;
text-align: center;
}
.book-info .book-desc {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.hero-overlay {
background:
radial-gradient(
@ -506,26 +617,26 @@ import NewsletterSignup from '../components/NewsletterSignup.astro';
rgba(20, 18, 15, 0.6) 100%
);
}
.hero h1 {
font-size: clamp(2.25rem, 10vw, 3.5rem);
line-height: 1.05;
}
.hero h1 .accent {
font-size: 0.88em;
}
.section-header {
margin-bottom: 2.5rem;
}
.pillars,
.featured-talks,
.cta-section {
padding: 4rem 1.5rem;
}
.talk-card {
padding: 1.5rem;
}

View file

@ -130,6 +130,48 @@ const posts: PostEntry[] = [
date: "2026-04-28",
tags: ["Engineering"],
},
{
title: "What a Dark Factory Actually Is (and Isn't)",
description: "The phrase is everywhere. The working meaning is gone. Here is what it is, what it isn't, and the five positions on the spectrum.",
url: "/blog/what-is-a-dark-factory",
date: "2026-05-22",
tags: ["Engineering"],
},
{
title: "Light vs Dark Factories: Why the Middle Is Harder Than Either End",
description: "A light factory has too many gates. A dark factory has too few. The middle is where the engineering happens, and the middle is the part nobody talks about.",
url: "/blog/light-vs-dark-factories",
date: "2026-06-02",
tags: ["Engineering"],
},
{
title: "Why Kubernetes for a Dark Factory",
description: "A dark factory is a control loop. Kubernetes is the most boring control loop in production. That is the reason.",
url: "/blog/why-kubernetes-for-dark-factory",
date: "2026-06-11",
tags: ["Engineering"],
},
{
title: "Kubernetes Operator Patterns for Software Factories",
description: "An operator is a control loop with a domain in it. That is the whole definition. Here are the four operator patterns and the one anti-pattern to avoid.",
url: "/blog/operator-patterns-for-software-factories",
date: "2026-06-19",
tags: ["Engineering"],
},
{
title: "The Taste Problem: What Dark Factories Can't Transfer",
description: "A dark factory can transfer code. It cannot transfer the judgment that knew the code was right. That is the problem provenance was invented to solve.",
url: "/blog/the-taste-problem",
date: "2026-06-29",
tags: ["Engineering"],
},
{
title: "The Human-in-the-Loop Seam: Where Humans Belong in a Dark Factory",
description: "Human-in-the-loop is a phrase everyone uses. Almost nobody uses it correctly. The seam is in the spec, not in the implementation.",
url: "/blog/human-in-the-loop-seam",
date: "2026-07-08",
tags: ["Engineering"],
},
];
const siteUrl = 'https://scott.scoble.me';

View file

@ -3,44 +3,56 @@ import type { APIRoute } from 'astro';
const siteUrl = 'https://scott.scoble.me';
const pages = [
{ url: '/', lastmod: '2026-05-24', priority: '1.0' },
{ url: '/speaking', lastmod: '2026-05-24', priority: '0.9' },
{ url: '/consulting', lastmod: '2026-05-24', priority: '0.8' },
{ url: '/books', lastmod: '2026-05-24', priority: '0.8' },
{ url: '/contact', lastmod: '2026-05-24', priority: '0.7' },
{ url: '/privacy', lastmod: '2026-05-24', priority: '0.3' },
{ url: '/about', lastmod: '2026-05-24', priority: '0.7' },
{ url: '/blog', lastmod: '2026-05-24', priority: '0.9' },
{ url: '/', lastmod: '2026-06-01', priority: '1.0' },
{ url: '/speaking', lastmod: '2026-06-01', priority: '0.9' },
{ url: '/consulting', lastmod: '2026-06-01', priority: '0.8' },
{ url: '/books', lastmod: '2026-06-01', priority: '0.8' },
{ url: '/contact', lastmod: '2026-06-01', priority: '0.7' },
{ url: '/privacy', lastmod: '2026-06-01', priority: '0.3' },
{ url: '/about', lastmod: '2026-06-01', priority: '0.7' },
{ url: '/blog', lastmod: '2026-06-01', priority: '0.9' },
];
const speakingPages = [
{ url: '/speaking/50-first-dates', lastmod: '2026-06-01' },
{ url: '/speaking/strip-mining-to-cultivation', lastmod: '2026-06-01' },
{ url: '/speaking/soil-under-your-team', lastmod: '2026-06-01' },
];
// All blog post URLs
const blogPosts = [
{ url: '/blog/50-first-dates-with-ai', lastmod: '2026-05-24' },
{ url: '/blog/strip-mining-cultivation', lastmod: '2026-05-18' },
{ url: '/blog/soil-health-teams', lastmod: '2026-05-18' },
{ url: '/blog/30-years-software', lastmod: '2026-05-18' },
{ url: '/blog/spec-first-audit-surface', lastmod: '2026-05-24' },
{ url: '/blog/open-source-projects', lastmod: '2026-05-23' },
{ url: '/blog/prove-sqlite', lastmod: '2026-05-23' },
{ url: '/blog/ai-has-amnesia', lastmod: '2026-05-18' },
{ url: '/blog/agentic-wars', lastmod: '2026-05-08' },
{ url: '/blog/ai-brain-fry', lastmod: '2026-05-03' },
{ url: '/blog/surveillance-extraction', lastmod: '2026-05-03' },
{ url: '/blog/gallup-disengagement', lastmod: '2026-05-03' },
{ url: '/blog/devex-shift', lastmod: '2026-05-03' },
{ url: '/blog/token-anxiety', lastmod: '2026-05-04' },
{ url: '/blog/three-questions-mentoring', lastmod: '2026-04-28' },
{ url: '/blog/strip-mining-signs', lastmod: '2026-04-28' },
{ url: '/blog/the-soil-beneath-the-code', lastmod: '2026-04-28' },
{ url: '/blog/strip-mining-happening-right-now', lastmod: '2026-05-07' },
{ url: '/blog/mentorship-isnt-latency-its-leverage', lastmod: '2026-05-07' },
{ url: '/blog/ai-exposes-leaders-cultivation-test', lastmod: '2026-05-07' },
{ url: '/blog/50-first-dates-with-ai', lastmod: '2026-05-30' },
{ url: '/blog/strip-mining-cultivation', lastmod: '2026-05-30' },
{ url: '/blog/soil-health-teams', lastmod: '2026-05-30' },
{ url: '/blog/30-years-software', lastmod: '2026-05-30' },
{ url: '/blog/spec-first-audit-surface', lastmod: '2026-05-30' },
{ url: '/blog/open-source-projects', lastmod: '2026-05-30' },
{ url: '/blog/prove-sqlite', lastmod: '2026-05-30' },
{ url: '/blog/ai-has-amnesia', lastmod: '2026-05-30' },
{ url: '/blog/agentic-wars', lastmod: '2026-05-30' },
{ url: '/blog/ai-brain-fry', lastmod: '2026-05-30' },
{ url: '/blog/surveillance-extraction', lastmod: '2026-05-30' },
{ url: '/blog/gallup-disengagement', lastmod: '2026-05-30' },
{ url: '/blog/devex-shift', lastmod: '2026-05-30' },
{ url: '/blog/token-anxiety', lastmod: '2026-05-30' },
{ url: '/blog/three-questions-mentoring', lastmod: '2026-05-30' },
{ url: '/blog/strip-mining-signs', lastmod: '2026-05-30' },
{ url: '/blog/the-soil-beneath-the-code', lastmod: '2026-05-30' },
{ url: '/blog/strip-mining-happening-right-now', lastmod: '2026-05-30' },
{ url: '/blog/mentorship-isnt-latency-its-leverage', lastmod: '2026-05-30' },
{ url: '/blog/ai-exposes-leaders-cultivation-test', lastmod: '2026-05-30' },
{ url: '/blog/what-is-a-dark-factory', lastmod: '2026-05-22' },
{ url: '/blog/light-vs-dark-factories', lastmod: '2026-06-02' },
{ url: '/blog/why-kubernetes-for-dark-factory', lastmod: '2026-06-11' },
{ url: '/blog/operator-patterns-for-software-factories', lastmod: '2026-06-19' },
{ url: '/blog/the-taste-problem', lastmod: '2026-06-29' },
{ url: '/blog/human-in-the-loop-seam', lastmod: '2026-07-08' },
];
const allPages = [...pages, ...blogPosts.map(p => ({ ...p, priority: '0.7' }))];
const allPages = [...pages, ...speakingPages.map(p => ({ ...p, priority: '0.7' })), ...blogPosts.map(p => ({ ...p, priority: '0.7' }))];
// Render the sitemap XML
const xml = `<?xml version="1.0" encoding="UTF-8"?>
const xml = `\<\?xml version="1.0" encoding="UTF-8"?\>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${allPages.map(page => `
<url>

View file

@ -53,6 +53,7 @@ import SharedForm from '../components/Form/SharedForm.astro';
<TalkCard
title="The Soil Under Your Team"
slug="/speaking/soil-under-your-team"
premise="Diagnose what's actually depleted before you fertilize. A diagnostic framework for team health in AI-augmented work."
audience="Team leads, managers, engineering directors, founders"
takeaways={[