↓ Skip to main content

What I've built and what I need: June 2026

Over the month since May, the agents repository evolved from building the SDLC pipeline into a mature, self-improving system. The biggest shift was adding verification and consistency tooling, introducing stateful SDLC tracking, and standardizing conventions across the skill library.

What I Have Been Working On
#

Added verification and consistency tooling. The latter half of the month focused on checking the system’s own work. validate-pr runs runtime validation of PRs, checking out a branch, building it, and proving every claim in the PR description through execution. verify-pr performs static code inspection covering quality, correctness, and architecture alignment. backpropagate-sdlc walks the SDLC artifact chain in reverse, from code back to issue, to verify end-to-end traceability and detect drift accumulated across phases. sync-repository ensures the entire repository stays consistent across SDLC, code, tests, documentation, types, and observability. improve-autonomy assesses what would be needed to run a session fully autonomously end to end.

Overhauled the SDLC infrastructure. The old bootstrap-sdlc was replaced by sync-sdlc, which reconciles the codebase with the .sdlc/ directory instead of just bootstrapping it. A new .sdlc/state.yml file in worktrees tracks the current phase, GitHub reference, and feature, giving skills persistent context across invocations. The sdlc-status skill was extracted and heavily improved through multiple iterations, adding vocabulary tooltips, open questions, and a footer. Observability and telemetry phases were added to the SDLC pipeline, extending coverage from needs assessment through deployment and retrospective. qualify-issue drives a multi-round Q&A loop to gather enough information before requirements. create-feasibility / review-feasibility and create-existing-solutions / review-existing-solutions assess viability and survey prior art before committing to a build. fix-issue orchestrates a bug fix end to end by delegating to reproduce-issue, create-implementation, and create-pr. Post-merge coverage extends through deploy-pr and observe-production, which deploy changes and verify SLOs and error rates in production. Production health is planned from the start with create-observability / review-observability / audit-observability, which define logging, metrics, tracing, and alerting for each feature. create-telemetry / review-telemetry define the analytics events for each feature. Cross-skill conventions were consolidated into a shared references file, and per-repository AGENTS.md overrides can now be stored under repositories/ for project-specific instructions. SDLC artifacts produced by the skills across projects I contribute to or monitor are tracked in a dedicated sdlc repository. That repository includes automation to generate static HTML status pages from the output of the sdlc-status skill. Each project’s artifacts are reviewed weekly by sync-sdlc to detect and correct drift.

Shipped 7 maintenance skills for code quality. A new “maintenance” skill category was added covering security auditing, code duplication detection, type gap analysis, churn analysis, and related diagnostics. These were integrated into the SDLC pipeline as a structured diagnose-harden-clean-document workflow, giving every feature a code health pass alongside the existing requirements-to-learnings flow.

Polished PR and issue workflows. Issue triage gained duplicate detection and smarter label management, and triage-issue was split out from triage-issues for single-issue versus batch processing. The ghx CLI was introduced and integrated into the review and feedback skills. It adds inline PR review comments, pending reviews, and comment stashing that the standard gh CLI does not support. Reversibility checks were added across review-* skills, and forward compatibility checks were introduced. create-issue had its duplicate checking restored and time estimates improved. A new automate-session skill was added to identify repetitive workflows worth encoding as skills.

Added new lifecycle and utility skills. create-needs-assessment and review-needs-assessment evaluate whether a feature addresses a genuine need before investing in feasibility or requirements. research-article maps the state of the art before writing, producing a research brief with organized sources. setup-docs-site scaffolds a MkDocs documentation site with a GitHub Actions publish workflow. vacation-handoff generates a pre-leave handoff covering deadlines, on-call coverage, and in-flight work. audit-attention analyzes how time splits between compounding and depreciating activities using the two-year test, suggesting what to delegate versus protect. create-message improves outgoing messages by removing negative tone and adding actionable suggestions. summarize-meeting produces a structured meeting summary from a transcript file. review-skills and compare-skills audit the skill library itself for duplicates, broken references, orphaned skills, and practices worth adopting from other libraries.

Closed three of last month’s needs. Replying to inline PR comments, listed as a need in May, is now fully integrated into the review and feedback skills via ghx. Incremental PR description updates, also listed in May, are now handled by the update-pr-description skill, which makes minimal adjustments to the existing description after new commits rather than regenerating from scratch. Bug reproduction now triggers automatically when the “bug” label is added to an issue via reproduce-issue, though commenting results back on the issue is still unreliable.

What I Currently Need
#

From last month, the following are still needed in priority order:

The new needs for this month:

Skill usage tracking. I started using agentsview, which lets you filter conversation logs on terms. Given a list of skill names over time, it is possible to filter on those names as long as they were not expanded. OpenCode only expands slash commands if they start a message, so prefixing them with a space prevents auto-expansion and would allow tracking, but that is an ugly hack that needs a cleaner solution.

Contextual Slack support. slack-cached can already read Slack threads, and given the right working directory it may have enough relevant context to help users better than generic Q&A pairs. What remains is wiring it into an actual support workflow.

Automate issue triage follow-up conversations. The triage-issues skill currently sends a single message when an issue is received. If the user replies, no further LLM interaction happens. The triage loop should continue the conversation until the issue is fully qualified.

Reliable bug reproduction comments. The reproduce-issue skill now triggers automatically when the “bug” label is added to an issue, but commenting the reproduction results back on the issue is unreliable and needs to be stabilized.

Introduce loops to automate skill usage. As described in Loops as Files, loops provide a scheduling layer that can run skills on a recurring basis without manual triggering. Wiring skills into loops would enable workflows like automated issue triage, weekly repository syncs, and scheduled PR reviews to run unattended.

Explore routines, habits, and event-driven skill triggering. Beyond loops, I want to investigate routines and event-driven triggers that fire skills when the right conditions occur. As the skill library grows past 130 skills, some are never used simply because nothing is wired to invoke them. Connecting those skills to the right trigger or schedule would unlock work that currently goes undone.

Improve the SDLC status report. As I work on large features, iterating involves back and forth between SDLC stages that requires changes to be backported and then forward propagated. The sdlc-status report needs better content and visualization to make this flow visible and navigable.

Reduce manual testing and validation. I spend too much time manually testing and validating that a feature works as expected. The improve-autonomy skill should help close this gap, but the challenge is behavioral: I instinctively revert to doing it myself instead of immediately asking how an agent could do what I am about to do.

Rework llm-augmented-workflows for flexibility. llm-augmented-workflows needs to be reworked to handle feature and bug fix requests flows more flexibly, and to support other harnesses beyond Claude Code such as OpenCode and Pi.


AI-Maxxing and Resistance Are the Same Mistake: Optimize Attention, Not AI Quantity

Two camps have hardened around LLMs, and they sound like opposites. One wants AI in everything, automating every task that can be automated, measuring success by how little the human touches. The other wants AI kept at a distance, preserving the craft, the understanding, and the roles that made the field what it is. They are not opposites; they are the same mistake pointed in different directions, and the mistake is optimizing how much AI to use instead of where to spend the attention AI frees up.

Two Stances, One Error
#

AI-maxxing treats the fraction of work delegated to a model as a score to be maximized. If a task can be automated, it should be, and the human’s remaining job is to orchestrate ever more automation. Its logical endpoint is the self-evolving codebase, the LLM agent company, the one-person team driving a dozen agents in parallel, none of them jokes and all of them early sketches of a real stance.

Resistance treats the fraction of work kept in human hands as a value to be defended. If a task can be done by hand, it should be, because doing it by hand is what builds the skill and the identity that make the work worth doing. Its logical endpoint is the engineer in 2026 still typing boilerplate from memory and calling it craft.

Each camp carries a real insight. The AI-maxxer is right that production has stopped being the bottleneck, and that clinging to manual production is no longer rational. The resistor is right that not everything delegable should be delegated, because delegation can erode the very capability that makes delegation safe. Both are right about the other’s blind spot and wrong about their own, because neither is asking the question that actually decides the outcome.

The Question Neither Camp Asks
#

The question that matters is not “how much AI should I use?” It is “of the things I do, which are depreciating and which are compounding, and does AI move each one in the right direction?”

A depreciating activity is one whose value drops as the environment changes. Typing boilerplate, memorizing an API, writing the fifth CRUD endpoint of the week: these were valuable when production was scarce, and they are melting in value every month that models get better. A compounding activity is one whose value rises the more of it you do, and that feeds back into everything else. Understanding a domain deeply, holding taste about what to build, judging whether a piece of code solves the right problem: these do not decay, and they make every other thing you do more effective.

The right stance follows from this distinction immediately. Delegate depreciating activities ruthlessly, and protect compounding activities ferociously.

The AI-maxxer breaks this by applying the delegation rule to everything, including the compounding activities, and slowly hollows out the judgment layer that makes the delegation produce anything worth having. The resistor breaks it by applying the protection rule to everything, including the depreciating activities, and slowly trades future capability to preserve a skill the market no longer rewards.

Why AI-Maxxing Is the More Insidious Error
#

Here is the asymmetry that lifts the argument out of a polite “both sides” essay.

Resistance is a recoverable error. The resistor falls behind, notices eventually, and can adopt the tools later. The depreciating skills they protected are still useful during the transition, and the AI is still there, waiting. The cost is lost time, which is real but bounded.

AI-maxxing is an insidious error, because it feels like winning the whole time. The output keeps flowing. The pull requests keep landing. The agents keep producing plausible, confident, well-formatted work, and nothing in the loop tells you that the judgment layer underneath has been quietly atrophying.

Delegation is not free even when the model is free. Every time you delegate a compounding activity, deciding what to build, evaluating whether the generated code is correct, choosing between two architectures, you forgo the practice that built the judgment you would need to evaluate the delegation. The AI-maxxer assumes judgment persists without exercise. It does not. A mind that never decides what to build loses the ability to tell whether what was built is worth shipping, and no model substitutes for that loss, because using the model well already presupposes it.

This atrophy is the hole I pointed at in The Shifting Bottleneck: when you automate a layer, the bottleneck moves up to a more judgment-heavy layer, it does not disappear. The difference is that the bottleneck article described the move as something that happens to the system. AI-maxxing is what happens when you mistake the move for a disappearance, and convince yourself there is no bottleneck left worth staffing.

Why Resistance Is Wrong, but Less Dangerously
#

The resistor’s error is the mirror image, and it deserves to be named fairly.

Most of what the resistor calls craft is just production, and production has stopped being the scarce thing. Writing code by hand does not teach the domain faster than reading code, including generated code, with intent. Memorizing an API does not make you a better architect; it makes you a faster typist, for an API that will be deprecated in eighteen months. Protecting a depreciating activity in the name of craft is not craft; it is nostalgia with a deadline.

The legitimate kernel inside resistance is the fear that delegation erodes capability. That fear is correct, and the AI-maxxer should borrow it wholesale. But the answer is not to refuse delegation across the board. The answer is to refuse it selectively, at exactly the compounding activities where the erosion matters, and to embrace it everywhere else so aggressively that you buy back the time to do the compounding work properly.

The resistor who types their own boilerplate to “stay sharp” ends up with less time for the deep domain work that would actually keep them sharp, and they are protecting the wrong layer. The AI-maxxer who delegates the deep domain work to “focus on orchestration” ends up with nothing left to orchestrate well, because orchestration without domain depth is just queue management.

The Test That Settles It
#

For any activity on your plate, ask one question. If I let the model do this for the next two years, will the me that emerges be more valuable, or less, than the me that kept doing it by hand?

Boilerplate, scaffolding, routine tests, formatting, summarizing a thread, drafting a first pass at a known pattern: delegate all of it, and the you that emerges after two years is more valuable, because you spent those two years on something else that compounded. Deciding what to build, judging whether a design is right, reading a hard paper with the intent of being able to teach it, debugging a subtle failure by reasoning about the system: keep these, even when a model offers to do them, because the two years of practice is the entire asset.

Notice that the test is about the allocation of your attention, not the quantity of AI you use. An engineer who delegates ninety percent of their work to a model and spends the freed time going deeper on the remaining ten percent is not the AI-maxxer this essay argues against. They are doing it right, and the high delegation ratio is a symptom of having correctly identified their compounding layer, not a target they are optimizing for. The number to watch is not how much you delegate; it is how much of your remaining time lands on compounding work.

The Boundary Moves, and That Is the Hard Part
#

The complication, and the reason the sorting is not a one-time exercise, is that the line between depreciating and compounding is not fixed.

A thing that was compounding yesterday can become depreciating tomorrow. Writing SQL by hand was once a compounding skill, a path to a deep understanding of how the data informs the business; today the model writes the query and the deep part is knowing what to ask it for and whether the result is truthful. Reading logs was once a compounding skill; increasingly the model triages them and the deep part is deciding which anomalies matter. Each shift in capability redraws the line, and the stance that worked last year can become either error in the next.

The moving boundary is why neither camp has a stable answer. The AI-maxxer’s “delegate everything” is wrong because some of what they delegate is still compounding, and the resistor’s “do everything by hand” is wrong because some of what they protect has already stopped compounding. The only stable skill is the meta-skill of repeatedly telling the two apart, and that meta-skill is itself compounding, which is the strongest case I can make for spending attention on it.

What to Do Next
#

