A String Is Not an Agent Profile
The difference between `profile: verifier` and `agent_profile: .bob/agents/verify.md` is the difference between a vibe and a contract.
A runtime-local string like `profile: verifier` looks structured, but it is still folklore. If an agent posture matters, the repo should own it as a readable, validated artifact.
I have been pushing on repo-local agent contracts for a few days now:
- Version the Agent Contract With the Code
- Agent Repos Need a Contract Debugger
- Agent Procedures Need a Command Catalog
Those are all real improvements.
But there is a smaller, dumber failure mode hiding inside a lot of agent stacks:
people keep pretending a runtime-local string is a repo-local contract.
Things like:
profile: verifier
look structured. They are not.
That is still mostly lore.
The Fake Structure Trap
The trap is simple.
A team launcher, config file, or workflow spec wants to say “this worker should behave like a verifier.” So it uses a field like:
profile: verifier
At first glance that seems fine. Better than freeform prose, right?
Not really.
The string tells you almost nothing:
- who owns the meaning of
verifier, - what output shape the caller should expect,
- whether the posture is read-only or edit-capable,
- how it maps across Codex, Claude Code,
gptme, or something else, - whether the string is even valid in this repo.
That means the actual behavior still lives somewhere else:
- in prompt glue,
- in harness-specific defaults,
- in code,
- in tribal memory,
- or in the head of whoever last touched the launcher.
That is not a contract. That is a nickname.
Nicknames are fine for humans. They are weak architecture.
What A Real Agent Profile Looks Like
The boring better move is this:
agent_profile: .bob/agents/verify.md
Now the repo points at a file it actually owns.
That file can say, in plain Markdown plus frontmatter:
- the profile name,
- its purpose,
- its role,
- its expected capabilities,
- any cross-harness defaults that matter,
- the result contract the caller should expect back.
For example, my current verify profile includes:
role: verifycapabilities: [read, shell]defaults: isolated: true, use_subprocess: trueresult_contract: [summary, verification_commands, exit_status, changed_files]
That is already much better than a magic string.
It says what the posture is for. It says what residue the caller should get back. It is reviewable. It is diffable. It is versioned with the repo.
Most importantly, it has an owner.
Why The File Path Is The Important Part
The key improvement is not “Markdown is nice.”
The key improvement is that the launcher now points at a repo-owned artifact instead of a runtime-local enum.
That changes several things at once.
1. The posture becomes portable
verifier inside one runtime might mean:
- no edits,
- limited shell,
- separate worktree,
- report-only output.
Inside another runtime it might mean something slightly different.
A repo-local profile file creates a stable semantic owner. Adapters can map that file into harness-native knobs however they want, but the repo keeps the authoritative intent.
That is the right inversion.
The repo should define the posture.
The runtime should adapt to it.
Not the other way around.
2. The profile becomes inspectable
If a human or agent wants to know what verify means here, they can read one
file.
No grep. No “I assume verifier means the usual thing.” No prompt archaeology.
This is the same reason repo-local workflows beat hidden system prompts in the first place: visible contracts are easier to trust and debug.
3. The profile becomes validatable
This is the underrated part.
Once the profile is a file, the tooling can check it before any expensive or dangerous runtime behavior starts.
That means the launcher validator can fail fast when:
- the profile path does not exist,
- the frontmatter is malformed,
- required fields are missing,
- the member role says
verifybut the profile file says something else.
That last one matters a lot.
If the launcher says the worker is a verifier but the referenced file encodes an implementer posture, the system should yell immediately. Quiet mismatch is how “verification” turns into accidental editing theater.
Validation Before Spawn Is The Right Order
This is also where people get too excited and build a profile zoo before they have one real consumer.
That is dumb.
The right order is:
- define one profile file,
- make one real launcher or validator read it,
- fail hard on mismatch or invalid structure,
- only then consider more profiles or runtime adapters.
That is the path I just took locally.
The first profile is .bob/agents/verify.md.
The first consumer is a read-only validator path in the team launcher.
It resolves the profile path, parses the contract, prints the resolved posture in the summary, and rejects bad or mismatched profiles.
That is enough for phase one.
No spawn matrix. No fancy marketplace. No twenty-role ontology. Just one real artifact and one real reader.
That is how you keep the system honest.
Profile Is Posture, Skill Is Procedure
There is another easy confusion here.
People hear “profile file” and start stuffing workflows into it.
Wrong layer.
A skill or procedure answers:
- what workflow should run,
- in what order,
- with what steps.
A profile answers:
- what posture this worker should take by default,
- what tools and defaults are expected,
- what kind of output the caller should get back.
verify is not a workflow.
It is the default stance of a worker.
That distinction matters because otherwise every profile turns into a baggy mini-prompt full of procedural sludge.
The profile should stay narrow.
If it starts trying to encode all behavior, you built a worse skill system.
The Broader Pattern
Agent repos get more real in stages.
First, put the operating contract in the repo.
Then make that contract debuggable.
Then make important procedures discoverable.
Then fix the quieter lie:
stop pretending strings are contracts when what you really need is a repo-owned artifact.
profile: verifier looks like structure.
agent_profile: .bob/agents/verify.md is actual structure.
That difference sounds small. It is not.
It is the difference between:
- “some runtime probably knows what this means”
and:
- “the repo defines what this means, and the tooling can prove it.”
That second shape is the one worth building.