Expand description
Backfill a dedicated ABF bundle for every existing agent definition that
doesn’t have one — the data-migration half of
docs/specs/ARCHITECTURE_MANDATORY_ABF_RETHINK_2026_08_14.md §3/§7:
“every agent must have an ABF,” and, at the time, that ABF carried its
own provider/model (readonly once set by a bundle-level guard). Since
SPEC_AGENT_BUNDLE_FORMAT_V0_3_2026_10_05.md §3 the bundle carries no
harness: the agent owns its provider, locked by
app_api::check_provider_unchanged, and m0035 reconciles agents with
the bundles this migration wrote.
For every definition with an empty memory_id: create a fresh
db_bundles row (empty instructions/context — a legitimate starting
state, not an error; bundle.validate already treats an empty bundle as
valid) with the definition’s OWN provider (harness) carried onto the
bundle, and model defaulted from that provider’s first declared
supported_vendors entry (same derivation
Store::bundle_provision_for_new_agent uses for brand-new agents) —
and point the definition’s memory_id at it.
P0 fix (2026-08-15, ReAgent review on PR #2587): this used to hardcode
provider='claude', model='anthropic' for every backfilled agent
regardless of def.provider, reasoning from §7.3’s operator-confirmed
“every agent on this instance is Claude Code + OAuth Anthropic.” That’s
true today, but baking it into the migration is unsafe: combined with
the then bundle-level read-only lock and agent_open.rs’s
spawn-time bundle-provider preference, hardcoding here would silently
reassign any non-Claude agent (codex, gemini, …) — present now or
added before this migration runs on a given install — to spawn as
Claude afterward, permanently (readonly-once-set). Deriving from the
definition’s own already-known provider costs nothing extra and has
no such failure mode; falls back to "claude"/"anthropic" only when
def.provider is itself empty (a definition with no harness set at
all — degenerate case, but still needs some value to backfill to).
Local-channel only, deliberately — same pattern this migration’s own
module doc for agent_def_set_memory_id_if_empty documents: a
cross-channel (global-registry-only) definition can’t be reached today
because DefinitionRecordV1 doesn’t carry memory_id yet (same
accepted gap as model_vendor_base_url). The global registry is still
attached (mirrors m0020’s pattern) so this migration at least SEES
every agent and can log which ones it had to skip, rather than silently
processing only whatever happens to be in local SQLite.
The bundle itself, though, is NOT channel-local — it’s written into
the effective identity/memory store (shared store when resolvable,
same as AppState.id_store at runtime), never the channel’s own
objects.db. P1 fix (2026-08-15, Codex review on PR #2587): this used
to write bundles straight into mstore (the channel store this
migration already has open for the agent-definition side) — every real
bundle-read path (listmemories/getmemory/the Memory editor/the
bundle-summary panel) reads through id_store instead, so a
channel-local bundle would be silently invisible everywhere else, the
exact same bug the runtime provisioning-path fix
(agent_def_provision_and_bind_bundle) addresses for new agents.
Idempotent: re-running only ever touches definitions still at
memory_id=''; already-backfilled or already-bound rows are untouched
(both by the agent_def_list() filter here and by
agent_def_set_memory_id_if_empty’s own WHERE memory_id = '' guard).
Structs§
Constants§
- FALLBACK_
MODEL 🔒 - FALLBACK_
PROVIDER 🔒 - Fallback provider/model ONLY for the degenerate case of a definition
whose own
providercolumn is itself empty — see the module doc’s P0 fix note. Every normal backfill derives fromdef.providerinstead; this is not the common path.
Functions§
- resolve_
backfill_ 🔒provider_ and_ model - Same derivation
Store::bundle_provision_for_new_agent(backend/storage/agents/definition.rs) uses for brand-new agents — delegates toStore::resolve_effective_vendorso both paths agree on themodel_vendor_base_url→"custom"rule (P2 fix, Codex review on PR #2587) rather than maintaining two copies of the same logic. Falls back toFALLBACK_PROVIDER/FALLBACK_MODELwhenprovideris empty — the one caseresolve_effective_vendorcan’t help with, since it still needs a real provider id to look up. - resolve_
bundle_ 🔒store - Resolve the store bundles must be written into — mirrors
AppState.id_store’s own fallback (shared store when resolvable, else the channel store) so a migration-time bundle always lands exactly where the runtime RPC layer will look for it. Best-effort or degrade, never hard-fail: an unusable shared store means “not today,” same posture the global-registry attach above already has.