Stop measuring yourself by how much AI you use, in either direction. Run the inventory instead. List the activities that fill your week, and mark each one depreciating or compounding, using the two-year test. Then push hard on both ends: delegate the depreciating ones as aggressively as you can, and block off time for the compounding ones so that the time you bought back actually lands on them.

Do not be surprised if the result is a higher delegation ratio than the resistor would tolerate and a lower one than the AI-maxxer would brag about. That is what getting it right looks like. The ratio is an output, not a target.

And watch the boundary. Re-run the inventory every few months, because the model’s growth will have moved it, and an activity that was worth keeping may now be worth delegating, or, more dangerously, the reverse. The most expensive mistake in either direction is the one you keep making because you sorted the list once and never looked again.

The AI-maxxer and the resistor are each certain they have found the answer, and that certainty is the real cost. The correct stance is uncomfortable: delegate like a maximalist, protect like a minimalist, and never stop asking which is which.

See also
#

References
#

  • Goodhart’s law - why maximizing a proxy (fraction of work delegated) undermines the outcome the proxy stood for
  • Theory of constraints - the framework for why removing one bottleneck reveals the next, and why delegation never eliminates the judgment layer
  • Automation bias - the tendency to over-trust automated output, the failure mode that lets AI-maxxing feel like winning while judgment atrophies
  • Satisficing - settling for “good enough” outputs, the silent cost of delegating compounding activities to a system that optimizes plausibility

Keeping Up With AI Is a Losing Strategy

Every week brings a new frontier model, a new agent framework, a new benchmark, and a new wave of posts explaining why this one changes everything. The production of AI-related information is growing exponentially. My ability to consume it is not, and neither is yours. Keeping up is the wrong goal, and chasing it will quietly consume the time and attention you need to actually use any of it.

The Asymmetry
#

Information overload is not new. What is new is the asymmetry between production and consumption in a domain that is itself being transformed by the technology it covers.

A human reads at roughly the same rate today as a generation ago: a few hundred words per minute, a handful of long-form pieces per day, a finite number of hours in the week. Meanwhile the supply of AI content is compounding. arXiv’s machine learning categories add thousands of papers every month, with submission volumes climbing year over year. Model providers ship flagship releases on a monthly cadence where they used to ship annually. And now a meaningful fraction of that content is itself written, summarized, or generated by LLMs, which closes the loop: AI produces content about AI faster than humans can read content about AI.

The loop is structurally different from “there is a lot to read.” The half-life of any specific fact is shrinking while the rate at which new facts arrive is rising. Both halves of the ratio are moving against you at once.

Why “Just Keep Up” Fails
#

The instinctive response to a firehose is to read faster, subscribe to more newsletters, and carve out more time. I tried all three. Reading faster works for a linear stream. The strategy collapses against an exponential one.

Every new source you add also adds its own outgoing links, its own recommendations, its own “you should also read this.” The graph of things you could read grows faster than the list of things you have read. You are not behind because you are undisciplined. You are behind because the target is moving faster than your means of reaching it.

The cost is not only time. Constant context-switching between model announcements, framework launches, and benchmark debates produces the same attention residue and decision fatigue that come from managing too many concurrent tasks. The result is feeling informed without being informed, superficial awareness with nothing behind it. You know the names of ten new models and cannot explain how any of them differ in a way that matters.

The Reframe: Optimize for Signal, Not Coverage
#

Coverage is impossible, so stop measuring yourself against it. The objective is not to read everything. The objective is to make good decisions about what to read, what to build, and what to ignore.

Making those decisions is a filtering problem, not a reading problem. And filters are something you can design, tune, and improve, unlike your reading speed.

Separate the Durable From the Ephemeral
#

Most of what feels urgent in the AI cycle is ephemeral. A specific model’s benchmark numbers, a specific launch event, a specific viral demo: these date within weeks. A surprising amount is durable. The mechanics of attention, the basics of context and prompting, the theory of constraints in a pipeline, the economic profile of inference costs, the difference between evaluation and vibes: these barely move across model generations.

When you are time-constrained, overweight the durable. Read one paper that teaches you a concept you will reuse for years over ten posts that summarize this week’s leaderboard. The ephemeral items are not worthless, but they should be consumed on demand, when a specific decision requires them, not on a schedule driven by fear.

Build a Funnel, Not a List
#

A reading list is a flat pile of undifferentiated “should read.” A funnel is a staged filter that gets cheaper at each stage.

At the top, sample broadly through a small number of trusted curators and aggregators, and let them do the first pass. At the middle, use titles and one-paragraph summaries to decide what earns a closer look. At the bottom, read the few items that survive, deeply. Most items should die at the top of the funnel, never reaching your full attention. The goal of a funnel is to throw almost everything away, confidently and without guilt.

This is the same principle behind supervising many agents at once: you cannot hold it all in working memory, so you externalize state, standardize summaries, and let most of the system run without your involvement (see Managing Many Concurrent LLM Agent Sessions).

Use AI to Compress the Stream to Your Bandwidth
#

The same technology producing the flood is also the best tool for surviving it.

Have an LLM summarize a forty-page paper into the three things you actually need. Have it compare two model release notes and tell you what differs in a way that affects your work. Have it triage a week of submissions and rank them against your stated interests. Used this way, the LLM is a lossy compressor tuned to your specific decision bandwidth.

The risk is that summaries carry the summarizer’s blind spots. Mitigate it by going to primary sources for anything you intend to act on, and by keeping the durable layer of your understanding built from full readings, not compressions. Summaries are for triage and breadth; depth still requires reading the original.

Consume on Demand, Not on Schedule
#

Push consumption, reading whatever arrived in your feed today, optimizes for the producer’s schedule. Pull consumption, reading what a current problem requires, optimizes for yours.

Most of what you “need to know” about a new model or tool only matters once you have a task that touches it. Until then, the information is inventory. When the task arrives, the information is usually still there, and a focused hour of just-in-time learning will beat ten hours of distracted pre-reading. Knowing where to find something when you need it is a more durable skill than knowing it now.

This is uncomfortable, because it requires trusting that you will not miss something critical. You will miss some things. You will also miss things by spending your day reading instead of doing, and those misses are silent.

Pick a Few Sources and Defend Them
#

There are more newsletters, podcasts, and feed channels than any person can sample, and most of them recycle the same primary sources. Find the small set that consistently adds signal rather than volume, and defend that set against additions. Every new subscription should displace an existing one, not stack on top of it.

The same applies to people. A few colleagues or communities that share your context will filter better than any algorithm, because they know what you already know and what you are trying to do.

The Skill That Compounds
#

Fighting the firehose makes it easy to miss what actually compounds.

Your current knowledge of AI is a depreciating asset. Specific model behaviors, tool APIs, and leaderboard rankings decay within months. What does not depreciate is the rate at which you can learn a new tool when you need it, the quality of your filter, and your grasp of the durable layer underneath the releases. These compound, and they are the only things in this cycle that do.

A developer who has internalized the theory of constraints, the structure of an LLM pipeline, and the difference between evaluation and marketing can pick up this week’s framework in an afternoon. A developer who chased every release for two years but never built that foundation knows a hundred tool names and cannot reason about any of them. The first developer is robust to the pace of change. The second is controlled by it.

The Bottleneck Moves, Again
#

The deeper pattern is the one described in The Shifting Bottleneck. When code generation stopped being the bottleneck, review became it. When review is automated, specification and judgment become it.

Information consumption has its own version. Once reading everything stops being possible, triage becomes the bottleneck. Once triage is delegated to AI, choosing what to care about, at the level of goals and taste, becomes the bottleneck. Each time you solve one layer, the constraint moves up to a more human, more judgment-heavy layer. The judgment layer is where the durable work sits, and it is also the layer least helped by reading faster.

What to Actually Do
#

Stop trying to keep up. Pick a small set of high-signal sources and ignore the rest. Overweight durable concepts over ephemeral releases. Build a funnel that throws most things away. Use LLMs to compress the stream to your bandwidth, then read primary sources for anything you will act on. Switch from push to pull: learn on demand, when a real task requires it. And spend the time you reclaim on the layer that compounds, your filter, your learning rate, and your judgment.

You will miss things. So will the person who reads all day, and they will also have missed the chance to build anything with what they read.

See also
#

References
#


Loops as Files: The Scheduling Layer Skills Forgot

Skills as files solved one half of the automation problem. They told the model how to do a task, versioned and reviewable, loaded into context on demand. They did not solve the other half. They told the model nothing about when to run. A skill is inert until something invokes it, and in practice that something is almost always a human typing a command.

That leaves the most experienced agent in your system doing nothing until you remember to ask it. It also leaves every event in your environment, the issue that was just opened, the Slack thread that just heated up, the dependency that just shipped a security patch, waiting for a human to notice and forward it to the right skill. The human has become the cron.

The fix is the same form as the fix for the variance problem in Bringing Everyone to the Same Level: take the invisible process out of someone’s head and make it a file. Except the process to extract now is not “how do I triage an issue,” it is “when do I triage issues, and what triggers that decision.” That belongs in a file too, and that file is a loop.

The Asymmetry Between How and When
#

Skills files normalized a useful idea. The prompt is the asset, the asset is text, text is versioned in git, and versioned text is reviewed, diffed, shared, and reused like code. The same model that made skills valuable applies cleanly to the trigger layer, but the trigger layer is still being treated as plumbing. It lives in crontabs nobody reads, in GitHub Action YAMLs that drift away from the skills they invoke, in shell scripts that bake agent invocations into system paths.

The result is that the two halves of an autonomous workflow live in different worlds. The prompt is curated. The trigger is improvised. When the prompt changes, the trigger does not, and when the trigger breaks, nobody who understands the prompt finds out until the agent has been silent for a week.

The schedule is part of the behavior. A skill that runs on /deploy and the same skill that runs at 02:00 every Tuesday are not the same skill. They have different blast radius, different cost profile, and different failure modes, and they deserve to be specified, reviewed, and owned together.

What a Loop File Is
#

A loop file is a markdown document with two parts. The frontmatter declares when it runs and under what constraints. The body declares what it does, almost always by composing skills that already exist.

In form it is the closest existing analog to a GitHub Actions workflow file, except the unit of work is not a shell command, it is an agent invocation with a loaded skill.

A minimal example:

---
name: hourly-issue-triage
description: Triage new issues as they appear, every hour.
on:
  schedule:
    cron: "0 * * * *"
skills: [triage-issue]
budget:
  max-runs-per-day: 24
  max-cost-usd-per-run: 0.50
---

# Hourly Issue Triage

For every issue opened since the last run, invoke `/triage-issue`.
Defer to the skill for all classification logic.
Stop when the budget for this run is exhausted and resume on the next tick.

That is the whole artifact. The skill owns the how. The loop owns the when, the how-often, the how-much, and the what-to-do-when-it-breaks. Each concern is in its own file, each file is reviewable, and changes to one do not silently invalidate the other.

The Frontmatter Is the Contract
#

The reason the frontmatter matters more than the body is that the frontmatter is the part the runtime actually parses. It is the contract between the human writing the loop and the system executing it.

A skill with no frontmatter is still useful as a document. A loop with no frontmatter is a skill with no trigger. So the fields that go in the frontmatter are not cosmetic metadata, they are the trigger specification, and they should be designed the way any interface is designed, with the smallest set of concepts that covers the realistic workload.

The field I want to anchor on is on, borrowed deliberately from GitHub Actions because the mental model is already widely understood. on says what causes this loop to fire, and its value can be one or many of three things.

Time
#

on:
  schedule:
    cron: "0 * * * *"

Cron is the obvious first case, and there is no reason to invent a new syntax for it. Cron is ugly, but it is universally understood, parseable, and already supported by every scheduler the reader is likely to have. Most loops that need to run on a clock are well expressed as a single cron expression, and loops that need more complex recurrence can compose multiple loop files.

A secondary form worth supporting is an interval, for cases where wall-clock alignment does not matter:

on:
  interval:
    every: 15m

Interval is easier to read and easier to distribute across a fleet, since N agents with every: 15m will naturally desynchronize in a way that cron will not.

Events
#

Time is the easy case. The interesting case is event-driven, which is where the word “loop” undersells what is going on. A loop that fires on a schedule is just a cron job with a markdown file on top. A loop that fires on an event is something genuinely new: a versioned, reviewable handler for things that happen in the world.

on:
  events:
    - type: github.issues.opened
      repo: "owner/repo"
      filter: "labels.length == 0"

    - type: slack.mention
      channels: ["#support"]
      filter: "text contains 'sev1'"

    - type: github.pull_request.opened
      repo: "owner/repo"
      filter: "author.trust < 'trusted'"

    - type: file.changed
      paths: ["pyproject.toml", "uv.lock"]

    - type: metric.threshold
      metric: api.error_rate
      window: 5m
      op: ">"
      value: 0.01

The pattern is the same in every case: a typed event source, an optional scope, and an optional filter expression. The types themselves are namespaced by source (github.*, slack.*, file.*, metric.*) so that adding a new integration is additive rather than a schema change.

A single loop can declare multiple events, and the runtime treats them as a logical OR. That covers the common case of “I want this skill to fire on a cron or when a human pokes it,” which is exactly the pattern of most operational loops.

Webhooks
#

