Skip to content

Commit 70226c4

Browse files
committed
docs: add ADR and implementation plan for config-driven agents
Add ADR and implementation plan for declaring agents in config.yaml instead of compiling them into the binary. fullsend run resolves agents from config at runtime, loading harnesses directly from URLs or local paths — no intermediate wrapper files on disk. Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Greg Allen <gallen@redhat.com>
1 parent 08e7c62 commit 70226c4

2 files changed

Lines changed: 625 additions & 0 deletions

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
---
2+
title: "NNNN. Config-driven agents"
3+
status: Accepted
4+
relates_to:
5+
- agent-architecture
6+
- agent-infrastructure
7+
topics:
8+
- agents
9+
- per-repo
10+
- configuration
11+
- extensibility
12+
---
13+
14+
# NNNN. Config-driven agents
15+
16+
Date: 2026-06-29
17+
18+
## Status
19+
20+
Accepted
21+
22+
## Context
23+
24+
Which agents fullsend knows about is currently compiled into the binary.
25+
Scaffold-embedded harnesses (`internal/scaffold/fullsend-repo/harness/`)
26+
define the canonical agent set, and a hardcoded `externalAgents` map in
27+
`baseurl.go` extends it for agents extracted to standalone repos. `HarnessNames()` reads from these compiled-in sources to determine
28+
which agents are available.
29+
30+
This creates three problems:
31+
32+
1. **Adding an agent requires a code change.** A user who wants to run a
33+
custom agent must modify the fullsend binary or work around it.
34+
2. **First-party agents extracted to external repos need special
35+
handling.** The `externalAgents` map is a one-off mechanism that pins
36+
commit SHAs and content hashes in Go source.
37+
3. **First-party and third-party agents follow different paths.**
38+
Fullsend's own agents are discovered from the scaffold embed;
39+
external agents are discovered from a hardcoded map. There is no
40+
single, uniform mechanism.
41+
42+
The triage agent extraction to `fullsend-ai/agents`
43+
([ADR 0045](0045-forge-portable-harness-schema.md) Phase 4) is the
44+
first agent to move out of the scaffold. More will follow. The
45+
registration mechanism must support both first-party extractions and
46+
user-defined agents without code changes to fullsend.
47+
48+
## Decision
49+
50+
Make agent registration a **config-level concept** in both org config
51+
and per-repo config.
52+
53+
**Config schema.** Add an `agents` list to both `OrgConfig` and
54+
`PerRepoConfig`. Each entry is either a string shorthand or an object
55+
with an explicit name. The source can be a URL (for remote harnesses)
56+
or a local path (for harnesses on disk):
57+
58+
```yaml
59+
# Per-repo config (.fullsend/config.yaml in target repo)
60+
version: "1"
61+
agents:
62+
# String shorthand — name derived from filename
63+
- https://raw.githubusercontent.com/fullsend-ai/fullsend/<sha>/internal/scaffold/fullsend-repo/harness/code.yaml#sha256=<hash>
64+
- harness/my-custom-agent.yaml
65+
66+
# Object form — explicit name overrides filename
67+
- name: triage
68+
source: https://raw.githubusercontent.com/fullsend-ai/agents/<sha>/harness/triage.yaml#sha256=<hash>
69+
- name: lint
70+
source: harness/my-linter.yaml
71+
allowed_remote_resources:
72+
- https://raw.githubusercontent.com/fullsend-ai/fullsend/
73+
- https://raw.githubusercontent.com/fullsend-ai/agents/
74+
```
75+
76+
An entry's source is treated as a URL if it starts with `https://`,
77+
and as a local path otherwise. Local paths resolve relative to the
78+
`.fullsend/` directory. When `name` is omitted (string shorthand), the
79+
agent name is derived from the filename (`triage.yaml` -> `triage`).
80+
When `name` is provided, it overrides the filename-derived name. Role and slug are read from the harness content at runtime, not
81+
stored in config. URL entries
82+
include an integrity hash fragment for content verification
83+
([ADR 0038](0038-universal-harness-access.md)); local paths do not
84+
require one since the content is already trusted on disk.
85+
86+
**`allowed_remote_resources` in per-repo config.** This field currently
87+
exists only in org config. Per-repo config gains it to support base
88+
composition without an org config repo. Both are checked at load time;
89+
per-repo entries are additive.
90+
91+
**Install seeding.** During install, the default first-party agent
92+
URLs are populated into `agents` — in org config for org-mode
93+
installs (`fullsend admin install <org>`), in per-repo config for
94+
per-repo installs (`fullsend github` / `fullsend admin install
95+
<owner/repo>`). They use the same format as any user-added agent. The
96+
`--agents` flag continues to control which roles are installed; the
97+
mapping from role to default harness URL is a build-time constant (not
98+
a runtime concept).
99+
100+
**CLI surface.** A `fullsend agent` subcommand group manages the
101+
agents list in config:
102+
103+
| Command | Effect |
104+
|---------|--------|
105+
| `fullsend agent add <url-or-path>` | Fetch/read harness, validate, append to config |
106+
| `fullsend agent list` | List registered agents with name, role, source |
107+
| `fullsend agent update <name> [sha]` | Re-pin agent to latest HEAD or a specific commit |
108+
| `fullsend agent remove <name>` | Remove agent entry from config |
109+
110+
`agent add` accepts a URL or a local path and validates the harness
111+
before appending it to config. `agent update` re-pins a URL-based
112+
agent to a new commit (latest HEAD or a specific SHA). Both commands
113+
automatically resolve unpinned URLs to fully pinned form — commit SHA
114+
and integrity hash are resolved and computed by the CLI so users never
115+
need to construct pinned URLs manually. The stored config entry is
116+
always fully pinned.
117+
118+
**Additive merge model.** Config entries are merged with
119+
scaffold-discovered agents, not a replacement. Scaffold agents form
120+
the base set; config entries add new agents or override scaffold
121+
entries with the same name. The merge rule: config wins over scaffold
122+
when names collide. This enables gradual migration — agents can be
123+
extracted one at a time without requiring every config to list all
124+
agents.
125+
126+
`fullsend agent list` shows the merged view (scaffold + config) so
127+
the full agent set is visible in one place.
128+
129+
**Runtime resolution.** `fullsend run <name>` looks up the agent by
130+
name in the merged agent set. For URL entries, it loads the harness
131+
directly from the URL — no intermediate wrapper file is generated on
132+
disk. For local path entries, the harness is loaded directly from the
133+
path. Role and slug come from the harness content itself, so no
134+
wrapper is needed to inject identity fields.
135+
136+
**Removal of compiled-in agent map.** The `externalAgents` map,
137+
`IsExternalAgent()`, and the external-agent branches in
138+
`HarnessBaseURL()`/`HarnessContentHash()` are deleted.
139+
`HarnessNames()` returns only scaffold-embedded names (used as the
140+
base set for the additive merge).
141+
142+
**Transition to authoritative config.** The additive model is a
143+
migration aid. Once all first-party agents have been extracted from
144+
the scaffold and all installations have populated `agents` in config,
145+
a follow-up change makes config authoritative: scaffold agents are no
146+
longer discovered, and `agents` is the complete list. An issue will
147+
be filed to track this transition once the last agent is extracted.
148+
149+
## Consequences
150+
151+
- **Extensibility.** Anyone can add an agent to the fullsend installation in their repo with
152+
`fullsend agent add <url-or-path>` — no code change, no PR to
153+
fullsend. Local paths support development and private harnesses.
154+
- **Uniform path.** First-party and third-party agents are registered
155+
the same way. Extracting an agent to a standalone repo is a config
156+
update, not a code change.
157+
- **Gradual migration.** The additive merge model means agents can be
158+
extracted from the scaffold one at a time. Users only need config
159+
entries for agents that have moved or been added — unchanged
160+
scaffold agents continue to work. Once all agents are extracted,
161+
config becomes authoritative and the scaffold fallback is removed.
162+
- **Org config dependency removed for per-repo installs.**
163+
`allowed_remote_resources` in per-repo config eliminates the need
164+
to read org config for base composition validation.
165+
- **Migration.** No forced migration. Existing installations continue
166+
to work without changes:
167+
- When `agents` is empty in config, the code falls back to the
168+
current scaffold-based agent discovery. This fallback is
169+
permanent until the config is populated — there is no deadline.
170+
- Per-repo configs that lack `allowed_remote_resources` inherit
171+
the org-level allowlist (if present) or use the scaffold
172+
fallback path, which does not require an allowlist.
173+
- To opt in, users can run `fullsend agent add` to populate the
174+
list manually, or the config is backfilled automatically the
175+
next time they run `fullsend admin install --upgrade`,
176+
`fullsend github` (re-install), or `fullsend repos sync`
177+
([ADR 0057](0057-repos-management.md)).
178+
- A deprecation notice is logged when the fallback is used, to
179+
nudge users toward populating the config.
180+
181+
## References
182+
183+
- [ADR 0033](0033-per-repo-installation-mode.md) -- per-repo installation mode
184+
- [ADR 0038](0038-universal-harness-access.md) -- URL-based resource references and integrity hashes
185+
- [ADR 0045](0045-forge-portable-harness-schema.md) -- harness composition via `base:` URLs
186+
- [ADR 0057](0057-repos-management.md) -- repos management for per-repo installations
187+
- [Implementation plan](../plans/config-driven-agents.md)

0 commit comments

Comments
 (0)