Skip to main content

Module m0021_backfill_agent_bundles

Module m0021_backfill_agent_bundles 

Source
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§

M0021BackfillAgentBundles

Constants§

FALLBACK_MODEL 🔒
FALLBACK_PROVIDER 🔒
Fallback provider/model ONLY for the degenerate case of a definition whose own provider column is itself empty — see the module doc’s P0 fix note. Every normal backfill derives from def.provider instead; 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 to Store::resolve_effective_vendor so both paths agree on the model_vendor_base_url → "custom" rule (P2 fix, Codex review on PR #2587) rather than maintaining two copies of the same logic. Falls back to FALLBACK_PROVIDER/FALLBACK_MODEL when provider is empty — the one case resolve_effective_vendor can’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.