The third trigger is a catch-all for events the runtime cannot subscribe to directly. A webhook is an event source the runtime exposes rather than consumes.

on:
  webhook:
    path: /loops/deploy-staging
    secret: ${DEPLOY_WEBHOOK_SECRET}

Anything that can hit an HTTP endpoint, from a monitoring tool to a ChatOps button to a physical device, can now trigger a skill, and the access control lives next to the prompt it gates.

The Frontmatter That Prevents the Loop From Eating Your Wallet
#

Time and events say when to start. The loop also needs to say when to stop, and this is the part that existing skill files do not need and loop files cannot live without.

budget:
  max-runs-per-day: 24
  max-cost-usd-per-run: 0.50
  max-concurrent: 1
concurrency: cancel-previous  # cancel-previous | queue | drop
timeout: 10m
on-failure: alert      # alert | retry | escalate | ignore
escalation:
  skill: notify-on-call
  after: 3 failures

I argued in The Self-Evolving Repository that cost runaway is one of the defining failure modes of autonomous systems, and a loop without a budget is one bad while True away from being a case study. Every loop file should be able to answer three questions without ambiguity: how often can this run, how much can each run spend, and what happens when it fails. If a loop cannot answer those questions, the runtime should refuse to start it.

The concurrency field is the other one that proves necessary early. A loop that runs every five minutes and takes six minutes to finish will, without a concurrency policy, slowly consume every slot the runtime has. cancel-previous, queue, and drop cover the realistic cases, and the right default is cancel-previous for anything stateless.

Loops Compose Skills, They Do Not Replace Them
#

There is a temptation, once you have a loop format, to start inlining the prompt into the loop body. Resist it. A loop file should read like an orchestrator, not like a skill, because it is one.

The body of a loop is typically a short sequence of skill invocations, with just enough glue logic to express ordering, branching, and handoff.

# Nightly Dependency Review

1. `/audit-dependencies` against the current lockfile.
2. If it reports any `severity >= high`, `/create-issue` with the audit output.
3. `/notify-on-call` summarizing counts and the worst offender.

Three skills, one paragraph of orchestration, fully readable. The logic that is hard (what counts as a high-severity vulnerability, how to phrase an issue, who the on-call is) lives in the skills it invokes, where it can be improved independently, tested independently, and reused from other loops.

This also keeps the loop file faithful to its job. A loop file that grows past a screen of text is probably doing the work of a skill and should be split. The same length discipline that keeps skills effective, which I borrowed from the “lost in the middle” argument in Bringing Everyone to the Same Level, applies to loops. If the runtime has to parse a long preamble before it even reaches the trigger, the trigger is no longer the contract.

Why Loops Have to Be Files
#

Every argument for skills as files applies, with minor edits, to loops as files, and one new argument applies only to loops.

Versioning. A trigger change is a behavior change. “Run triage on every new issue” and “run triage on every new issue except those from outside contributors” are different policies with different consequences. A trigger that lives in a crontab or a GitHub Actions YAML that nobody reviews is a policy that nobody reviewed, and reviewing it matters because the consequences of a trigger bug (silently not running, or silently running too often) are usually larger than the consequences of a prompt bug.

Reviewability. A loop file in git means every change goes through a pull request, which means the blast radius of the change is visible to the people who will be paged when it goes wrong. This is the same point I made, about code, in The Codebase Gardener: a standard that lives only in someone’s head is a standard that dies when that person goes on vacation. A loop that lives only in someone’s crontab has the same half-life.

Portability. A loop file describes what should happen, not where it runs. The same file can be executed by a local scheduler during development, by a team-shared runner in production, and by a CI provider that wants to dry-run it on every commit. That portability is what makes loops shareable across teams the way skills are shareable, and it is what makes a “loop library” a coherent concept in a way that a “crontab library” never was.

Diffability. The single most useful property of a file is that you can git blame it. When the on-call gets paged at 03:00 because the deployment loop has been firing every two minutes for an hour, the first question is “who changed the trigger, when, and why,” and the answer should be one git log away, not a long manual search through a CI settings UI.

The argument that applies only to loops is co-location with the skill. A skill file and the loop that schedules it are describing two facets of the same behavior, and when they live in the same repository, in the same format, in the same review pipeline, they evolve together. A skill change that should have changed the trigger (say, “this skill is now expensive, run it less often”) is a change that can actually be made in the same pull request by the same person who understood the consequence.

What the Runtime Owes You
#

A loop format without a runtime is a markdown opinion. The runtime is what makes loops safe to leave running, and it owes the operator a small, specific set of behaviors that are not optional.

Idempotency by default. A loop will be double-triggered. The cron will fire twice during a clock skew. The webhook will be retried. The runtime must be willing to run the same loop with the same inputs twice and treat the second run as a no-op, which means the skills it invokes must be idempotent, which is a property worth designing into skills whether they are loop-driven or not.

Per-run state. Every run writes a state file, in the same form I described in Managing Many Concurrent LLM Agent Sessions: what fired, what ran, what it produced, what it cost, what it will do next time. Without this, debugging a misbehaving loop is reading logs, and reading logs is what we used to do before we had files.

A kill switch. There must be one command, one CLI flag, one environment variable, that stops every running loop and disables every scheduled trigger. The cost of not having this is the cost of not having a circuit breaker in your electrical panel.

Observability that is not optional. A loop that fails silently is strictly worse than no loop at all, because no loop at least fails loudly when a human expected it. Every run emits run-started, run-succeeded, run-failed, run-budget-exceeded events, and the runtime ships them to whatever sink the operator already trusts.

Dry run. --dry-run evaluates the trigger, selects the skill, expands the inputs, and prints what it would have done, without invoking the model. This is the cheapest possible way to debug a trigger filter, and it should exist on every loop runner.

What Goes Wrong
#

Loops inherit the failure modes of any autonomous system and add two of their own.

Drift toward the measurable. A loop that fires on github.issues.opened will, over time, optimize the project for issue-driven signals, because those are the signals that cause work to happen. The project stops reacting to anything that does not show up as an event the loop can see. This is Goodhart’s law applied to ops automation, and the defense is the same as in The Self-Evolving Repository: keep a human-edited roadmap, and reserve a small number of loops for periodic “what should we be working on” reflection that is not event-driven.

Loops stepping on loops. Once loops are cheap to write, people write a lot of them. Two loops that both touch the same issue tracker, both with their own opinions about labels and priorities, will quietly fight each other, and the issue tracker will lose. The runtime needs loop-level isolation (separate working directories, separate state files, separate rate limits) and a registry that makes it easy to answer “which loops currently fire on this event,” the same way a codebase makes it easy to answer “which tests currently exercise this function.”

Trigger rot. A loop that worked when it was written will silently stop working when the thing it triggers on changes form, the GitHub webhook payload gains a field, the Slack channel gets renamed, the metric gets relabeled. Loops need the same periodic sweep that code needs, and the sweep is mechanical: for each loop, fire its trigger in dry-run, confirm the skill still runs, retire the loops that nobody owns.

Cost runaway, again. Worth saying twice. An event-driven loop with no budget, attached to a busy event source, can turn a busy Slack day into a four-digit API bill. The budget field in the frontmatter exists for exactly this reason, and the runtime must enforce it hard, not warn softly.

The Naming Question
#

I have used “loop” throughout because it captures the simplest mental model: something that runs again and again. But “loop” undersells the format. A loop that fires only on slack.mention is not really looping. It is reacting.

The more accurate name is probably “routine,” “trigger,” or “automation,” and the reason none of those quite land is that each one emphasizes one of the two trigger classes at the expense of the other. “Loop” emphasizes time. “Trigger” emphasizes events. “Routine” emphasizes the work. The format itself does not care. The on: field is the source of truth, and the file is whatever its trigger says it is.

In practice, teams that adopt this will settle on one word and use it for both, the same way “skill” is now used for files that range from one-line hints to multi-page workflows. The word matters less than the format, and the format is: markdown, frontmatter, on, a budget, a short body.

What to Do Next
#

You do not need a runtime to start.

Find the one task that you do on a cadence and keep doing manually because it is “too small to automate.” Triage the overnight issue queue. Summarize the Slack channel at end of day. Check whether dependencies have shipped patches. That task is your first loop.

Write it as a loop file, even if the runtime that will execute it does not exist yet. Write the frontmatter faithfully, the trigger you want, the budget you would accept, the skill it should invoke. If the skill does not exist yet, write its stub too.

You now have two artifacts that describe the behavior you want, in the same repository, in the same format, reviewable in the same pull request. When a runtime arrives (and several already will, given how cheaply existing coding agents can be wrapped in a scheduler), your loop is ready to drop in.

Then do it again with the next task, and the next. Each loop file is a piece of operational judgment that stops being a private habit and starts being a shared, owned, versioned standard. The team that wins in this era is not the one with the most skills. It is the one whose skills run, on time and on event, without anyone remembering to ask them to.

See also
#

References
#


When a Closed Decision Reopens: Breaking the Scope Relitigation Cycle

A decision reached with your principal did not actually close the question. It closed the question in the room where it was made. When a new manager met the senior leader for the first time, the shared understanding that took weeks to build stayed behind, and the original ambiguity rushed back in to fill the gap. The cycle you are trapped in is not a scope problem. It is a context-transfer problem that looks like a scope problem.

The Decision Didn’t Reopen. The Context Didn’t Transfer.
#

When leadership says “build this feature” without saying what “this” means, they have not given you a requirement. They have given you a vague goal, and asked you to find a concrete target inside it. You did the right thing. You built a demo to make the vague goal concrete enough to react to, you sat with the principal, and you negotiated a concrete, buildable target. That work was real, and it was correct.

Then a person who was not in the room walked into a different room with the senior leader, and the target moved.

This feels like betrayal, or like a failure of the new manager, or like leadership changing its mind. It is usually none of those. It is the predictable consequence of a decision that was never written down in a form that could travel. A decision that lives only in the heads of the people who were present is a decision that cannot survive contact with anyone who was absent. Your organization keeps generating absent people: new hires, new managers, new conversations, new quarters. Every unwritten decision has a half-life, and the half-life is exactly as long as it takes for one new person to enter the chain.

The senior leader was never actually aligned with you on a specific scope. They were aligned with a feeling, which is that the feature should be impressive and should exist. The specific, feasible compromise you reached with the principal was never transferred back to the person whose vague wish started the whole thing. So when the topic came up again, the leader reached for the only thing they still held, which was the original wish, unmodified by everything you had since learned was impossible.

You Have Two Problems, Not One
#

The conversation keeps oscillating because two genuinely different questions have been tangled into a single thread, and the two questions keep pulling against each other.

The first question is the north star: what would the ideal version of this feature look like, if the tools caught up and the constraints disappeared? That vision is valuable. It tells you which direction to walk, and it is the thing that excites leadership in the first place.

The second question is the milestone: what will we actually ship this quarter, with the tools we have today? That answer is necessarily smaller, uglier, and more compromised than the north star, and it is the only thing you can actually build.

The cycle happens when leadership talks about the north star and hears your milestone answer as a refusal, and when you talk about the milestone and hear their north star answer as scope creep. You are both correct, about different questions, and the fight is sustained entirely by the fact that nobody has written the two answers on the same page and drawn a line between them.

The fix is to separate the questions, explicitly and in writing. Keep the north star. Document it, reference it, and let it do the work of inspiring people. Then draw a hard line underneath it and write, separately, what you are committing to build now, why that subset and not another, and what specifically would have to change for the next slice to become feasible. The north star is a direction. The milestone is a contract. Conflating them is what makes every meeting feel like the last meeting, again.

“That’s Impossible” Is Not an Answer a Leader Can Process
#

Here is the cruelest part of the situation, and the one most likely to keep you stuck.

When you tell leadership “we can’t build the full vision, the tools aren’t there yet,” you are reporting a technical fact, and you expect it to land as a boundary. It does not. It lands as an opinion, and a suspicious one, because every leader has heard engineers declare things impossible that later turned out to be merely hard. The word “impossible” carries no information a decision-maker can act on. It sounds like reluctance, and so it invites the very relitigation you are trying to end.

The move that actually closes the loop is to convert impossibility into a set of specific, dated, falsifiable claims. Not “we can’t do X,” but “X requires capability Y, which does not exist in our stack; the closest vendor offering is at stage Z; building it ourselves is an estimated N months of work from the platform team, which is not currently funded.” Now the leader is not arguing with your willingness. They are looking at a menu, and the menu has prices on it. They can choose to accept the limit, fund the enabler, or descope, but whatever they choose, they are choosing with information instead of against a vibe.

Saying “impossible” stops the decision. An itemized list of what it would take lets the decision move forward. Leaders cannot act on a flat refusal, but they will act on an itemized list, and often they will fund the capability that unlocks the next step. This reframing also protects you. The day the tools do catch up, your document already names the capability you were waiting for, and the path to the north star becomes a matter of executing a plan you wrote months ago instead of restarting the conversation from scratch.

A Verbal Decision Is Just an Opinion That Hasn’t Been Overwritten Yet
#

The single highest-leverage thing you can do to stop the back-and-forth is to insist that decisions get written down the moment they are made, in a place that outlives the meeting.

