core-agent
AGENTS.md9 starsChanged 43 days agoApache-2.0View on GitHub
When an AGENTS.md-aware agent runs inside this repo, this file is loaded into its system prompt as the project-level instruction prefix. It holds rules and pointers. The mechanics, incident history and worked examples behind each rule live in docs/contributor-pitfalls.md and in the skills under .agents/skills/. Read the pointed-to section before doing the thing it guards. Keep this file short: a new rule earns a line here and its story goes there. core-agent is a reusable Go agent runtime built on…
- Deletes or force-pushes
- Installs packages
- Commits and pushes
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,774 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository10 other files it gives its agents.
- .agents/AGENTS.mdAGENTS.md
- .agents/reviewer/AGENTS.mdAGENTS.md
- adversarial-review-gateSkill
- changelog-bulletSkill
- prefix-failure-verificationSkill
- presubmit-sweepSkill
- stacked-pr-orderSkill
- autonomous-setupSkill
- cli-setupSkill
- library-embeddingSkill
The file
Show its rules and text
We split this AGENTS.md into the rules it gives an agent
15 rules. About 0 are common, 5 are distinctive, and 10 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 3
When an AGENTS.md-aware agent runs inside this repo, this file is loaded into its system prompt as the project-level instruction prefix.
- Very specific: line 98
License headers everywhere: Every Go, shell, YAML and Python source file starts with the full Apache 2.0 boilerplate attributed to Google …
- Distinctive: line 120
Tests before merging: Every new package ships with unit tests.
- Very specific: line 136
Write one neutral string, not interactive and headless variants.
- Distinctive: line 140
Never state frequency ("use this generously").
- Very specific: line 163
ADK's
telemetry.New(...)does not install OTEL globals: Call providers.SetGlobalOtelProviders(), as pkg/telemetry/otel.go does. - Very specific: line 185
Worktrees for feature branches: Do feature and parallel-track work in a dedicated git worktree under .worktrees/<name> (git worktree add …
- Very specific: line 195
Run presubmits before every push: Run everything in dev/ci/presubmits/ except the two e2e-* scripts.
- Very specific: line 212
Bumping the
core-tuipin is not just ago.modedit: Every exported interface in github.com/go-steer/core-tui/tui must be implemented … - Very specific: line 219
Adversarial review gate before every PR: Before gh pr create on any change touching Go code:
- Distinctive: line 221
Run a skeptic subagent over the staged diff, checking correctness, races and API misuse, verified against real dependency source rather …
- Distinctive: line 242
Rebase, don't merge: Keep feature branches rebased on main.
- Very specific: line 248
After each parent lands, rebase with --onto.
- Very specific: line 249
After a base-update on a branch stacked on a release-promotion PR, read git diff <base>...HEAD -- CHANGELOG.md.
- Very specific: line 261
Check gh pr view <N> --json mergeStateStatus first.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
core-agent project memory
When an AGENTS.md-aware agent runs inside this repo, this file is loaded into
its system prompt as the project-level instruction prefix. It holds rules and
pointers. The mechanics, incident history and worked examples behind each
rule live in docs/contributor-pitfalls.md
and in the skills under .agents/skills/. Read the
pointed-to section before doing the thing it guards. Keep this file short: a
new rule earns a line here and its story goes there.
What this project is
core-agent is a reusable Go agent runtime built on the Google ADK
(google.golang.org/adk/v2). It is the bottom layer for any Go project that
needs a multi-turn LLM agent. It provides:
- model providers;
- MCP servers and skills;
- instruction loading;
- permission gating;
- budgets and guardrails;
- telemetry;
- durable sessions;
- remote attach;
- a baseline built-in tool suite (files, shell, search, todo).
It ships an in-process TUI (via go-steer/core-tui) as the default TTY surface
and stays runnable headless. Product-specific tools remain a consumer concern.
It was extracted from go-steer/cogo and
keeps that project's layout conventions.
Layout
Library packages live under pkg/, unexported helpers under internal/, and
binaries under cmd/.
pkg/
agent/ ADK agent + runner wrapper; autonomous loop; background
subagents; compaction, checkpoints, guardrails.
compose/ Reusable substrate wiring (multi-session factory, MCP
digest, builders) lifted out of cmd/core-agent.
instruction/ AGENTS.md / CLAUDE.md / GEMINI.md loader (AGENTS.d
overlay, @include, builtin: personas).
config/ .agents/config.json schema, discovery, atomic Save.
permissions/ ask/auto/acceptEdits/plan/allow/yolo gate, bash safety,
path scope, control-plane writes, plan-first.
approver/ Model-backed approver behind permission mode `auto`.
tools/ Built-in tool suite + GateToolset (permissions → ADK).
mcp/ skills/ mcp.json server lifecycle; SKILL.md discovery.
models/ Provider interface + Resolve; gemini/, anthropic/, and
profiles/ via go-steer/core-models. New providers and
adapter fixes go to core-models, not here.
pricing/ Re-export of core-models' price catalog (rates are fixed
upstream, then the pin is bumped).
usage/ modeltier/ taskclass/ Cost tracker; model-tier + task routing.
eventlog/ Durable sessions + audit log (SQLite/Postgres/MySQL).
transcript/ Transcript files on exit (pkg/session is its deprecated
alias).
attach/ attachadapter/ auth/ HTTP/SSE remote-attach server, the agent
bridge onto it, and its authentication.
childenv/ Keeps daemon credentials out of child processes.
digest/ watchdog/ hooks/ agentenv/ recording/ telemetry/
runner/ Headless (one-shot) + REPL drivers.
internal/ Attach client, core-tui remote adapter, web UI, evals,
self-recipe tests, image pins, version, testutil.
cmd/core-agent/ Reference CLI (in-process TUI by default).
cmd/core-agent-tui/ Standalone TUI (spawn-and-attach).
examples/ Library examples + recipes (examples/internal/smokeset
says which ones CI runs).
extras/scion/ Scion harness integration.
dev/ Build, test, lint, CI, UAT, release tooling; see
dev/README.md.
docs/ Internal design docs, assessments, UAT records.
docs/site/ Published Astro Starlight site.
.agents/ The self-development recipe and its skills (#1116).Build & test
dev/tools/ci # full local CI, fast-fail order
dev/tools/build # go build ./...
dev/tools/test-unit # go test -race -coverprofile, all packages
dev/tools/lint-go # golangci-lint (pinned version auto-installs)
dev/tools/fix-go-format # gofmt + goimportsThe default test run needs no network and no API keys. Provider-gated tests skip cleanly without credentials.
Conventions
- Design before non-trivial work. Anything bigger than a small fix gets a
docs/<feature>-design.mdwith a "Settled decisions (do not relitigate)" section and an explicit "Out of scope" list, registered indocs/README.md. Give it a status line, and update that line when the work ships or is abandoned. - License headers everywhere. Every Go, shell, YAML and Python source file
starts with the full Apache 2.0 boilerplate attributed to Google LLC.
goheaderindev/tools/lint-goenforces it for.gofiles. For other new files, rundev/tools/add-license-headers. - The complexity thresholds only go down.
funlenandgocognitindev/tools/.golangci.ymlare a ratchet pinned to the worst function in the tree. If your function trips it, split the function. Raising the number is never the fix, and neither is//nolint. The one exemption,cmd/core-agent'srun(#685), is named in the config. Don't grow it: reusable substrate goes inpkg/compose(seedocs/compose-extraction-design.md), and binary-only glue goes in a named helper incmd/core-agent, not inline inrun(). - Commits. Make them small and self-contained, with Conventional Commits
subjects (
feat:,fix:,docs:,chore:,refactor:,test:,ci:,build:). The body says why and what verification was done. Sign off with DCO (git commit -s); seeCONTRIBUTING.md. - No agent attribution of any kind. That means no Co-Authored-By agent
trailer, no "Generated with" footer, no agent as author or committer, and no
marker trailer. This applies to commits, PR titles and PR bodies. The
required
agent attributioncheck scans every branch commit, because a squash merge copies co-authors intomain. History: contributor-pitfalls § agent attribution. - Tests before merging. Every new package ships with unit tests. A feature without a test is not done. A bug fix needs a regression test that fails on the pre-fix code (see the adversarial review gate below).
- Errors flow to the user. Provider, tool and config failures never
panic. They surface as errors through the agent loop, or as
core-agent: ...lines on stderr. - Gate everything that consumes external state. MCP and skill tool calls
pass through
permissions.Gate, so the same permission semantics apply everywhere. Consumers wrap their own tools withtools.GateToolset. Tool descriptions and
jsonschema:tags are prompt text, not code comments. They carry system-prompt weight, they outrank the persona when the model picks a tool, and a recipe author can neither see nor override them. Review them as prose. Three rules (#905, #909):- A string argument is a writing prompt. Name what the text must contain (findings, evidence, the proposed change), never its genre ("summary", "status").
- Write one neutral string, not interactive and headless variants. Branch
only on what is TRUE about this build (
whenTool,gate.HasTool). When a variant only changes what is TYPICAL, delete the example instead. To remove a tool for a deployment, change its registration. - Never state frequency ("use this generously").
internal/testutil.ModelFacingBanssweeps every registered tool's description and arg schema. Add the next bad phrase there.TestEveryToolRegisteringPackageHasASweepfails any package that registers a tool without a sweep. It cannot tell whether your sweep covers the tools you register only conditionally; that part is on you (#919). Incidents: contributor-pitfalls § tool descriptions.
Pitfalls & gotchas (real ones we've hit)
t.Setenvandt.Parallel()don't mix. A test that callst.Setenvcannot callt.Parallel().- ADK streaming needs
agent.RunConfig{StreamingMode: agent.StreamingModeSSE}. The defaultStreamingModeNoneproduces noPartialevents. - ADK tool declarations live on
req.Config.Tools([]*genai.Tool).req.Toolsis unused by the providers. - Gemini function names must match
[A-Za-z0-9_]{1,64}. MCP tools are namespaced<server>_<tool>, never with a dot (pkg/mcp/namespace.go::sanitizePrefix). - The MCP SDK's
Toolset.Tools(ctx)needs anagent.ReadonlyContext. A stub is inpkg/mcp/listctx.go. - ADK's
telemetry.New(...)does not install OTEL globals. Callproviders.SetGlobalOtelProviders(), aspkg/telemetry/otel.godoes. - Vertex Claude model IDs sometimes need an
@VERSIONsuffix (e.g.claude-opus-4-5@20251101). Pass that form via--modelif the bare alias fails. - In
cmd/core-agent, anything wired beforerunner.Runtakes a getter, never a value read offagentRef.agentRefis nil at every wiring site, and nil forever on--multi-session. An eager read is a startup SIGSEGV even when the feature is off. Usefunc() *agent.Agent { return agentRef }. A per-session consumer needs a per-session getter. Read contributor-pitfalls § getters before touching wiring inmain.go. - To test that a shell script calls something, scan what the shell executes, not the file text. Assert any hand-rolled lexer against real bash, and decide which way it may err before you write it. Read contributor-pitfalls § shell scripts before writing or reusing a script-scanning check.
How we develop
There is one long-lived branch, main. Work happens on short-lived branches
(feat/…, fix/…, chore/…, docs/…), each with a PR against main.
- Worktrees for feature branches. Do feature and parallel-track work in a
dedicated git worktree under
.worktrees/<name>(git worktree add .worktrees/<name> -b <branch> origin/main) rather than switching branches in the root checkout..worktrees/is gitignored. - Merging. A PR merges when branch protection's required checks are green.
The live list:
gh api repos/go-steer/core-agent/branches/main/protection/required_status_checks --jq '.contexts[]'. On a docs-only PR,ci-docs.ymlemits no-op passes fortest,lint,go mod tidy is cleanandgovulncheck. The other required checks run for real. - Run presubmits before every push. Run everything in
dev/ci/presubmits/except the twoe2e-*scripts. They are the same scripts CI runs, so a green local sweep is the same green run. Glob the directory rather than trusting any hand-kept list. Skill:presubmit-sweep. - A harness script that runs the binary pins its config with
-c.config.Findwalks up from the cwd, so an unpinned run silently inherits the first.agents/above it.--agents-dirdoes not substitute. Usepristine_config/${SMOKE_CONFIG}fromdev/smoke/_common.sh. Enforced byverify-harness-config-pinned(#1116); seedev/README.md. - Every
main.gounderexamples/(outsideinternal/andtestdata/, at any depth) is either run by CI or excluded with a reason. Add a line toexamples/internal/smokeset, named by the directory's path relative toexamples/. A test there requires every example to be listed, andexamples-smokeruns the runnable ones and fails on a non-zero exit (#852, #1417). - Bumping the
core-tuipin is not just ago.modedit. Every exported interface ingithub.com/go-steer/core-tui/tuimust be implemented with avar _ coretui.X = (*T)(nil)guard, or declined with//coretui:declined Xplus a reason. This applies incmd/core-agent/coretui_guards.goand ininternal/coretuiremote/guards.go.verify-coretui-guardsenforces it; seedev/README.md. Adversarial review gate before every PR. Before
gh pr createon any change touching Go code:- Run a skeptic subagent over the staged diff, checking correctness, races and API misuse, verified against real dependency source rather than memory.
- Fix or pin every finding.
- Record the outcome in the PR body under
## Adversarial review.
For bug fixes, verify the new regression test fails on the pre-fix code by patching the behaviour back. Do not revert files or check out the parent: a compile error is not evidence about an assertion.
Skills:
adversarial-review-gateandprefix-failure-verification.The required
review-gatecheck verifies the## Adversarial reviewheading has a non-trivial body (and forfix:PRs, a record of pre-fix failure verification), but the substance of the review is still on you. Evidence that the gate pays: across the seven substantive PRs of the #537–#567 shutdown/resume train, it caught a P0/P1-class defect in nearly every first draft.- Rebase, don't merge. Keep feature branches rebased on
main.git push --force-with-leaseon your own branch is normal, but a precedinggit fetchdisarms the lease. Never force-pushmain. Stacked PRs.
- Retarget downstream PRs to
mainbefore merging the parent, because--delete-branchcloses them. - After each parent lands, rebase with
--onto. - After a base-update on a branch stacked on a release-promotion PR, read
git diff <base>...HEAD -- CHANGELOG.md. A duplicated version heading merges without conflict.
Skill:
stacked-pr-order. The hazard is explained in contributor-pitfalls § stacked PRs.- Retarget downstream PRs to
- Admin merge.
gh pr merge <N> --admin --squash --delete-branchis the maintainer path for the rebase cascade and for release commits. It is not a way to skip review on contributor PRs. On a Go PR, confirm that the realCIrun passed:ci-docs.yml's no-op jobs reuse the required check names. - "No checks reported" means the PR is DIRTY, not that CI is broken.
Check
gh pr view <N> --json mergeStateStatusfirst. See contributor-pitfalls § DIRTY. - Squash-merged branches are not ancestors of
main. Prove a branch merged with the PR'sheadRefOidbefore deleting it, and check for an open PR first. See contributor-pitfalls § branch in main. - UAT lives in two places. Hermetic, CI-runnable scripts go in
dev/smoke/NN-*.sh(mock providers, no credentials). Manual UAT against real backends goes indev/uat/<feature>/. All UAT state goes under/tmp, never$HOME. - The published site moves with the code. A user-visible change updates
docs/site/src/content/docs/in the same PR. Before adding or renaming a user-visible surface (tool, provider, image variant, CLI flag, release), rundev/tools/docs-lint. It hard-fails the drift that has bitten before: numeric tool counts, spelled-out image-variant counts, pinned@vX.Y.Zin install snippets, and prose version pins for the wrong major. [Unreleased]grows on every merged PR that ships a user-visible change. Add one bullet to## [Unreleased]inCHANGELOG.md, under#### Feature,#### Bug or Regression,#### Documentation,#### Other (Cleanup)or#### Security, capped at 300 characters and ending with a([#NNN](url))link. Breaking changes get a**BREAKING:**prefix under#### Changed. Cite the issue, not the PR. Enforced byverify-release-notes. Skill:changelog-bullet.Which doc changes are user-visible. Three kinds get a
#### Documentationbullet:- the published site under
docs/site/src/content/docs/; README.mdand the READMEs underexamples/;- a change to a rule in
AGENTS.md,CONTRIBUTING.md,docs/release-process.mdor a skill under.agents/.
Everything else under
docs/anddev/is internal and needs no bullet of its own. When an internal doc records a user-visible change, the bullet belongs to that change. History: contributor-pitfalls § user-visible docs.- the published site under
How we release
The full recipe is docs/release-process.md.
What a PR author needs:
- SemVer. A minor release for additive features, a patch for fixes only.
Breaking changes need a major bump, with a one-version deprecation period
when feasible. A major bump renames the module path to
/vNbefore anyvNtag, pre-releases included; a tag cut first can never bego installed (#327). - Never hand-carve the CHANGELOG.
dev/release/cut-dev-tag.shhandles dev, rc and pre-release tags.dev/release/cut-ga-tag.shfolds every pre-release section since the last GA into one cumulative entry, hoists BREAKING bullets, and leaves a<HEADLINE — ...>placeholder for you to replace. - What the scripts do. Both assume
[Unreleased]is current (backfill fromgit logif it isn't), run preflight guards such as pricing freshness, editCHANGELOG.md, and print the commands to finish. They do not commit, tag or push. - After a GA, bump
internal/version.Versiontov<next-minor>.0-dev(release-process step 4). Until that lands,verify-version-fallbackfails every PR. It was missed after v3.0.0 (#1351).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

