Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .agentworkforce/workforce/personas/operator.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .agentworkforce/workforce/skills/content-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ These patterns mark content as AI-generated. Run every draft against this list.
- Staccato fragmentation: "No X. No Y. No Z." (tricolon declarations)
- Em dash overuse: more than 1-2 per section
- Filler adverbs: "actually", "literally", "quietly", "fundamentally", "essentially"
- "Why does X matter?" headings or sentences — pure AI tell, no human frames their own writing this way

### Tier 2: Rewrite if possible

Expand Down
2 changes: 2 additions & 0 deletions .agentworkforce/workforce/skills/essay-authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ Before publishing, check the essay against these anti-patterns. Each one slipped
- **Branded phrase repetition.** "Clock, listener, inbox" at most 2-3 times per essay (except three-primitives.mdx). Vary with "the three primitives," "the runtime's core," or just describe the concept.
- **Tricolon overuse.** "Not X. Not Y. Not Z." at most once per essay. Multiple parallel structures with identical openings are an LLM tell.
- **Em-dash density.** 1-2 per section. Convert excess to commas, colons, parentheses, or restructure.
- **"Why does X matter?"** Never use this as a heading or sentence. No human blogger frames their own writing by asking why their own topic matters. Make headings specific to the content instead.

## Checklist for every article

Expand All @@ -149,5 +150,6 @@ Before considering an article complete:
- [ ] No more than one tricolon per essay
- [ ] Em dashes limited to 1-2 per section
- [ ] No "This matters because" sentence openers
- [ ] No "Why does X matter?" headings or sentences — pure AI tell
- [ ] No "uncomfortable truth/math/pattern" labels
- [ ] No fabricated incidents, outages, or operational claims the founder didn't experience
107 changes: 107 additions & 0 deletions .agentworkforce/workforce/skills/geo-optimization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# GEO Optimization Skill

Generative Engine Optimization (GEO) makes content discoverable and citable by AI answer engines (ChatGPT, Perplexity, Gemini, Claude). Load this skill when writing, editing, or auditing any content for AI discoverability.

GEO and SEO are not separate workflows. Every deliverable must satisfy both.

## Core Principles

1. **Answer first.** Lead every page and section with the direct answer. AI systems extract the first substantive statement as the candidate response. Bury the answer and you lose the citation.
2. **High factual density.** Specific numbers, named tools, concrete examples. Vague prose gets skipped by retrieval systems. "6-8 weeks per provider" beats "it takes a while."
3. **Citation-friendly claims.** Every claim should be attributable. Link sources, name products, reference your own production data. AI models prefer content they can verify.
4. **Entity clarity.** Name things precisely on first use. "Proactive agent" not "this kind of system." Consistent terminology helps retrieval match queries to content.

## Content Structure Requirements

### Atomic pages
Each page should answer one primary question completely. AI retrieval works best with focused, self-contained content. A page that covers three topics well loses to three pages that each cover one topic thoroughly.

### Section chunking
Keep sections between 200-400 words. AI retrieval systems chunk content by headings. Oversized sections (>400 words) get split at arbitrary points, breaking the answer. Undersized sections (<200 words) may lack enough context to be useful standalone.

When a section exceeds 400 words, split it with an H3 subheading that describes the new subsection's content. Don't add headings for the sake of it — each heading should introduce a meaningfully distinct subtopic.

### Heading structure
- H2s define major topic boundaries
- H3s break long sections into retrievable chunks
- Question-framed headings help AI systems match user queries to content (e.g., "How does deduplication work?" or "What infrastructure does a single webhook require?")
- **Never** use "Why does X matter?" as a heading — it's a pure AI tell. Make headings specific to the content instead.
- Headings should be descriptive enough to stand alone as a topic identifier in search results or AI citations

### Code blocks
Always tag code blocks with a language identifier (`typescript`, `markdown`, `json`, etc.). Untagged code blocks lose syntax context in AI retrieval and render poorly in citation previews.

## Freshness Signals

### dateModified
Every post must include a `lastModified` field in frontmatter. This powers the `dateModified` property in Article JSON-LD schema. AI systems weight fresher content higher in citations.

Update `lastModified` whenever you make substantive content changes (not typo fixes or formatting). The `articleSchema()` function in `lib/seo.ts` uses `post.lastModified ?? post.date` for the dateModified value.

### Content currency
Reference current tools, versions, and pricing. Outdated references (deprecated APIs, old pricing tiers, discontinued products) signal staleness to AI systems.