This is what Architecture Decision Records were invented for, as Michael Nygard argued when he proposed the format. The record is a short document that captures the context, the decision, the alternatives considered, and the status, so that a person who arrives later can understand not just what was decided but why. The same discipline applies to scope decisions, which are every bit as consequential as architectural ones. A one-page record that says “we will build this subset, not the full vision, because of these specific constraints, and we agreed this on this date with these people” is worth more than any number of follow-up meetings.

The reason writing matters more than talking is that writing has different physics than speech. A conversation degrades the instant it ends. The memory of it degrades faster, and as it passes through more mouths it distorts, the way the children’s game of telephone turns a clean phrase into nonsense in a few retellings. A document does not degrade. It says the same thing to the new manager on their first day as it said to the principal on the day it was written. It says the same thing to the senior leader in their next one-on-one as it said in the room where the compromise was struck.

If a decision is worth making, it is worth making durable, and a decision is only as durable as the artifact it is recorded in. The cost of writing it down is ten minutes. The cost of not writing it down is the cycle you are currently living in, paid in weekly installments, forever.

The New Manager Is Not the Enemy. They Are a Missing Node.
#

It is easy, and emotionally satisfying, to blame the new manager for reopening the settled question. Resist that story. Almost certainly, they walked into their first meeting with the senior leader underprepared, because nobody had handed them the history. They improvised from the leader’s enthusiasm, which pointed at the north star, not at your milestone compromise. That is not sabotage. That is a new person doing their best with the information they were given, which was not enough.

The deeper structural issue is that information in an organization flows along its communication paths, and an org that just gained a new link between the team and the senior leader has, in effect, rewired those paths. A decision that was stable under the old wiring may be unstable under the new wiring, because the new path bypasses the node where the context lived. This is why onboarding a new manager is not a courtesy, it is a defensive necessity. Within their first week, they need to receive, in writing, the current decisions, the reasons behind them, and the constraints that produced them. A new manager who has read the decision records cannot accidentally contradict them, because they know they exist.

There is also a principal-agent dynamic here that is worth naming. The senior leader, the new manager, the principal, and you all have different information, different incentives, and different audiences they are trying to satisfy, the classic pattern of the principal-agent problem. The new manager may be, reasonably, trying to demonstrate value to their new boss, and an enthusiastic “we can do more” lands better in that first meeting than a cautious “actually, we already agreed to less.” They are not wrong to want to look good. The system is wrong to have left them no shared record to align to. Fix the system and the manager will fall into line with it, because the record gives them something safer to bring to the leader than improvisation: a documented plan they can defend.

A Playbook for Breaking the Cycle
#

When you find yourself back at the start of a scope conversation you thought was over, do the following, in order.

First, stop re-arguing the scope in the meeting where it resurfaced. Relitigating under pressure produces worse decisions than the original ones, and it taxes the same finite decision-making energy that you need for the work itself. Buy time. Say that you want to make sure the conversation reflects everything the team has already learned, and that you will come back with a written summary. That sentence is free, and it breaks the spiral.

Second, write the decision record, if it does not already exist. Context, the north star, the agreed milestone, the specific technical constraints that forced the compromise, the people who agreed, and the date. This is the artifact that should have existed all along, and creating it now is not empty procedure, it is what makes every later step work.

Third, convert every “we can’t” into a priced option. For each piece of the vision that is out of reach, write what it would take to reach it: which capability, which team, which rough timeline, which dependency. Give leadership the itemized options, not a flat refusal.

Fourth, brief the new manager before the next senior-leader conversation. Walk them through the record in person, make sure they understand the constraints, and explicitly invite them to bring their own objections now rather than in the room with the leader. Make them a co-author of the record, and they will defend it instead of overturning it.

Fifth, re-present to the senior leader, on your terms, with the record in hand. Acknowledge the north star openly, because dismissing the vision is what makes leaders dig in. Then show the milestone, show the priced options, and ask for a single explicit decision: accept the current scope, fund an enabler, or descope. Whatever they choose, write the new decision down before anyone leaves the conversation. A meeting that ends without a written decision is a meeting that will have to be held again.

Finally, schedule the next scope review in advance, on a date you control, instead of waiting for the topic to erupt on its own. Scope conversations are not avoidable, and they are not the enemy. The enemy is surprise relitigation, the version that ambushes you in someone else’s calendar. A standing quarterly review where the north star, the milestone, and the priced options are revisited together turns the cycle into a rhythm, and a rhythm is something a team can plan around.

The Real Lesson
#

You cannot build something that is impossible today, and you should not promise that you can. But you also cannot expect a vague wish, transmitted through a changing cast of people, to ever stop generating the scope conversation you dread. The way out is not to win the argument one more time, harder. It is to change the medium in which the argument happens. Write the vision. Write the milestone. Write the prices of the gaps between them. Put all of it where the next person can find it. The conversation that reopens next month will reopen against a record instead of against your memory, and records win those fights.

Decisions that are written, priced, and shared do not need to be relitigated, because they travel to the rooms you are not in and make your case for you.

See also
#

References
#


The Codebase Gardener: Keeping a Codebase Sane When Teammates and Their Agents Pull in Every Direction

You cannot review your way to a sane codebase when the code is being produced faster than you can read it. This is the new arithmetic of the LLM era, and it is brutal for the one engineer on the team who cares about consistency. The strategy that actually works is to stop fighting at the pull request and start fighting at the layer everyone, and every agent, is forced to pass through.

The Math Changed Under Us
#

For most of software history, one careful engineer could hold a codebase together by reviewing most of what landed in it. The volume was human. A teammate produced code at roughly the rate you could read it, and a determined reviewer could keep the drift in check. That equilibrium is gone, and two things killed it.

First, throughput. A teammate driving an LLM agent now opens in an afternoon the pull requests that used to take a week. The unit of work did not change; the rate at which units appear did.

Second, plurality. Each teammate may be driving a different agent, loaded with different skills, different conventions, a different mental picture of what “good” means for this codebase. The codebase does not converge on a single style through argument anymore. It fragments into as many dialects as there are agents producing it, and each dialect looks plausible, because plausibility is exactly what an LLM optimizes for. A function can be internally coherent, consistent with nothing around it, and wrong about the domain all at once.

You, alone, reading diffs, are now the slowest station in a pipeline that was designed to outrun you. Per-unit review scales linearly with your hours, and the entropy is being produced exponentially. No amount of discipline closes that gap.

Reviewing Harder Is the Trap
#

The instinct of the engineer who cares is to review more, longer, more strictly. This is exactly the wrong move, for reasons I laid out in Rethinking Code Review in the Age of LLMs. Every hour you spend line-editing someone else’s agent output is an hour the next three pull requests go unread. You become the bottleneck, and bottleneck reviewers do not stay bottleneck reviewers for long. Teammates start tagging each other for approvals, slicing changes to slip under your threshold, or simply waiting you out until you tire.

You end up exhausted, quietly resented, and still losing ground to the codebase. The worst part is that the work feels virtuous while you do it. Catching a misnamed variable in review feels like defending the standard. It is not. It is fixing one small problem while the codebase drifts as a whole.

Review is a per-unit activity, and per-unit activity cannot match entropy that is being produced faster than you can read it. If your entire strategy is “I will catch the problems at review,” the strategy has already lost. The question is not how to review better. The question is how to make most of the review unnecessary.

Move the Leverage From Review to Infrastructure
#

The way one person defends a codebase against many producers is to stop operating on the units and start operating on the system that produces them. This is the theory of constraints applied to code quality: if you cannot keep up at the review station, do not add more reviewers. Change the station.

Concretely, it means every standard you care about has to live in one of the few places work is forced to pass through, rather than in your head or in your review comments. There are only a handful of such places, and together they are the entire leverage surface available to a single engineer.

The skill and rule files every agent loads at the start of a task. The CI gates every pull request must clear before it can merge. The templates and scaffolds every new module begins from. The default configuration the linters, formatters, and analyzers ship with. The hooks that run on commit, on push, and on merge.

If your standard is encoded in any of these, it executes on every change, forever, without you being present. If it lives only in your review comments, it executes only when you are awake, looking, and willing to argue about it. That is the whole difference between a standard and an opinion. A standard runs whether or not anyone agrees with it. An opinion dies the moment you go on vacation.

This is the same point made, from the team’s perspective, in Bringing Everyone to the Same Level: the senior engineer’s edge was always a process they ran in their head, and a process in a head does not scale. Here the stakes are higher, because you are not trying to lift the team to your level. You are trying to keep the standard in place while the team is actively, if innocently, drifting from it. The mechanism is the same. Encode the process, and let the encoding do the defending.

Make the Right Thing the Path of Least Resistance
#

Most code in a codebase is the way it is because that was the easiest thing to type at the time. This is a feature, not a bug, for someone trying to steer a codebase alone. If the easiest path also happens to be the correct path, conformity is free, and nobody has to be persuaded of anything.

This is the paved road idea, and it is the single highest-leverage intervention available to a solo defender. You do not get compliance by arguing for it. You get it by making the compliant thing the thing that happens when nobody is thinking.

Ship a project template that already has the test harness, the lint config, the migration format, and the observability scaffolding wired in, and a teammate who spins up a new service produces conforming code by default, without ever reading your standards document. Wire the migration linter into CI so that a non-backward-compatible migration fails to merge, and the question of whether backward compatibility is “our standard” stops being a question at all. Friction for the wrong thing, zero friction for the right thing. That asymmetry does more work than any amount of documentation, because it operates on the path of least resistance rather than against it.

The flip side matters just as much. Remove friction from the behavior you want. If you want small, reversible changes, make small changes trivially easy to merge. If reviewing is expensive, people batch, and batching is exactly what produces the large, entangled, unreviewable pull requests that defeat you. Tax what you want less of, and subsidize what you want more of, and do both in the tooling rather than in the standup.

Invariants, Not Preferences
#

Most review wars never end because most of what people fight over are preferences. Tabs versus spaces. Single exit versus early return. One assertion per test. Preferences are arguable, and in an LLM-saturated codebase, arguable means unwinnable, because the other engineer’s agent was told the exact opposite of what yours was told, with equal confidence.

The move is to convert as many preferences as possible into invariants before they can become arguments. An invariant is a property the system will enforce whether or not anyone agrees with it. Database migrations must be backward compatible. New dependencies require an audit record. Public API changes require a feature flag. Cyclomatic complexity above a threshold fails the build. These are not opinions. They are machine-checkable constraints, and a constraint that a machine checks is a constraint you never have to argue about again.

Every preference you promote to an invariant is one fewer conversation you have to win, and one fewer place the codebase can drift while you are not looking. Reserve your remaining human attention for the small set of things that genuinely cannot be encoded: taste, architectural direction, whether a feature should exist at all. Everything else should be a gate or a default, silently doing the work you used to do by hand.

Garden in Sweeps, Not in Diffs
#

Per-PR review is not the only way to fight decay, and in this era it is no longer even the best way. Some drift will always get through, because no gate is perfect and because some of what rots a codebase is not visible in any single diff. Naming conventions slide. Duplication accumulates. A module that was clean in isolation becomes a knot once three teammates have each extended it in a different direction.

The answer is to operate the Boy Scout rule at the codebase level rather than at the commit level: run regular maintenance sweeps instead of trying to catch everything one pull request at a time. Once a week, run the dead-code analysis. Run the duplication detector and look at the new clusters. Pull the complexity trend report and see which functions crossed a threshold this week. Skim a list of newly added dependencies.

A sweep lets you fix the eighty percent of drift that no individual pull request would have surfaced, and it lets you fix it in a way that does not require winning an argument on each one. You are not blocking anyone’s work. You are tidying, in batches, on your own time, against objective signals from the tools. The gardener does not follow every leaf as it falls. The gardener rakes.

This is also the frank acknowledgment that some entropy is the cost of speed, and that the goal is not zero drift. The goal is a drift rate low enough that your weekly sweep runs net positive, so that over months the codebase gets cleaner rather than dirtier even as it grows. A codebase that gets slowly cleaner under load is a codebase you are successfully defending. A codebase that gets slowly dirtier no matter how hard you review is one you are losing, and the review is not the fix.

Write the Decisions That Outlast You
#

One of the cheapest, highest-leverage things a solo defender can do is write things down in a form that survives without them. Architecture decision records, one page each, capturing what was decided, what alternatives were rejected, and why. A short conventions document that is explicitly the source of truth the lint config is derived from. A “why this exists” header on the modules most likely to be misunderstood.

The point of writing it down is not to win today’s argument. Today’s argument will be re-litigated regardless, because the engineer on the other side, or their agent, has not read it. The point is to ensure that the same argument does not have to be re-won from scratch every time, by you, in real time.

A decision that lives in a file is a decision the next agent can be pointed at. A decision that lives only in your head dies the moment you switch teams, or take a week off, or simply get tired of explaining it for the fiftieth time. When your defenses are encoded as files, the codebase keeps its structure without you holding it together. When they are encoded only as your vigilance, the codebase is one two-week vacation away from drift you will spend a month undoing.

Pick the Battles That Are Actually One-Way Doors
#

You cannot defend every standard, and trying to is the fastest route to burnout. The discipline is to decide, in advance, which fights are worth your scarce attention, and the useful frame is the distinction between one-way and two-way doors, which I wrote about in When Engineers Disagree on Best Practices.

A one-way door is a decision that is hard or impossible to reverse. Choosing a primary database. Committing to a service boundary that will be expensive to move later. Adopting a framework that will permeate every file. Dropping a database column. These deserve your full attention, and as the defender of the codebase these are where you should spend it. Block them, slow them, write the decision record, make the team justify the trade-off.

A two-way door is a decision you can undo in an afternoon. A naming convention. A helper function in the wrong package. A test that could have been structured better. Let these go, or fix them in a sweep later. The solo defender who treats every diff as a one-way door exhausts themselves on reversible things and has nothing left for the decisions that actually compound.

This is also the cure for the resentment that otherwise makes this role unsustainable. You will see things every day that are not how you would have done them. Most of them do not matter. Learn to recognize which decisions are irreversible, let the reversible ones pass, and you will last long enough to actually defend the things that count.

The Real Limit
#

Sometimes the team will not align, and no amount of infrastructure will fully save you. A teammate may insist on driving their own agent with their own skills, their own conventions, their own picture of the codebase, and treat your paved road as a suggestion rather than a default. A manager may value shipping velocity over every standard you have encoded, and quietly override the gates that matter.

At that point your job is no longer to win. It is to make the cost of the drift visible. Keep the trend reports. Keep the complexity numbers. Keep the record of which decisions were one-way doors that got walked through without the decision being made. Do this not to build a case against anyone, but because the most powerful thing a defender can produce, when defense fails, is a clear record of what was lost and when, so that the next attempt at sanity starts from evidence rather than from vibes.

And know when to stop defending a particular front. A codebase is not worth your health, and a team that has decided, collectively, to let the codebase rot will rot it with or without you. Your leverage is highest at the start of a codebase’s life and lowest once the rot is structural. If you have encoded what you can, written down what you know, and the drift is still winning, the rational move is to spend less energy fighting and more energy deciding whether this is still the codebase you want to be responsible for.

What to Do Next
#

You do not need permission, and you do not need a migration. Pick the single standard that is being violated most often, the one that costs you the most review time, and convert it from a review comment into a gate. A lint rule. A CI check. A line in the skill file every agent loads. Run it on every pull request, including the ones you do not personally review.

Then do it again, next week, with the next most expensive standard. Each conversion is a piece of vigilance that stops being yours and starts being the system’s. Over a quarter, the surface you have to defend manually shrinks to the small set of genuinely judgment-laden calls, and the volume of entropy you face stops mattering quite so much, because most of it is being caught upstream of you, by machinery that does not tire.

The solo defender who wins is not the one who reviews the most. The solo defender who wins is the one who has made themselves, slowly and deliberately, the part of the pipeline that is no longer strictly necessary.

A sane codebase in this era is not one that a heroic reviewer holds together by force of attention. It is one whose standards have been pushed so far upstream, into the skills and gates and defaults and templates, that the code arrives mostly correct, and the reviewer is left doing the small amount of work that only a human can do. Build that, one encoded standard at a time, and the direction the rest of the team is pulling in starts to matter a great deal less.

See also
#

References
#

  • Wikipedia, “Theory of Constraints” – Goldratt’s framing for why you change the bottleneck station rather than adding effort at it, the basis for moving leverage from review to infrastructure
  • Wikipedia, “Paved road” – the principle of making the compliant path the easiest path, which lets a single engineer steer a codebase without persuading anyone
  • Wikipedia, “Technical debt” – the metaphor for accumulated drift that a weekly gardening sweep is meant to keep net-negative
  • Wikipedia, “Broken windows theory” – why visible decay accelerates further decay, and why keeping the drift rate below the sweep rate matters disproportionately
  • tomzx/agents – a working library of the skill files and review gates that implement the infrastructure-first defense described here

The Self-Evolving Repository: Automating a GitHub Project End to End with LLMs

The question is not whether an LLM can write code for your project. It already can. The question is whether a project can continue to evolve, fix its own bugs, add its own features, and improve its own quality without you ever opening a terminal, writing a prompt, or reviewing a pull request.

This is the self-evolving repository: a GitHub project where every function of the maintainer has been replaced by an automated loop driven by LLMs. No steering. No human-in-the-loop. The maintainer sets it up once, walks away, and the project keeps moving.

I want to be precise about what this means, what it requires, and where it breaks.

What Full Automation Means
#

Most LLM-assisted development today is interactive. A developer describes a task, the LLM writes code, the developer reviews, requests changes, and merges. The developer is the steering wheel. The LLM is the engine.

Full automation removes the steering wheel. The project must decide for itself what to work on, implement the work, verify it, and ship it. The maintainer who set it up does not intervene in the loop.

This is a fundamentally different problem from “use an LLM to write faster code.” It is the problem of encoding every judgment a maintainer makes into a system that can execute that judgment autonomously, reliably, and indefinitely.

A maintainer of an active project does many things. They triage bug reports. They decide which feature requests align with the project’s direction. They prioritize competing work. They review contributions. They fix CI when it breaks. They update dependencies. They write documentation. They make architectural decisions. They decline changes that would make the codebase worse.

Each of these is a decision that currently requires human judgment. Full automation means building a system that makes each of these decisions without the human.

The Autonomous Loop
#

A self-evolving repository runs a continuous loop.

Observe → Decide → Implement → Verify → Ship → Learn

Each stage must be fully automated.

1. Observe
#

The system continuously monitors signals about the project’s state. Open issues and their content. Pull requests from external contributors. CI failures and test results. Dependency vulnerability alerts. User feedback in discussions. Stack Overflow questions mentioning the project. Download and usage statistics. Error reports from production telemetry if the project is a library or service.

These signals are the system’s eyes. Without them, the project is blind to what needs attention. The richer the signal sources, the better the system can prioritize.

A project that only monitors its own issue tracker sees a narrow slice of what needs attention. A project that also monitors community discussions, ecosystem health, and downstream breakage has a much fuller picture of where to invest effort.

2. Decide
#

This is the hardest stage, and I will return to it.

Given the observed signals, the system must decide what to work on next. This is not just “pick the highest priority issue.” It is a sequence of judgments: which problems are worth solving, which are urgent, which can wait, which should be declined, which require architectural changes versus incremental fixes.

The decision stage encodes the maintainer’s judgment about what matters. If this encoding is poor, the project will be busy but not productive, shipping changes that do not move the project in a meaningful direction.

3. Implement
#

The system decomposes the chosen work into tasks and generates code. This is the part that LLMs are already good at. Given a clear specification and sufficient context about the codebase, current models can produce working implementations for most routine tasks.

The implementation stage benefits from the same patterns that work in interactive LLM development: competitive generation (multiple attempts, best one selected), iterative refinement (generate, evaluate, regenerate), and specification-driven development (the spec is authored before the code).

The difference is that in the autonomous loop, the specification is also generated by the system, not by a human.

4. Verify
#

Before any change ships, it must pass automated verification. This is the quality gate that replaces human code review.

The verification pipeline should include: the existing test suite, static analysis and linting, security scanning, type checking, and scenario-based validation where an LLM evaluates whether the implementation satisfies the specification.

As I argued in Rethinking Code Review in the Age of LLMs, human code review of LLM-generated code is the lowest-value activity in the pipeline. Automated verification is not just faster, it is more consistent and more reliable for the kinds of checks that matter at this stage.

The critical requirement is that verification criteria are defined before implementation, not after. If the system writes tests to match what it already implemented, the tests prove nothing. The specification and its acceptance criteria must exist first.

5. Ship
#

Verified changes are merged and released. This stage is mostly mechanical: merge the pull request, run the release pipeline, publish the package, update the changelog.

The one non-obvious requirement is a rollback mechanism. If a shipped change introduces a regression that the verification pipeline missed, the system needs to detect the regression in production telemetry and automatically revert. Without this, errors compound.

6. Learn
#

The system records what worked and what did not. Every failed implementation, every reverted change, every missed bug is an input to future decisions.

This learning takes several forms. Failed prompts and their corrections become few-shot examples for future implementations. Bugs that escaped verification become new test cases. Decisions that led to regressions update the prioritization model. Architectural patterns that caused repeated problems become constraints the system avoids.

Over time, the system accumulates institutional knowledge the way a human maintainer does, but in a form that is explicit, versioned, and transferable.

The Direction Problem
#

The hardest problem in a self-evolving repository is not implementation. It is not verification. It is not even CI automation.

It is direction.

When a human maintainer decides what to work on, they are applying judgment that is difficult to articulate. They know the project’s trajectory, the community’s needs, the competitive landscape, and the technical debt that is becoming critical. They have taste. They have a vision for what the project should become.

Encoding this judgment into an autonomous system is the real challenge.

The roadmap as a steering mechanism
#

The maintainer’s initial setup must include a machine-readable roadmap. Not a vague vision statement, but a structured document that defines: the project’s purpose and non-goals, the target user and their needs, the quality bar for changes, the architectural principles that must be respected, and the current priorities ranked by importance.

This document is the project’s constitution. Every autonomous decision is evaluated against it. When the system decides what to work on, it checks the decision against the roadmap. When the system reviews a contribution, it checks whether the contribution moves the project toward a roadmap goal.

The roadmap does not need to be exhaustive. It needs to be sufficient to distinguish work that matters from work that does not. This is a lower bar than most people assume. Most of a maintainer’s triage decisions are not subtle architectural judgments. They are straightforward: this bug affects users, fix it; this feature request is out of scope, decline it; this dependency has a security vulnerability, update it.

Direction from external signals
#

The roadmap provides static direction. External signals provide dynamic direction.

If the system observes that a particular error message is appearing frequently in community discussions, that is a signal to improve the error message or fix the underlying issue. If the system observes that a downstream project broke after a recent release, that is a signal to fix the breaking change. If the system observes that a dependency released a major version, that is a signal to evaluate the migration.

These signals allow the project to evolve in response to its environment without a human pointing the way. The system reacts to what is actually happening rather than to what someone predicted would happen.

The drift problem
#

Signal-driven direction carries a fundamental tension. A self-evolving repository that only reacts to external signals will optimize for whatever those signals measure. If the signals are bug reports, the system will become excellent at fixing bugs and terrible at anything else. If the signals are feature requests, the system will accumulate features and lose coherence.

This is Goodhart’s law applied to software maintenance: when a measure becomes the target of an autonomous system, it ceases to be a good measure.

The roadmap exists to counteract this drift. It is the fixed point that keeps the project aligned with its original purpose even as the system optimizes for observable signals. But the roadmap itself can become stale. A project’s context changes over time. User needs shift. The ecosystem evolves. A roadmap written in January may be wrong by July.

A fully autonomous system needs a mechanism for updating its own roadmap. This is where the direction problem becomes genuinely difficult. Updating the roadmap means making a judgment about what the project should become, and that judgment requires understanding the project’s users, ecosystem, and competitive position at a level that current LLMs can approximate but not fully replicate.

The pragmatic solution is not to solve this perfectly but to bound it. Let the system make small roadmap adjustments based on observed signals, but require large directional changes to go through a human review. This keeps the system autonomous for 95% of decisions while preserving human oversight for the 5% that determine the project’s long-term trajectory.

Quality Without a Human
#

The quality question is the one most people get stuck on. “How do you trust code that no human reviewed?”

The answer is the same one I gave in The Future of Code Review: you do not trust the code, you trust the verification system.

A self-evolving repository needs a verification pipeline that is more rigorous than what most human-maintained projects have today. Not because the code is worse, but because there is no human fallback.

The pipeline should have multiple independent layers, each catching different classes of problems.

Unit and integration tests catch functional regressions. These should be comprehensive and fast. The system should be able to run the full suite in minutes, not hours.

Property-based testing catches edge cases that example-based tests miss. For pure functions and data transformations, property tests can generate thousands of inputs automatically, surfacing bugs that a human would never think to test.

Static analysis catches type errors, security vulnerabilities, and common anti-patterns. Tools like Semgrep, CodeQL, and language-specific analyzers should run on every change.

Mutation testing verifies that the test suite is actually meaningful. If you can mutate the code and the tests still pass, the tests are not testing what you think they are. This is especially important when the tests themselves are LLM-generated.

Scenario-based validation uses an LLM to evaluate whether the implementation satisfies its specification. This is the layer that catches semantic problems that automated tests miss: the code works, but it solves the wrong problem.

Adversarial verification has a separate agent actively try to break the implementation. This agent writes tests designed to fail, probes edge cases, and attempts to find inputs that produce incorrect behavior. It is the automated equivalent of a hostile code reviewer.

The key insight is that each layer should be independent. If the same LLM that wrote the code also wrote the tests and also ran the review, the correlated failure rate is high. The system will make the same mistake in implementation, tests, and review because they all share the same blind spots.

Diversity of verification methods reduces correlated failures. Static analysis does not share blind spots with property testing. Mutation testing does not share blind spots with scenario validation. The layers are strongest when they are structurally different, not just different invocations of the same model.

What Can Go Wrong
#

A self-evolving repository is a complex autonomous system. Complex autonomous systems have failure modes that are hard to predict.

Silent quality decay
#

Silent quality decay is the hardest failure mode to notice. The system ships changes that pass all verification layers but gradually degrade the codebase’s quality. Each individual change is defensible. The cumulative effect is a codebase that is harder to maintain, slower to evolve, and full of subtle interactions that no one understands.