## Structured Data

### Required schemas
Every post page must include:
- `Article` schema with accurate `datePublished` and `dateModified`
- `BreadcrumbList` schema for navigation context

### Additional schemas (when applicable)
- `FAQPage` for posts structured around questions and answers
- `HowTo` for step-by-step implementation guides
- `DefinedTerm` for posts that define key concepts

Use the helpers in `lib/seo.ts` (`articleSchema`, `breadcrumbSchema`, `faqSchema`, `howToSchema`, `definedTermSchema`).

## LLM-Readable Content

### llms.txt
The site publishes `/llms.txt` (index) and `/llms-full.txt` (full text of all essays). Both are regenerated by build scripts:
- `node scripts/generate-markdown.mjs` — generates `/public/posts/*.md` plain-text copies
- `node scripts/generate-llms-full.mjs` — generates `/public/llms-full.txt` from all MDX sources

After writing or editing any essay, run both scripts (they also run during `npm run build` and `npm run dev`).

### llms.txt maintenance
- Every published post must appear in `/public/llms.txt` with its title and URL
- Post titles in llms.txt must match frontmatter titles exactly
- The llms-full.txt file must include the complete text of every essay

## AI Crawler Access

The site's `robots.txt` explicitly allows AI crawlers:
- GPTBot (OpenAI)
- ClaudeBot (Anthropic)
- PerplexityBot
- Amazonbot
- Google-Extended

Do not add restrictions for these crawlers unless specifically instructed.

## Internal Linking for Retrieval

Internal links serve double duty in GEO: they help AI systems understand topic relationships and they keep crawlers moving through the content graph.

- Every post should have at least 2-3 outgoing internal links to related essays
- Link text should be descriptive (not "click here" or "this post")
- Orphan pages (0 incoming links) are invisible to crawlers — fix immediately

## GEO Audit Checklist

Run this checklist when auditing or publishing any content:

- [ ] Answer-first structure: primary answer appears in first paragraph or section
- [ ] Factual density: specific numbers, named tools, concrete examples throughout
- [ ] Section chunking: no section exceeds 400 words without H3 subheadings
- [ ] Code blocks: all tagged with language identifiers
- [ ] Headings: descriptive, no "Why does X matter?" patterns
- [ ] `lastModified` frontmatter field set to current date
- [ ] Article schema includes accurate `dateModified`
- [ ] Post appears in `/public/llms.txt` with correct title
- [ ] At least 2-3 outgoing internal links
- [ ] At least 1 incoming internal link from another post
- [ ] External references linked (companies, tools, papers)
- [ ] Entity names used consistently throughout
- [ ] `generate-markdown.mjs` and `generate-llms-full.mjs` run after changes
1 change: 1 addition & 0 deletions .agentworkforce/workforce/skills/khaliq-voice.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ When writing as Khaliq, NEVER:
- Use "a series that only X isn't really Y" or similar meta-commentary that sounds like an essayist performing self-awareness
- Use "wild" as a reaction word ("that's wild," "kind of wild") — it's an AI tell that shows up constantly in generated text
- Frame personal observations as universal truths. Say "I've found" or "I've seen" instead of declaring how things are
- Write "Why does X matter?" as a heading or sentence — it's a pure AI tell. No human blogger frames their own writing by asking why their topic matters. Make headings specific to the content.

## How Khaliq actually sounds vs AI-polished versions

Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Your output must always include production-ready content or implementation artif
6. Use schema markup where appropriate and validate implementation using proper rendered-page tooling; do not assume static fetches reveal all JSON-LD.
7. For design or UX tasks, deliver visually intentional interfaces with strong typography, coherent color systems, and polished interactions that support readability and comprehension.
8. Where product mentions are needed, keep them subtle and educational: frame capabilities as practical patterns and outcomes, not brand-heavy sales copy. Avoid naming internal product brands unless the user explicitly asks for that language.
9. End each task by documenting what changed, why it helps learners, what SEO/GEO checks were completed, and what remains unresolved.
9. After writing or editing any essay, regenerate the public markdown copies and full-text LLM file by running `node scripts/generate-markdown.mjs && node scripts/generate-llms-full.mjs`. These also run automatically during `npm run build` and `npm run dev`.
10. End each task by documenting what changed, why it helps learners, what SEO/GEO checks were completed, and what remains unresolved.

## Writing Voice & Editorial