This happens when the verification pipeline measures immediate quality (does this change pass tests?) but not systemic quality (does this change make the codebase healthier?). Metrics like cyclomatic complexity, coupling, and test coverage can help, but they are proxies, not ground truth.

The complexity spiral
#

Without a human saying “this is too complex, simplify it,” the system may accumulate complexity over time. Each new feature adds code. Each bug fix adds special cases. Each dependency update adds compatibility layers. The codebase grows without bound because nothing in the autonomous loop says “stop adding, start removing.”

A self-evolving repository needs an explicit simplification loop. The system should periodically identify the most complex parts of the codebase and attempt to simplify them, even if no bug is forcing the change. This is technical debt reduction as an automated process.

Specification drift
#

When the system writes its own specifications, the specifications can drift from what users actually need. The system optimizes for specifications it can satisfy, not specifications that matter. Over time, the project accumulates well-implemented features that nobody uses.

This is the autonomous version of the problem I described in The Shifting Bottleneck: when implementation becomes free, deciding what to implement becomes the hard part. In a self-evolving repository, the decision problem is fully automated, which means the drift can go unnoticed for longer.

Cascading failures
#

A single bad change that passes verification can break downstream functionality in ways that the verification pipeline does not catch. The system then generates fixes for the symptoms rather than reverting the cause. Each fix introduces new changes, each of which can introduce new problems. The system enters a fix loop, shipping increasingly desperate patches that make the situation worse.

The defense against this is blast radius limitation. Changes should be small, merged incrementally, and monitored for downstream impact. The system should have a threshold: if more than N changes in a row require fixes, stop and flag for human attention rather than continuing to patch.

Cost runaway
#

Every iteration of the autonomous loop costs money: API calls for generation, verification, and decision-making. A system that is stuck in a fix loop, or that is attempting overly ambitious changes, can burn through significant compute before anyone notices.

The system needs explicit budgets. A maximum number of iterations per change. A maximum cost per change. A maximum number of changes per day. When a budget is exceeded, the system pauses and logs the situation rather than continuing to spend.

What the Maintainer Does Once
#

The maintainer’s role in a self-evolving repository is concentrated entirely in the setup phase. Everything that happens after setup is automated.

The setup has five components.

The roadmap. A structured document defining the project’s purpose, non-goals, priorities, quality standards, and architectural principles. This is the single most important artifact. It determines whether the autonomous system makes good decisions or busy decisions.

The verification pipeline. The combination of test suites, static analysis, property tests, mutation tests, scenario validation, and adversarial verification that gates every change. This pipeline must be in place and passing before the autonomous loop starts. If the verification pipeline is weak, the autonomous system will ship low-quality code.

The decision policy. The rules that govern how the system prioritizes work. What signals does it monitor? How does it rank competing tasks? When does it work on bugs versus features versus technical debt versus documentation? This policy encodes the maintainer’s judgment about how to allocate attention.

The guardrails. Budget limits, blast radius constraints, escalation triggers, and rollback mechanisms. These are the safety systems that prevent the autonomous loop from causing damage when something goes wrong.

The learning infrastructure. The mechanisms by which the system records outcomes and feeds them back into future decisions. This includes the few-shot example library, the bug-to-test pipeline, and the decision evaluation framework.

Once these five components are in place, the autonomous loop can run. The maintainer’s ongoing involvement is limited to reviewing escalation reports, the small percentage of decisions that the system flags as requiring human judgment.

When This Makes Sense
#

Not every project should be a self-evolving repository.

This approach makes sense for projects with: clear specifications and measurable quality criteria, active user bases generating feedback signals, maintenance-heavy codebases where most work is bug fixes and incremental improvements, and well-established architectures where most decisions are routine.

It makes less sense for projects that are: in early exploration where direction changes frequently, in domains where correctness is safety-critical, in codebases with poor test coverage or unclear specifications, or where the competitive advantage is design taste that is hard to encode.

The threshold question is simple: can you write down the rules a competent maintainer would follow, well enough that following them mechanically produces good outcomes? If yes, the system can work. If no, the system will be autonomous but not effective.

The Deeper Question
#

A self-evolving repository raises a question that I explored in Scaling the LLM Agent Company: when the system is its prompts, its pipelines, and its proprietary context, what is the project?

The project is no longer its code. The code is a byproduct of the system that generates it. The project is the roadmap, the verification pipeline, the decision policy, the guardrails, and the accumulated learning data. These are the artifacts that determine what the project becomes.

This reframes what it means to maintain a project. The maintainer’s most important work is not writing code, reviewing pull requests, or fixing bugs. It is designing the system that does all of those things autonomously. The maintainer becomes an architect of processes rather than an executor of tasks.

The project that outlives this setup is the one where the autonomous system makes decisions the maintainer would have made, ships quality the maintainer would have approved, and evolves in a direction the maintainer would have chosen.

The question is whether you can encode your judgment well enough to make yourself unnecessary.

If you can, the project evolves without you. If you cannot, no amount of automation will compensate for the missing taste.

See also
#


Bringing Everyone to the Same Level: How Skills and LLMs Collapse Code Quality Variance

Every team has the same structure. A few engineers ship clean, well-tested, well-scoped work, and the rest ship work that mostly works. The gap between them is not typing speed. It is everything that happens before and after the typing: the steps they remember to run, the checks they know to perform, the conventions they learned by fixing past failures. That knowledge has always been the real asset, and it has never scaled, because it lived inside a small number of heads.

LLMs alone do not fix this. They make everyone faster, which is a different thing from making everyone produce the same quality. Done naively, they widen the variance, because an LLM is a multiplier on the quality of the instructions it receives. The fix is to give every engineer, and every model, the same instructions. That is what a skill is, and it is why skills are the mechanism that finally brings everyone to the same level.

The Variance Problem Has Always Been a Knowledge Problem
#

Ask yourself what actually separates the output of your strongest engineer from your weakest. It is rarely the language syntax. Both of them can write a function.

The difference is that the strong engineer, before touching code, does a long list of invisible things. They look for an existing solution before building a new one. They write down what “done” means before they implement. They check whether the change can be undone. They name things the way the rest of the codebase names things. They write the edge case the junior would have forgotten. They run the linter, and they run it before opening the pull request, not after a reviewer asks.

Each of these is a step, and a step can be written down. For most of the history of software, these steps were passed on informally, by watching and working alongside others. You learned them by pairing with someone better, by getting review comments on your seventh PR, by breaking production once and remembering forever. This is mentorship, and it works, but it is slow, expensive, and uneven. It cannot keep up with a team that is hiring, and it cannot keep up with a codebase that is changing. The senior engineer’s edge was always a process they ran in their head, and a process in a head does not scale.

LLMs Raise the Floor but Not the Ceiling
#

Drop an LLM into this situation and the naive expectation is that it levels the field. It does not, at least not on its own.

What an LLM actually does is amplify whatever it is given. Give it a vague one-line prompt and you get back plausible, generic code that does not match your conventions and forgets your edge cases. Give it a precise specification, the relevant files, the patterns to reuse, and the checks to satisfy, and you get back code that is hard to distinguish from your best engineer’s output. The model is the same in both cases. The difference is entirely in the context, which, as I argued in The Importance of Context When Interacting with LLMs, is the entire mechanism by which a frozen set of weights produces behavior relevant to your situation.

This has an uncomfortable implication. Before skills, the LLM made the senior engineer better and the junior engineer faster, and the gap between them stayed roughly the same. The senior engineer instinctively provides the context the model needs, because they know what good work requires. The junior engineer does not know what they do not know, so they ask for less, and they get less back. The tool that was supposed to democratize quality quietly reproduced the existing hierarchy, because it rewarded the same hidden knowledge that had always separated them.

A Skill Is the Senior Engineer’s Process, Made Executable
#

A skill is a versioned file, usually markdown, that tells an LLM agent exactly how to perform a task. Not a vague hint. The steps, in order, the checks to run before declaring success, the gates the output must pass, and the format the output should take. When the agent loads the skill, it stops improvising and follows the encoded process instead.

This is where the comparison to a human matters. A junior engineer told to “go implement the feature” forgets half the steps, because they never fully learned them. A junior engineer, or even an autonomous agent, told to implement the feature through a skill cannot forget the steps, because the steps are in the prompt the model reads on every run. The skill is not advice the engineer might ignore. It is part of the execution path.

Think of it as the checklist idea from medicine and aviation, with one critical upgrade. A surgical checklist still depends on a tired human choosing to read it and choosing to follow it. A loaded skill does not depend on anyone’s discipline. The model follows it because the skill is the instruction, and following instructions is what the model does.

Why This Collapses the Variance
#

Run the same task through two engineers with two different LLMs, but the same skill, and watch what happens to the output. Both implementations start from the same specification step. Both run the same “have you checked for an existing solution?” step. Both end at the same verification step, with the same gates. The code they produce is not identical, but it converges toward a common standard, because the process that produced it is identical.

The thing that used to vary, the invisible checklist inside each engineer’s head, is now constant. What varies is only the judgment applied at each step, and even that is bounded by the gates the skill enforces. Skills do not make everyone equally brilliant. They make everyone equally unable to skip the steps that matter, and skipping the steps that matter is what produced most of the variance in the first place.

This is the same logic I described, from the other direction, in Developer Trust Profiles. There I argued the ideal end state is one where “every contributor, senior engineer or new hire, funnels their work through agents that enforce the same standards,” so that authorship stops carrying signal and “the output converges into something homogeneous.” The trust profile was a transitional step, and the point was to make itself obsolete. Skills are the mechanism that completes that transition. They are the shared, enforced pipeline that makes the variance shrink in the first place.

The Leverage Moves Up, Again
#

If the junior engineer can now produce work that follows the senior engineer’s process, what is left for the senior engineer to do?

The answer is the same one that keeps appearing everywhere AI touches the development pipeline. The bottleneck moves up the decision chain, as in The Shifting Bottleneck, and the leverage moves with it.

The new seniority is not in running the skills. Anyone, and any agent, can do that. The new seniority is in writing them. Deciding what the process should be in the first place. Deciding which gate matters and which only looks like a gate. Deciding what “done” means for this kind of task, precisely enough that a model can enforce it. The judgment that used to be applied privately, one pull request at a time, is now applied once, at the skill level, where it benefits every future execution.

This is why a good skill library is one of the most valuable assets a team can hold. It is the institutional memory of how the team does things well, written in a form that executes itself instead of sitting in a wiki nobody reads. When a senior engineer leaves, the skills stay, and the standard stays with them. When a new engineer joins, they do not spend a year absorbing the conventions through code review. They load the skills on day one, and their first pull request already follows the team’s process.

What a Skill Has to Get Right to Actually Level the Field
#

Not every file labeled “skill” collapses the variance. Most do not. Writing a skill that genuinely raises everyone to the same level requires a few specific disciplines.

Encode the steps that are actually forgotten, not the steps everyone already does. A skill that says “write clean code” is worthless, because nobody sets out to write dirty code and the phrase carries no executable instruction. A skill that says “before implementing, run a search for existing solutions and list what you found” changes behavior, because that is exactly the step people skip when they are in a hurry.

Make the gates concrete enough for a model to evaluate. “Make sure it is well tested” is an opinion. “Write tests for the empty input, the maximum value, and the concurrent case, then run the suite and confirm it passes” is a gate. The whole mechanism depends on the model being able to check its own work, and it can only do that against criteria it can test. This is the same reason I argued, in Rethinking Code Review in the Age of LLMs, that quality is a property of your constraints, not of your reviewers. The constraints in a skill are where the quality actually lives.

Keep the skill shorter than the attention it will receive. As the “lost in the middle” work showed, a model’s reliability degrades when a context fills with noise. A skill that tries to encode every possible consideration becomes a skill the model half-follows. Prefer several focused skills over one giant document, and cut anything the model would do correctly without being told.

Version and maintain it like the code it produces. A skill that encodes a convention from two years ago is worse than no skill, because it enforces a stale standard on every run. Treat the skill library as part of the codebase, with owners, review, and the same “does this still justify its cost?” scrutiny you would give any dependency.

The Realistic Limits
#

This only works for the part of the job that is describable, and it is worth being clear about where it stops.

Skills cannot encode taste that the writer cannot articulate. If your best engineer’s advantage is a feel for when an abstraction is about to collapse, and they cannot explain the signals they are reading, that advantage does not survive the translation into a file. It stays in their head, and the skill without it will produce work that is competent but not inspired. For the routine majority of software, which is most of software, the describable process is enough. For the genuinely hard design calls, it is not.

Skills can also lock in mediocrity. A skill that encodes a mediocre process enforces that mediocrity on everyone, consistently, forever. The mechanism is morally neutral. It collapses the variance in whichever direction the skill points, so a careless skill library can quietly lower a strong team to a lower common standard instead of raising the rest. The remedy is that the skills themselves have to be written by the people whose process you actually want to reproduce, and revisited when the process improves.

Finally, this raises the floor for producing code, but producing code was never the only bottleneck. As Software Engineering Teams in the Age of AI argues, deciding whether a feature should exist, and writing a precise specification for it, remain human and valuable. Skills make the execution layer homogeneous. They do not decide what to execute.

What to Do Next
#

You do not need a grand migration to start closing the gap.