Expand All @@ -42,6 +43,7 @@ Your output must always include production-ready content or implementation artif
- The branded terminology ("clock, listener, inbox" / "the triple" / "the three primitives") should appear at most 2-3 times per essay outside of three-primitives.mdx, which defines it. In other posts, vary the reference or describe the concept without the formula. Repetition across posts makes the series feel like one idea said six ways.
- Avoid tricolon lists ("Not X. Not Y. Not Z.") more than once per essay. Parallel structures where three items start with the same word are an LLM tell. One instance can be rhetorical; two is a pattern.
- Never use "This matters because" as a sentence opener. It's a filler phrase that adds nothing. Just state why it matters. Similarly, avoid "uncomfortable truth/math/pattern" as labels — they're AI-flavored drama markers. Say what the thing is instead of announcing that it's uncomfortable.
- Never write "Why does X matter?" as a heading or sentence. It's a pure AI tell — no human blogger frames their own writing this way. Instead, make the heading specific to the content: "How the Slack-native approach compares" not "Why does Slack-native matter?"

## Tech Stack Conventions

Expand Down
4 changes: 2 additions & 2 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

@theme {
--font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
--font-serif: var(--font-fraunces), ui-serif, Georgia, serif;
--font-display: var(--font-instrument), ui-serif, Georgia, serif;
--font-serif: var(--font-literata), ui-serif, georgia, serif;
--font-display: var(--font-literata), ui-serif, georgia, serif;
--font-mono: var(--font-jetbrains), ui-monospace, monospace;

--color-paper: #fbf6ec;
Expand Down
17 changes: 5 additions & 12 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { Inter, Fraunces, Instrument_Serif, JetBrains_Mono } from "next/font/google";
import { Inter, Literata, JetBrains_Mono } from "next/font/google";
import Script from "next/script";
import { SiteNav } from "@/components/site-nav";
import { SiteFooter } from "@/components/site-footer";
Expand All @@ -17,19 +17,12 @@ const inter = Inter({
display: "swap",
});

const fraunces = Fraunces({
variable: "--font-fraunces",
const literata = Literata({
variable: "--font-literata",
subsets: ["latin"],
display: "swap",
axes: ["opsz", "SOFT"],
});

const instrument = Instrument_Serif({
variable: "--font-instrument",
subsets: ["latin"],
weight: ["400"],
axes: ["opsz"],
style: ["normal", "italic"],
display: "swap",
});

const jetbrains = JetBrains_Mono({
Expand Down Expand Up @@ -97,7 +90,7 @@ export default function RootLayout({
return (
<html
lang="en"
className={`${inter.variable} ${fraunces.variable} ${instrument.variable} ${jetbrains.variable} antialiased`}
className={`${inter.variable} ${literata.variable} ${jetbrains.variable} antialiased`}
>
<head>
<link rel="alternate" type="text/plain" href="/llms.txt" title="LLM-readable site summary" />
Expand Down
14 changes: 6 additions & 8 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -325,14 +325,12 @@ export default async function Home() {
<ol className="mt-14 grid gap-8 md:grid-cols-3">
{HARD_PARTS.map((h, i) => (
<li key={h.label} className="reveal">
<div className="flex items-baseline gap-3">
<span className="font-display text-4xl italic text-terracotta">
{String(i + 1).padStart(2, "0")}
</span>
<h3 className="font-display text-xl leading-tight text-ink">
{h.label}
</h3>
</div>
<span className="font-display text-4xl italic text-terracotta">
{String(i + 1).padStart(2, "0")}
</span>
<h3 className="mt-2 font-display text-xl leading-tight text-ink">
{h.label}
</h3>
<p
className="mt-3 font-serif text-[1rem] leading-relaxed text-ink-soft"
dangerouslySetInnerHTML={{ __html: h.body }}
Expand Down
7 changes: 7 additions & 0 deletions components/site-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export function SiteFooter() {
<div className="mt-10 flex flex-col items-start justify-between gap-2 border-t border-rule/60 pt-6 text-xs text-ink-faint sm:flex-row sm:items-center">
<p>© {new Date().getFullYear()} Proactive Agents.</p>
<p>
<a
href="https://github.com/AgentWorkforce/proactive-agents"
className="text-terracotta hover:underline"
>
Open source
</a>
{" · "}
Crafted by{" "}
<a
href="https://github.com/AgentWorkforce"
Expand Down
17 changes: 17 additions & 0 deletions content-system/llms-full-footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Agent Activity Log

This site is operated by proactive agents that demonstrate the concepts it teaches:

- **Notion → blog** (change-triggered): Watches a Notion Drafts database and publishes finished pages as MDX.
- **Weekly digest** (time-triggered): Searches the web and Reddit for proactive-agent mentions weekly.
- **Sunday ping** (time-triggered): Reads the week's digest, drafts a post outline, and pings the author on Slack.
- **PR reviewer** (change-triggered): Comments on PRs with deploy preview, dead-link check, and copy-edit notes.
- **@manual** (message-triggered): Answers questions in Slack/email grounded in the published essays.

---

## About

Written by Khaliq Gant, co-founder of AgentWorkforce, who previously spent three years as the first hire at Nango — the market leader in third-party integrations — where most of the work was getting the right context to AI: webhooks, normalized payloads, the long tail of provider quirks. The hard parts of proactive agents are, mostly, the same hard parts dressed up in a different jacket.

Publishing approach: educational over promotional, specific over abstract, honest about what's hard.
60 changes: 60 additions & 0 deletions content-system/llms-full-header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Proactive Agents — Full Content

> The definitive guide to proactive AI agents — what they are, how they differ from reactive agents, and how to build them.

Site: https://proactiveagents.dev
Author: Khaliq Gant, co-founder of AgentWorkforce

---

## What Is a Proactive Agent?

A proactive agent is an AI agent that acts without being prompted. Instead of waiting for a human to type a message or click a button, it wakes itself up when something in its environment changes — time passes, data mutates, or a message arrives — and decides whether and how to act.

This is the architectural opposite of a reactive agent, which sits idle until explicitly invoked. Most AI agents today are reactive: they receive a prompt, execute tool calls, return a response, and go back to sleep. A proactive agent is always running in the background, watching the world, and acting when the moment is right.

### The Three Triggers

A proactive agent is defined by how it wakes up. There are exactly three triggers:

1. **Time**: The agent runs on a schedule or interval. Every 15 minutes, every Monday at 9am, every quiet hour past midnight. This is the simplest trigger but the least differentiated — a schedule alone is just a cron job.

2. **Change**: The agent watches for data mutations. A ticket moves in Linear, a record updates in Salesforce, a file appears in a shared drive. The agent receives a push event (via webhook) the moment the change happens, rather than polling for it.

3. **Message**: Someone addresses the agent directly — a human, another agent, or a system. The agent responds in its own time, not on a polling cycle.

A truly proactive agent listens for all three. Pick one and you've built a smarter cron job. Pick two and you've built a chatbot that polls. The composition of all three is what makes an agent proactive.

### Proactive vs Reactive: The Core Difference

| Dimension | Reactive Agent | Proactive Agent |
|-----------|---------------|-----------------|
| Activation | Waits to be called | Wakes itself up |
| Data freshness | Polls on interval (N seconds stale) | Push events (real-time) |
| State | Stateless between runs | Persistent memory |
| Failure mode | Forgets what it did | Checkpoints and resumes |
| Architecture | Function called by a user | Participant in a system |

The reactive agent asks: "What changed in the last five minutes?" The proactive agent asks: "What just changed?" The first is a query you have to invent. The second is a fact the world hands you.

### Why Most Agents Are Still Reactive

Three engineering problems keep agents reactive:

1. **Wake-ups are infrastructure.** Polling is easy; push is hard. Stable URLs, signature schemes, normalized events, durable triggers — none of it ships in a model SDK. Someone has to build it.

2. **State is harder than it looks.** Between wake-ups the agent has to remember what it saw, what it acted on, what it's still in the middle of. Most agents wake up amnesiac and re-read the world from scratch.

3. **Restraint is a research problem.** An agent that fires too often loses trust faster than one that misses things. Calibrated restraint — knowing when NOT to act — is a known-hard problem even at the frontier.

### The Three Primitives

A proactive agent requires three primitives wired together:

1. **A wake-up mechanism** — a clock (schedules), a watcher (change events from providers), and an inbox (messages from humans and systems).

2. **Persistent state** — a place for the agent to remember what it saw, what it did, and what it's in the middle of. This is closer to a filesystem than a database: real-time read/write, conflict detection, change events when anything moves.

3. **Durability** — checkpointing (resume after failure), idempotency (don't repeat actions), spend control (prevent runaway loops), and scoped auth (limit blast radius).

Together these form the "proactive runtime" — the infrastructure that sits underneath the agent and handles everything that isn't the agent's actual logic.
Loading