Pick the one task where your team’s output varies the most, the one where the senior engineer’s pull request looks nothing like the junior’s. Write down the steps your strongest engineer actually takes when they do it well, the steps they would never admit to because they seem obvious to them. Turn that into a skill, and run the next instance of that task through it, regardless of who is doing it.

Watch the output converge. Then do it again for the next task. The variance does not close all at once. It closes one encoded process at a time, and each skill you add is a piece of seniority that stops being a private habit and starts being a shared standard.

The team that wins in this era is not the one with the most powerful model. It is the one whose best engineer’s process runs, unchanged and unskipped, in every other engineer’s session.

See also
#

References
#


When Engineers Disagree on Best Practices: Surviving the Forming Stage

A new team’s first disagreement about best practices is not a warning sign. It is the moment the team actually starts becoming a team. What separates teams that survive this phase from those that stall is not which practices they adopt, but how they resolve the disagreement.

Why the Forming Stage Produces Disagreement
#

Every engineer arrives with a set of practices that worked somewhere else. The testing strategy that saved your last team from weekly regressions. The code review checklist that caught the security bug before it shipped. The branching model that kept releases sane. These are not random preferences. They are lessons from real failures, and the engineer who brings them believes, correctly, that they matter.

The problem is that every other engineer on the new team arrives with their own set of practices, learned from different failures. When these differing practices meet, you get the pattern Bruce Tuckman described in his 1965 study of small-group development: forming, storming, norming, performing. Teams do not skip from forming straight to performing. They pass through storming, the phase where members test each other, surface conflicting assumptions, and negotiate how work should be done.

The forming stage is uniquely prone to practice disagreements for three reasons.

First, there is no shared history. On an established team, conventions are settled and invisible. The team just does things a certain way because that is how it has always been done. A new team has no “always,” so everything is up for debate at once.

Second, there is no established trust. Disagreeing about code style feels low-stakes on a team where everyone has shipped together for a year, because everyone knows the argument is about the style, not about competence. On a new team, the same disagreement can feel like a referendum on someone’s expertise, because no one has yet proven anything to anyone else.

Third, there is no shared vocabulary for resolving conflict. An established team has accumulated meta-norms: how they make decisions, who decides what, when to escalate, when to let something go. A new team has none of this. So every disagreement is also, simultaneously, a negotiation about how disagreements get resolved.

Most “Best Practice” Debates Are Preference Debates
#

The first thing to recognize is that the phrase “best practice” misleads. A best practice is a practice that is demonstrably better than its alternatives across most contexts. Very few software engineering practices clear that bar.

Consider the debates that take up the most discussion time on a new team. Tabs versus spaces. Git Flow versus trunk-based development. Story points versus no estimates. Unit tests versus integration tests as the primary signal. Commit message conventions. These are not best practices. They are conventions, and a convention’s value comes almost entirely from being shared, not from being optimal. The team that uses the second-best testing framework consistently will outperform the team that uses the best one inconsistently.

This does not mean all practices are equal. Some debates involve genuine principles where the choice has lasting consequences: whether to build or buy a critical dependency, how much to invest in observability before shipping, whether a monolith or services is the right starting architecture. These deserve real deliberation. The skill is in telling the convention debate from the principle debate, because they require completely different resolution mechanisms.

Reversible Versus Irreversible Decisions
#

A distinction that Jeff Bezos drew in his 2016 letter to shareholders maps directly onto the convention-versus-principle problem. Some decisions are one-way doors. Once you walk through, you cannot come back. Choosing a primary database, committing to a cloud provider, or picking a framework that will permeate your codebase are one-way door decisions. These deserve heavy deliberation, because the cost of being wrong is high and the cost of reversing is higher.

Most practice debates on a new team are two-way doors. You can adopt trunk-based development, try it for six weeks, and switch back if it is not working. You can pick a linter configuration, discover it fights your codebase, and change it. Two-way door decisions should be made quickly, because the fastest path to knowing is often to try.

A new team that treats every practice debate as a one-way door will spend its first two months in meetings and produce nothing. A team that treats a genuine one-way door as reversible will make a choice it regrets for years. The discipline is in classifying the decision before debating it.

The Experiment Mindset
#

The single most effective frame for resolving practice disagreement during forming is to treat practices as hypotheses, not as identities.

When two engineers disagree about whether to require code review before merge, the unproductive version of that conversation is an argument about whether code review is good. Both people dig in, cite their favorite blog posts, and the discussion becomes about winning.

The productive version converts the disagreement into an experiment. You require code review for the next month, track how many defects it catches in review versus in production, and look at the data together at the end. Now the two engineers are not opponents. They are co-investigators running the same experiment.

The experiment works because it removes the requirement that someone be wrong. An engineer who advocated for mandatory review does not lose face if the experiment shows it adds little value, because they ran the test in good faith. An engineer who opposed it does not lose face if the experiment shows it catches real bugs, because they agreed to look at the evidence.

A few rules make experiments work.

Time-box them. An open-ended experiment is just a delayed argument. Four to six weeks is usually enough to see signal without exhausting the team’s patience.

Define success criteria before you start. If you wait until the experiment is over to decide what would count as success, you will re-litigate the original disagreement with data instead of opinions, which is only marginally better.

Keep the cost of reversal low. If switching back after the experiment is expensive, the experiment is really an irreversible decision.

Write down the result. A one-paragraph note recording what you tried, what you observed, and what you decided turns a single resolution into institutional memory that saves the next new team member from re-fighting the same battle.

How You Decide Matters More Than What You Decide
#

Here is the point that is easy to miss when you are in the middle of a heated debate about, say, whether to use feature branches or trunk-based development.

The specific answer matters less than you think. What matters enormously, especially during the forming stage, is that the team builds a repeatable process for reaching decisions together.

A team that resolves its first disagreement well, with a clear process, shared evidence, and a decision everyone can live with, has just created something more valuable than the practice it chose. It has created the template for the next disagreement, and the one after that. Each resolved disagreement makes the next one easier, because the meta-process is now familiar.

A team that resolves its first disagreement badly, by letting the loudest person win, or by avoiding the conflict until it grows worse, or by escalating to a manager who picks a winner, has also created a template. That template will repeat. The first disagreement a new team faces sets the pattern for every disagreement that follows.

That template is why the forming stage deserves deliberate attention rather than the hope that things will sort themselves out. The norms a team establishes in its first weeks are sticky. They become “how we do things here,” and once that identity forms, it is far harder to change later than to set well at the start.

A Playbook for the First Disagreement
#

Concretely, when the first real practice disagreement surfaces on a new team, the following sequence works.

Name it. Say out loud that this is a disagreement about practice, that disagreements are expected, and that the goal is to find a resolution the team can commit to, not to identify who is right.

Classify it. Ask whether this is a convention debate, where value comes from consistency, or a principle debate, where the choice has lasting consequences. Ask whether it is a one-way door or a two-way door.

For conventions and two-way doors, decide fast. Default to whoever has to live with the consequence most directly, or to the option that is easiest to change, or to the existing convention if one already exists. Time-box the decision to one conversation, not three.

For principles and one-way doors, slow down. Write a short decision record capturing the options, the trade-offs, and the reasoning. Get the right people in the room, which usually means the people who will own the consequences, not necessarily the people with the strongest opinions.

Either way, record the outcome. A one-page architecture decision record or a paragraph in the team handbook turns a fragile verbal agreement into something a new hire can read six months later.

And then commit. The principle of disagree and commit applies here. Once the team has decided, even those who argued otherwise support the decision fully. A practice executed with full commitment and an imperfect choice beats a better practice executed with resentment and inconsistency.

When Someone Refuses to Commit
#

There is a failure mode worth naming.

Occasionally an engineer treats a practice debate as something they will not concede under any circumstance, refuses to accept the team’s decision, and continues working the old way. This is no longer a disagreement about practice. It is a disagreement about whether the team’s decision-making process has legitimacy.

This must be addressed directly, and early. A team that tolerates a member who overrides collective decisions after they are made has no process at all. It has a veto by the most stubborn person. The conversation is no longer about the practice. It is about participation in a shared system.

In a healthy forming team, this conversation is rare, because the process itself was fair enough that losing a debate does not feel like losing status. When it does happen, resolving it cleanly, with empathy but without accommodation, is one of the most important trust-building acts the team will undertake. Underneath that trust is psychological safety, the shared belief that you can speak up, dissent, and even be wrong without being humiliated, which Google’s Project Aristotle identified as the single strongest predictor of team effectiveness. A team cannot storm productively without it.

The Payoff
#

A team that navigates its forming-stage disagreements well emerges with two assets.

The first is a set of shared conventions, probably imperfect, that let the team move fast without relitigating every choice. The second, and more important, is a shared process for handling the next thing they do not yet agree on.

New disagreements never stop. The codebase grows, the team grows, the technology landscape shifts, and yesterday’s settled practice becomes tomorrow’s debate. The teams that handle this well are not the ones that picked the best practices on day one. They are the ones that learned, during forming, how to disagree productively, decide efficiently, and commit fully.

That skill compounds for the life of the team.

See also
#

References
#


Managing Many Concurrent LLM Agent Sessions

When one person can spawn a dozen LLM agent sessions in parallel, the bottleneck is no longer the agents. It is the human trying to keep track of them all. Your working memory holds a handful of items at best, your context switching cost is real and measurable, and every interruption leaves residue that degrades the next task you pick up. The question is not whether you can run 20 sessions simultaneously. The question is whether you can remain effective while doing so.

The Cognitive Bottleneck
#

Before discussing strategies, it helps to understand what actually limits a human supervisor.

Working memory is tiny. George Miller’s foundational work suggested that humans can hold approximately seven plus or minus two items in short-term memory. Later research by Nelson Cowan revised this estimate down to about four chunks for young adults. Either way, the number is small. If each agent session requires you to remember its goal, its current state, its blockers, and the decision you were about to make, you hit the ceiling at three to five sessions, not twenty.

Context switching has a measurable cost. Research on task switching in cognitive psychology shows that switching between tasks incurs a time penalty, sometimes called “switch cost.” This cost ranges from fractions of a second to several minutes depending on task complexity. When you switch from agent session A to agent session B, you must offload A’s context from your working memory, load B’s context, and reconstruct where you left off. The more complex each session’s context, the more expensive the switch.

Attention residue accumulates. When you switch from one task to another without fully completing the first, part of your attention remains stuck on the previous task. The Zeigarnik effect describes how unfinished tasks persist in memory, consuming cognitive resources even when you are trying to focus on something else. With multiple agent sessions running concurrently, many are perpetually unfinished, creating constant attention residue.

Decision fatigue compounds. Each agent session requires you to make decisions: approve this output, redirect this approach, answer this clarifying question. Research on decision fatigue shows that the quality of decisions degrades after a long session of choice-making. Supervising many sessions means making many decisions, and the later decisions in the day will be worse than the earlier ones unless you manage the load.

Working memory, switch cost, attention residue, and decision fatigue together define the ceiling on how many sessions you can manage. Every strategy below works by attacking one or more of them.

Strategy 1: Externalize All Session State
#

The single most powerful principle is to stop relying on your brain to hold session state. If it is not written down, it is consuming working memory.

Maintain a state file per session. Every agent session should produce a machine-readable and human-readable state file that captures its current status, goal, progress, blockers, and next steps. When you return to a session after an hour or a day, you read the file, not your memory. The file is the single source of truth. This is the agent equivalent of the workstack concept applied at scale.

Use structured status summaries. The state file should follow a consistent template across all sessions. A format like the following works well:

Session: fix-auth-bug
Goal: Fix OAuth callback failing on production
Status: blocked
Progress: Identified root cause (clock skew on load balancer)
Blocker: Need production access to verify fix
Next: Deploy fix to staging, verify callback works

When every session uses the same structure, you can scan a dashboard of twenty sessions in under a minute. The consistency eliminates the cognitive parsing overhead that comes from varied formats.

Write decisions to a log. Every decision you make about a session should be written to the session’s log file with a timestamp and rationale. This serves two purposes. First, when you return to the session, you can see not just where it is but why it is there, without reconstructing the reasoning. Second, the log becomes an audit trail that other humans (or agents) can consume.

Checkpoint before switching. Before switching away from any session, write a one-line “resume hint” that tells your future self exactly where to pick up. This takes five seconds and saves minutes of reconstruction time. The hint should be specific enough that you can act on it without re-reading the entire session history.

Strategy 2: Standardize All Interactions
#

Variability is the enemy of scale. Every difference between how sessions operate is a new thing your brain must parse, increasing cognitive load and switch cost.

Use a uniform session protocol. Every session should follow the same lifecycle: initialization, execution, checkpointing, escalation, and completion. The protocol defines what the agent does at each stage, what it reports, and how it signals that it needs human input. When the protocol is uniform, switching between sessions costs less because you always know what to expect.

Standardize output formats. If session A reports progress in a bulleted list and session B writes a paragraph and session C uses a table, your brain must context-switch not just on content but on format. Pick one format and enforce it everywhere. Markdown headers, numbered lists, and consistent section ordering let your eyes scan quickly without parsing structure.

Establish a shared vocabulary. Define the terms agents use to communicate status: “blocked,” “waiting-for-human,” “in-progress,” “complete,” “failed.” Every agent uses the same words to mean the same things. This reduces ambiguity and eliminates the need to translate between sessions.

Create session naming conventions. A session named “auth-fix-3” tells you nothing when you have twenty sessions. A session named “fix-oauth-callback-prod-bug” tells you exactly what it is doing. Descriptive names reduce the time to identify and triage sessions, which reduces the overhead of switching between them.

Strategy 3: Make Interactions Asynchronous and Batched
#

Synchronous interaction with agents is the enemy of parallelism. If you must respond to each agent within seconds, you can only effectively manage one at a time.

Let agents run to a natural stopping point. Instead of watching each agent in real-time and responding to each question as it arises, configure agents to work until they hit a genuine blocker. At that point, they checkpoint their state and wait. You then process the batch of blocked agents at your convenience.

Batch your decisions. When multiple sessions need human input, collect all the requests and process them in a single sitting. This is more efficient than context-switching for each one individually, because you stay in the same cognitive “mode” for the entire batch. Five decisions made in one focused session will be faster and higher quality than the same five decisions made across five interruptions.

Time-box your session reviews. Instead of monitoring sessions continuously, check them at fixed intervals: every 30 minutes, every hour, twice a day. This creates predictable rhythm and eliminates the reactive, interrupt-driven pattern that fragments attention. Between check-ins, agents work autonomously and you focus on other things.

Coalesce interruptions. If you have three sessions that will each need input within the next 15 minutes, do not handle them one at a time. Wait until all three are ready, then handle them together. Interruption coalescing is a well-known technique in operating systems and it applies equally to human attention management.

Strategy 4: Make Agents Resolve Their Own Ambiguities
#

Every question an agent asks you is a context switch. The fewer questions it asks, the more sessions you can manage.

Provide rich default resolution rules. Instead of letting agents escalate every ambiguity, give them standing instructions for common situations. “If the test suite fails, attempt to fix it before escalating.” “If a dependency is missing, install it from the lockfile.” “If a naming convention is unclear, follow the pattern used in the nearest file.” These defaults eliminate a large fraction of questions that would otherwise interrupt you.

Define escalation thresholds explicitly. Agents should escalate to you only when they cross a defined threshold: they have tried multiple approaches and failed, they need access they do not have, or they are about to make an irreversible decision. Everything else should be handled autonomously. The clearer the threshold, the fewer unnecessary escalations.

Allow agents to ask other agents. If you have multiple sessions running, agents can sometimes resolve each other’s questions. An agent that needs to understand how another part of the system works can query the agent working on that part. This peer-to-peer resolution keeps you out of the loop for routine coordination.

Use confidence-based escalation. Agents that can express confidence in their outputs can be configured to escalate only when confidence drops below a threshold. High-confidence work proceeds without interruption. Low-confidence work pauses for review. This naturally filters your attention toward the sessions that need it most.

Strategy 5: Use Hierarchical Orchestration
#

The most effective way to manage many agents is to not manage them all directly.

Introduce orchestrator agents. Instead of you supervising 20 worker agents, have 4 orchestrator agents each supervise 5 workers. You interact only with the orchestrators, who aggregate and summarize their workers’ status. This reduces your direct interaction count by a factor of 5 and lets you operate within your working memory limit. This mirrors the pattern described in Scaling the LLM Agent Company, where the orchestration layer replaces the human management layer.

Fan-out and fan-in. Give an orchestrator a single high-level task. It decomposes the task, assigns subtasks to workers, collects results, and presents you with a synthesized output. You never interact with the individual workers. Your cognitive load is the same whether the orchestrator manages 3 workers or 30.

Assign team leads per domain. If your agents work across multiple domains (frontend, backend, infrastructure, testing), assign one orchestrator per domain. Each domain lead reports to you with a one-paragraph summary. You manage four domain leads instead of twenty individual contributors.

Layer the hierarchy as needed. For very large numbers of agents, add another layer. A chief orchestrator manages domain orchestrators, who manage team leads, who manage workers. Each layer compresses information, so you always deal with a manageable number of direct reports.

Strategy 6: Practice Progressive Disclosure
#

You should never need to understand everything about every session to make a decision. Information should arrive in layers, from summary to detail, on demand.

Lead with the one-line summary. Every session status should begin with a single sentence that tells you whether action is needed: “Session blocked, needs your decision on X” or “Session running normally, 60% complete.” If the summary says no action needed, you move on. You never read the details unless the summary demands it.

Provide drill-down on request. Behind the one-line summary is a paragraph. Behind the paragraph is the full session log. Behind the log are the raw outputs. Each layer is available but not shown by default. This keeps your default view clean and your cognitive load low.

Use severity indicators. Color-code or tag sessions by urgency: red for “needs immediate human input,” yellow for “proceeding but with a risk you should know about,” green for “all clear.” Your eye scans for red first, yellow second, and ignores green. This triage happens in seconds, not minutes.

Summarize at the right granularity. An orchestrator reporting “all five workers are making progress” is useless if one of them is about to make a critical mistake. An orchestrator reporting every token from every worker is noise. The right granularity is: what is done, what is in progress, what is blocked, and what decision you need to make next.

Strategy 7: Isolate Sessions from Each Other
#

Interference between sessions is a major source of cognitive load. When sessions share state, a change in one can invalidate your understanding of another.

Enforce session independence. Each session should operate in its own workspace, with its own file tree, its own dependencies, and its own state. Sessions should not mutate shared resources that other sessions depend on. This prevents the “I changed something in session A that broke session B” problem, which is one of the most expensive debugging scenarios when managing many sessions.

Use separate branches or worktrees. If sessions work on the same repository, each should use its own git worktree or branch. This ensures that the output of one session does not corrupt the working state of another. When you are ready to integrate, you merge branches deliberately, not accidentally.

Scope tools and permissions per session. Each session should have access only to the tools and resources it needs. A session fixing a frontend bug does not need database write access. This reduces the blast radius of errors and eliminates cross-session interference.

Make session boundaries explicit. When you switch from session A to session B, the boundary should be clear. Close session A’s workspace, open session B’s. The physical or virtual separation reinforces the cognitive separation.

Strategy 8: Build a Session Dashboard
#

When managing more than three or four sessions, you need a single view that shows all of them at once. Switching between terminal tabs or IDE windows to check on each session is itself a form of context switching.

Create a single pane of glass. A dashboard that lists all active sessions with their status, progress, and severity indicators lets you assess the entire fleet in one glance. This dashboard can be as simple as a generated text file or as sophisticated as a web UI. The key is that it presents all sessions in a uniform, scannable format.

Surface only actionable information. The dashboard should show you what needs your attention, not everything that is happening. Sessions running normally should be collapsed or summarized. Sessions that need input should be highlighted. The dashboard is a triage tool, not a log viewer.

Automate status collection. The dashboard should be generated automatically from the state files that each session produces. No manual updates. If a session’s state file changes, the dashboard reflects it. This eliminates the overhead of manually polling each session for status.

Add alerting for critical events. Instead of watching the dashboard continuously, configure alerts for events that genuinely require immediate attention: a session failed, a session is about to make an irreversible change, a session has been blocked for more than N minutes. Everything else can wait for your next scheduled review.

Strategy 9: Use AI to Triage AI
#

One of the most effective ways to manage many agent sessions is to use an LLM to help you manage them.

AI-assisted summarization. Before you look at a blocked session, have an LLM read the session log and produce a one-paragraph summary of what happened, what the blocker is, and what decision you need to make. This saves you from reading potentially hundreds of lines of conversation history. The summary gets you to the decision point faster.

AI-assisted triage. When you return from a break and find 12 sessions waiting for input, have an LLM rank them by urgency and group similar requests. “Sessions 3, 7, and 12 all need the same decision about database schema, you can answer them together.” “Session 9 is urgent, it is blocked on a production deploy.” This pre-processing reduces your cognitive load and helps you batch effectively.

AI-assisted decision drafting. For each blocked session, have an LLM propose a decision with rationale. You review the proposal, accept it or modify it, and move on. Reviewing a well-formed proposal is faster and less cognitively demanding than constructing a response from scratch, especially for the tenth session of the day.

AI-assisted state reconstruction. When you return to a session after days away, have an LLM produce a “previously on” summary that reconstructs the relevant context. This is faster and more reliable than reading the full log yourself, and it handles the long-gap resumption problem that pure state files struggle with.

Strategy 10: Manage Your Own Cognitive Resources
#

The human supervisor is a finite resource. Strategies that optimize agent throughput while ignoring human cognitive limits will fail.

Chunk sessions into clusters. Instead of treating 20 sessions as 20 independent items, group them into 4 clusters of 5. Each cluster shares a domain, a goal, or an orchestrator. You manage 4 clusters, not 20 sessions. This leverages chunking, the same memory technique that lets experts recall complex board positions in chess.

Dedicate focus blocks. Reserve uninterrupted blocks of time for session supervision. During a focus block, you process the queue of blocked sessions, make decisions, and unblock agents. Outside of focus blocks, you do other work. This prevents the constant low-level attention drain that comes from monitoring sessions while trying to do other things.

Reserve decision budget for high-stakes sessions. Not all decisions are equal. A decision about whether to deploy to production matters more than a decision about variable naming. Allocate your decision budget accordingly. Make the important decisions when you are fresh. Batch the trivial decisions and make them in a low-energy slot.

Take recovery breaks. Context switching and decision-making are cognitively expensive. After a heavy session of supervising multiple agents, take a deliberate break to let attention residue dissipate. This is not laziness, it is maintenance of the tool that matters most: your judgment.

Strategy 11: Reduce Session Count Through Better Task Definition
#

The easiest session to manage is the one you never needed to start.

Write precise specifications. A well-specified task runs autonomously and produces fewer questions. A vaguely specified task generates constant clarifying questions, each of which is a context switch. Investing in specification quality before launching a session benefits the session for its entire lifetime. This is the same principle described in The Importance of Context When Interacting with LLMs: the quality of the context determines the quality of the output.

Merge related tasks. If two tasks touch the same code or the same domain, combine them into one session instead of running two. One session managing a coherent area is easier to track than two sessions with overlapping scope.

Eliminate unnecessary sessions. Before launching a new session, ask whether the task is worth the supervision overhead. Some tasks are faster to do yourself than to delegate to an agent and then supervise. The shifting bottleneck principle applies: as agent supervision becomes the bottleneck, the optimal strategy shifts from spawning more agents to being more selective about which tasks to delegate.

Pre-compute common answers. If you find yourself answering the same type of question across multiple sessions, encode the answer once in a shared resource that all sessions can access. A shared FAQ, a coding standards document, or a decision tree eliminates a category of future questions.

Strategy 12: Version and Review Session Patterns
#

Managing many sessions is a skill that improves with deliberate practice.

Record what worked. When a session runs smoothly from start to finish, note what made it smooth. Was the specification clear? Were the escalation thresholds well-calibrated? Did the status format make triage easy? These observations compound into better patterns over time.

Post-mortem failed sessions. When a session drifts from its instructions, review why. Did the agent lack critical context? Did the escalation threshold let it run too long without checking in? Did the state file fail to capture the information you needed to intervene effectively? Each failure is a data point for improving the protocol.

Iterate on the protocol. The session protocol, the status format, the escalation rules, and the dashboard design should all evolve as you learn what works. Treat your agent management system as a product that you are continuously improving. The goal is not to find the perfect system on day one but to get a little better every week.

Share patterns across teams. If multiple people in your organization supervise agent sessions, share what works. The patterns that reduce cognitive load for one person will likely help others. This is the same knowledge-sharing principle that makes software engineering teams effective: institutional knowledge compounds when it is explicit and shared.

The Ceiling: How Many Can You Actually Manage?
#

There is no single number, but the constraints are real.

With no system, no tooling, and synchronous interaction, a human can effectively manage 1 to 2 concurrent agent sessions. The cognitive overhead of tracking each session’s state in working memory and responding to questions in real time is too high to scale further.

With externalized state files, standardized protocols, and asynchronous batching, 4 to 6 concurrent sessions become feasible. You are no longer holding state in your head, and you batch your interactions to reduce switch cost.

With hierarchical orchestration, progressive disclosure, and a dashboard, 10 to 15 concurrent sessions are manageable. The orchestrators compress information, the dashboard provides a single view, and you interact with a manageable number of direct reports.

With AI-assisted triage, tight escalation thresholds, and well-specified tasks, 20+ concurrent sessions are achievable for a human who has invested in the workflow. At this scale, you are no longer managing individual sessions. You are managing a system that manages sessions.

The progression mirrors the task-stack philosophy: you push interruptions onto a stack, handle them in batches, and pop them off when done. The difference is that the stack now contains not just your own tasks but the states of dozens of autonomous workers, and the stack is externalized rather than in your head.

The Meta-Principle
#

Every strategy in this article reduces to one of four operations on the cognitive constraints:

Reduce working memory load by externalizing state, standardizing formats, and chunking sessions into clusters.

Reduce switch cost by standardizing protocols, batching interactions, and isolating sessions.

Reduce attention residue by letting sessions reach natural stopping points, taking recovery breaks, and using focus blocks.

Reduce decision fatigue by having agents resolve their own ambiguities, using decision templates, and reserving decision budget for high-stakes choices.

The agents are not the bottleneck. Your brain is. Every hour you spend engineering the workflow around your own cognitive limits is worth ten hours of trying to power through them.

References
#