Skip to content

Sub-Agent Lifecycle (dev → test → promote → rollback)

PF1: Sub-Agent Lifecycle (dev → test → promote → rollback)

Section titled “PF1: Sub-Agent Lifecycle (dev → test → promote → rollback)”

The consumer-agent runs multiple sub-agents (Scout / Forethought, Shopping, Earnings Coach when it lands, future Play / eReceipts / Rewards / Restaurant verticals). Each sub-agent is a factory-registered Python module conforming to PC1’s Agent Definition contract. Today: a new sub-agent appears in agent_config.yaml, gets imported by factory.py, and goes live on the next deploy. Removing one is symmetric — delete the entry, redeploy. No staged ramp, no formal promotion criteria, no codified decommission cleanup path.

That works at the current vertical count (Shop + Support/Scout live, others in flight) but doesn’t scale. As Play / eReceipts / Rewards / Restaurant onboard, the “appears in config + deploy” approach hits three failure modes:

  1. No promotion discipline. A new sub-agent’s first production traffic is also its widest blast radius — 100% of routed users see it. Bad sub-agents are caught by user reports, not eval gates.
  2. Rollback requires redeploy. Reverting a bad sub-agent change means a config change + image build + ECS rollout. Slow when the bug is user-facing.
  3. Decommission is undefined. When a sub-agent is permanently removed from the codebase (e.g., a sub-agent is replaced by another, or its capability is folded into a different sub-agent), the old sub-agent’s eval suite, dataset entries, prompt blocks, and tool dependencies leave residue. No documented cleanup contract.

PF1 closes these gaps. Three load-bearing commitments:

  1. A sub-agent moves through an explicit lifecycle state machine. dev → test → promote → rollback. Each transition has a clear gate (PC5 owns the gate semantics) or a clear operator action. Operational sub-states inside promote (ramp percentage progression) are recorded in the PC6 experiment config, not as separate lifecycle states.

  2. Promotion = Agent Definition becomes active in the registry. No per-agent deploy targets, no separate runtimes. PC1 §5.2’s agent_config.yaml::agents registry IS the activation surface; PF1 commits the staged path to landing there.

  3. Rollback is config-revert + factory refresh. Same pattern as PC5’s rule rollback (PC5 §5.7) and PC6’s prompt-version rollback (PC6 §5.10). No consumer-agent redeploy required.

Without PF1:

  • PC5’s gates have no lifecycle to gate. PC5 §5.9’s PF1-partnership table references the lifecycle states; without PF1, PC5’s three milestones are gates against an undefined backdrop.
  • Cohort-gated sub-agent rollout has no spec home. Today, cohort gating happens for prompts (PC6) and flags (PF8) but not for whole sub-agents. PF1 owns the sub-agent rollout cohort discipline.
  • Vertical onboarding stalls at “now what?” A vertical drafts an Agent Definition (PC1’s contract); without PF1, they don’t know how to move it through staged production exposure.

Companion: Platform Spec Lab row PF1. The spec is the source of truth.

Per the Platform Spec Lab, PF1 owns the Sub-Agent Lifecycle (dev → test → promote → rollback) capability for the AI Assistant Platform’s consumer-agent runtime. The capability has four components:

  • Lifecycle state machine — the explicit states a sub-agent moves through.
  • Promotion semantics — what activates a sub-agent in the registry.
  • Rollback semantics — how a bad sub-agent change reverts.
  • Decommission cleanup — how a sub-agent’s artifacts are cleanly removed after rollback.

PF1 is scoped to Sub-Agent Lifecycle — the lifecycle is inside the consumer-agent process; sub-agents are factory-registered Python modules, not deployable units of their own. PF1 does not adopt TEAM/FAM-style per-agent deploy targets.

PF1 partners with:

  • PC1 (Agent Composition) — owns the Agent Definition contract sub-agents conform to. PF1 commits the staged path to becoming active in the registry.
  • PC5 (Agent CI/CD Pipeline) — owns the three gate milestones (pre_merge, pre_ramp, pre_full). PF1 references gates by milestone name; PC5 §5.9 commits the partnership table.
  • PF8 (Feature Flag & Cross-Vertical Observability Conventions) — owns flag naming (ai_assistant_*), kill-switch lifecycle, required Grafana panels. PF1 reuses for sub-agent cohort gating.
  • PF5 (Vertical Scaffolding + Validation Tools) — scaffolds the artifacts a new sub-agent ships with. PF1 commits the state shape PF5 scaffolds toward.

Sub-agent registration (production):

consumer-agent/agent_config.yaml::agents — declarative registry of Agent Definitions. Each entry:

- id: shop
description: Handles shopping queries, product discovery, offers
role: native
model: gpt-5.4-mini-low
tools: [search_offers, search_products, ...]
prompt_blocks: [persona-shop, instructions-shop, safety-base]
sub_agents: []
tuning: {reasoning_effort: low}

consumer-agent/src/consumer_agent/factory.py::create_gateway_agent_from_config — reads the registry, resolves the Agent Definition’s references (model from registry, tools from registry, prompt_blocks from registry, sub_agents from registry), constructs a LangGraph CompiledStateGraph.

The orchestrator’s sub_agents list determines what’s callable at the dispatch layer. Adding a sub-agent = adding a new entry to agents + listing it in the orchestrator’s sub_agents. Removing = the reverse.

Cohort-gated behavior (production):

consumer-agent/src/consumer_agent/utils/feature_flags.py — Feature Flipper integration. Used today for cohort-style behavior gating (e.g., consumer_agent_xml_prompt, consumer_agent_sub_agents_as_tools). Naming convention ai_assistant_* per PLT-552; codified by PF8.

What’s NOT yet codified:

  • A lifecycle state machine for sub-agents — sub-agents either exist in config or don’t
  • Promotion criteria — there’s no formal “this sub-agent passes the bar for production traffic”
  • Staged rollout for new sub-agents — first production traffic is also the widest blast radius
  • Rollback path without redeploy — config changes require image build + ECS rollout
  • Retirement — no documented cleanup path when a sub-agent is replaced

PF1 inherits from PC1 (Agent Composition):

  • Agent Definition shape (PC1 §5.2) — the declarative contract a sub-agent conforms to. PF1 doesn’t redefine; commits the lifecycle path to activation.
  • Factory-time binding (PC1 §5.3, PC1 §3.3) — sub-agents resolve at factory invocation. PF1 commits the staged-rollout discipline that determines which factory invocation gets which sub-agent.
  • Vertical integration model (PC1 §5.9) — verticals contribute Agent Definitions, not runtime classes. PF1 commits the lifecycle each Agent Definition moves through.

PC5 §5.9 commits the partnership table. PF1 mirrors it from the lifecycle side:

PF1 lifecycle transition or sub-statePC5 gate(s)Trigger
dev → testNone (developer-driven)Developer ready for review
test → promote (PR merge + operator initiates ramp)pre_merge (PR-driven, dataset-scoped)PR merge
Ramp progression inside promote — first non-zero steppre_ramp (PC6-driven, trace-sampled)Operator advances ramp percentage from 0 to first non-zero step
Ramp progression inside promote — 100% steppre_full (PC6-driven, larger trace sample)Operator advances ramp percentage to 100%
* → rollbackNoneOperator action OR kill-switch fires from any active state

PC5’s three gates fire at distinct points in the PF1 lifecycle: pre_merge at the test → promote transition (PR merge), pre_ramp at the first non-zero ramp step inside promote, pre_full at the 100% ramp step inside promote. Operational sub-states inside promote (initial ramp percentage, intermediate ramp percentages, fully-rolled-out at 100%) are recorded in the PC6 experiment config rather than as distinct lifecycle states. Gate failure halts ramp progression but does not move the lifecycle out of promote — only operator action or kill-switch fires moves the lifecycle to rollback.

TermMeaning
Sub-agentA factory-registered Python module conforming to PC1’s Agent Definition contract. Examples: Scout, Shopping, Earnings Coach.
Agent DefinitionThe declarative bundle (id, description, role, model, tools, prompt_blocks, sub_agents, tuning) PC1 commits. Source of truth for a sub-agent’s behavior.
Registryconsumer-agent/agent_config.yaml::agents. Where Agent Definitions become active.
Lifecycle stateOne of: dev, test, promote, rollback. A derived value — computed from (registry presence, gating-flag state, rollout config), never stored as a field on the Agent Definition card (§5.1).
PromotionThe transition from test to promote after pre_merge passes. The sub-agent’s Agent Definition becomes eligible for production traffic, gated at dispatch by a Feature Flipper cohort flag (declared in the flag/rollout config, not on the card) whose ramp percentage the operator advances over time.
Ramp progressionThe operator-driven progression of the cohort flag’s percentage inside promote (e.g., first non-zero step → intermediate steps → 100%). Recorded in the PC6 experiment config; not a lifecycle state change. pre_ramp and pre_full PC5 gates fire as percentage advances.
RollbackThe transition from any active state (test, promote) to inactive. Same path as PC5/PC6 rollback: config revert + factory refresh OR kill-switch fires, no redeploy. Sub-agents leaving production reach rollback. rollback is the terminal state for that Agent Definition; fix-forward requires authoring a new Agent Definition (see FR-12). PF1 has no separate retired terminal state.
Decommission cleanupOperational cleanup of artifacts (Python module deletion, eval-suite tagging, prompt-block cleanup, tool dependency cleanup) for a sub-agent that has been rolled back and is being retired from the codebase. Cleanup runs after the lifecycle state reaches rollback; the cleanup itself is not a lifecycle state. See §5.6.
Kill-switchA Feature Flipper flag (per PF8 naming) that, when off, drops the sub-agent from the orchestrator’s dispatch surface at next factory invocation. Firing the kill-switch is one of the triggers that moves a sub-agent to rollback.

FR-1 — Lifecycle state machine. Every sub-agent MUST move through the four explicit states declared in §3.4 (dev, test, promote, rollback). Forward transitions are unidirectional (dev → test → promote); rollback is reachable from any active state.

FR-2 — Promotion semantics. Promotion = the sub-agent’s entry in agent_config.yaml::agents becomes active, gated at dispatch by a Feature Flipper cohort flag. The entry MAY exist in the registry while not dispatchable, because the runtime consults the gating flag at factory invocation and the flag is off (or below 100%). The flag→sub-agent binding lives in the flag/rollout config, not as a field on the Agent Definition card. Activation = the flag flips on for the configured cohort at some ramp percentage; the operator advances ramp progression over time inside promote.

FR-3 — Cohort-gated rollout for new sub-agents. A new sub-agent’s transition from test to promote MUST be cohort-gated via a Feature Flipper flag (per PF8 naming). The cohort definition follows PC1 §5.5 cohort discipline (Agent Definition version + registry versions + feature flags + experiment-arm membership per PC6 §5.7). The initial activation MUST start below 100% of eligible traffic; the operator advances ramp percentage over time, gated by PC5’s pre_ramp and pre_full checks.

FR-4 — Gates inside promote. The pre_merge gate from PC5 §5.5 fires at the test → promote transition. The pre_ramp and pre_full gates fire as the operator advances the cohort flag’s ramp percentage inside promote. Gate failure halts ramp progression at the current percentage; operator action is required to proceed or to move the sub-agent to rollback.

FR-5 — Rollback without redeploy. Rolling back a sub-agent change MUST be achievable by reverting the agent_config.yaml change (or flipping the cohort flag off) AND triggering a factory refresh. No consumer-agent image build or ECS redeploy required. Matches PC5 §5.7 and PC6 §5.10 rollback patterns.

FR-6 — Decommission cleanup contract. When a sub-agent reaches rollback and is being retired from the codebase (not just paused for investigation), cleanup MUST follow a documented path (§5.6):

  • The Agent Definition is removed from agent_config.yaml::agents.
  • The sub-agent’s Python module is deleted from the codebase (or left in place with the cohort flag off for a transition window; the pending deletion is tracked on the decommission ticket).
  • Eval-suite entries (datasets, judge configs in PC5’s manifest) tagged for the decommissioned sub-agent are removed OR explicitly retained for replay (operator decision).
  • Prompt blocks unique to the decommissioned sub-agent are deleted (per PC1 §5.7 file-backed registry pattern).
  • Tool dependencies (PS2 connectors) used only by the decommissioned sub-agent are deleted OR explicitly retained (operator decision).

Decommission cleanup is operational work that follows the lifecycle reaching rollback; it is not itself a lifecycle state.

FR-7 — Rollback target tracking. Every promotion MUST record the previous stable state of the sub-agent (the Agent Definition’s prior git commit). Rollback resolves to this target. Matches PC6 §5.6 rollout config pattern.

FR-8 — In-flight conversation tolerance. A rollback or kill-switch event MUST NOT corrupt in-flight conversations. Sub-agents resolve at factory invocation per PC1 §5.5; in-flight turns complete on the sub-agent they started with. New turns get the rolled-back state.

FR-9 — Kill-switch contract. Every cohort-gated sub-agent rollout MUST have a Feature Flipper kill-switch flag created at the test → promote transition. The kill-switch operates independently of cohort assignment; firing it drops the sub-agent from the orchestrator’s dispatch surface entirely at next factory invocation and moves the lifecycle to rollback.

FR-10 — Lifecycle audit trail. Every state transition MUST be auditable: a record capturing sub-agent ID, source state, target state, trigger (operator / kill-switch), Agent Definition commit, and cohort definition, retained for post-hoc analysis. This record is carried by PC6’s rollout-config audit, not emitted separately by PF1: a transition IS a rollout-config change, so it is recorded where that change is made (PC6’s rollout audit, landing in PS5’s store), alongside the prompt-rollout events PC6 already records. PF1 contributes the transition field set; it does not own a parallel audit stream.

Why not the agent runtime: a state transition is an operator rollout-config change, and the agent runtime only ever reads the current derived state (§ design principle 2) — it never observes the moment of transition, so it has no emission point for this record. The agent runtime ships only the two gate-dispatch signals it does observe (§10.2): a gate denial and a kill-switch firing. Ramp-percentage changes inside promote were already PC6 rollout-config events, not PF1 events; this folds the remaining dev→test→promote→rollback transitions into that same PC6 record so there is one audit home, not two. Tracked as a PC6-implementation line item (the PC6 rollout audit must carry the PF1 transition fields).

FR-11 — Cross-sub-agent dependency tracking. If sub-agent A’s Agent Definition includes sub-agent B in its sub_agents list (PC1 §5.2 — the orchestrator pattern), then retiring sub-agent B MUST fail until sub-agent A’s reference is removed. CI enforces this at PR time.

FR-12 — Re-baselining on Agent Definition major changes. A major Agent Definition change (model swap, prompt block additions/removals, tool list changes) MUST be authored as a NEW Agent Definition (new identity/commit), which enters the lifecycle at dev. The new Agent Definition then progresses through the full lifecycle — re-passing pre_merge at the test → promote transition, then pre_ramp and pre_full as ramp percentage advances inside promote. The prior Agent Definition remains in rollback as the rollback target. Modifying an Agent Definition in-place while it is in promote or rollback is not permitted. Minor changes (typo fixes, tuning adjustments) MAY be applied in-place with operator override.

NFR-1 — State transition latency. Lifecycle state transitions (excluding gate evaluation latency, which is PC5’s NFR) MUST complete in under 1 minute. State is config + flag; transition is operator action + factory refresh.

NFR-2 — Rollback latency. Full rollback path (revert + factory refresh + dispatch-surface update) MUST complete in under 5 minutes. Matches PC6 NFR-3 cancellation cadence; production users see no torn state during the window.

NFR-3 — Audit trail retention. Lifecycle transition records MUST be retained for at least 90 days for post-hoc analysis. Carried by PC6’s rollout audit (per FR-10), so retention is PC6 NFR-4’s rollout-audit retention; storage backend is PS5’s trace + event store.

NFR-4 — Cohort-gating overhead. The Feature Flipper cohort check at factory invocation MUST add negligible latency — sub-millisecond per check, dict-lookup against pre-fetched flag state. Cache invalidation on flag change is operational.

NFR-5 — Decommission cleanup window. Decommission cleanup (FR-6) MUST complete within one quarter from initiation. The decommissioned sub-agent’s agent_config.yaml entry MAY remain (cohort flag held off, so its derived state is rollback) during the window for replay support — no deprecated marker is added to the card; the pending cleanup is tracked on the decommission ticket.

AC-1 — Given a new sub-agent’s agent_config.yaml entry added and its cohort flag (ai_assistant_subagent_<id>, owned in the flag/rollout config, not on the card) set off, when the runtime starts, the sub-agent MUST NOT appear in the orchestrator’s dispatch surface. Existing sub-agents MUST be unaffected.

AC-2 — Given a sub-agent at test state with pre_merge gate passed, when an operator initiates the transition to promote via PC6-style rollout config with rollout_mode: experiment, the sub-agent MUST become dispatchable for users in the treatment cohort only. Control users MUST continue without the sub-agent.

AC-3 — Given a sub-agent in promote at an intermediate ramp percentage and the pre_ramp gate fails on the next advancement attempt, the ramp progression MUST halt — no further percentage increase. The sub-agent remains accessible to whatever cohort percentage was active at the moment of failure; operator action is required to proceed or to move to rollback.

AC-4 — Given a kill-switch fires (Feature Flipper flag off) during a sub-agent rollout, when the next factory invocation runs, the sub-agent MUST NOT appear in the orchestrator’s dispatch surface for any user. In-flight conversations complete on the sub-agent they started with; new turns route to the previous stable state. The lifecycle moves to rollback.

AC-5 — Given a sub-agent in promote at 100% ramp and an operator initiates rollback, when the rollback completes within NFR-2’s 5-minute budget, the sub-agent’s prior stable Agent Definition (per FR-7’s recorded target) becomes the active version. No consumer-agent redeploy MUST be required. The lifecycle moves to rollback.

AC-6 — Given sub-agent A’s sub_agents list references sub-agent B, when a PR attempts to decommission sub-agent B without removing A’s reference, CI MUST fail with a clear error per FR-11.

AC-7 — Given a sub-agent has reached rollback and is being decommissioned, its Agent Definition entry MAY remain in agent_config.yaml with its cohort flag held off for up to one quarter (per NFR-5) so its eval suite can replay. This needs no status/deprecated marker on the card: “being decommissioned” is not a distinct lifecycle state — the runtime sees only the derived rollback (present in registry, flag off, no traffic). The quarter-long artifact cleanup (FR-6’s five-step contract) is an operational runbook tracked on the decommission ticket, not a config primitive. After the window, the entry, the sub-agent’s Python module, decommissioned prompt blocks, and unused tools MUST be removed per FR-6’s cleanup contract.

AC-8 — Given any lifecycle state transition, the transition record (sub-agent ID, source/target state, trigger, Agent Definition commit, cohort definition) MUST be present in PC6’s rollout audit and retrievable from PS5’s trace + event store. PF1 does not emit a separate event; the fields are carried on the PC6 rollout-config record that the transition produces.

AC-9 — Given a major change to a deployed sub-agent’s Agent Definition (e.g., model swap from gpt-5.4-mini-low to gpt-5.4-nano), when a PR opens with the change, CI MUST require that the change be authored as a NEW Agent Definition entering the lifecycle at dev, with the prior Agent Definition remaining in rollback as the rollback target. The new Agent Definition re-passes pre_merge at the test → promote transition, then pre_ramp and pre_full as ramp percentage advances inside promote. Minor changes MAY be applied in-place with explicit operator override annotation.

AC-10 — Given a sub-agent change PR, the factory-time cohort check MUST complete in sub-millisecond per NFR-4. Per-request overhead from PF1’s cohort-gating MUST be negligible against turn latency.

Sub-agents are factory-registered Python modules; the lifecycle is in-process; promotion = registry activation gated by Feature Flipper + PC5’s three gates; rollback is config-revert + factory refresh.

Three properties hold across every PF1 contract:

  1. In-process, not deployable. Sub-agents are Python modules instantiated by factory.py. The lifecycle controls which Agent Definitions are active in the registry, not which deploy targets are running. No K8s pools, no separate processes, no per-agent runtimes.

  2. State is derived, never stored. A sub-agent’s lifecycle state is not a field on its Agent Definition card — it is a pure function of three external sources: (a) its presence in agent_config.yaml::agents, (b) the state of the cohort flag that gates it, (c) the rollout config’s rollout_mode (cohort vs full). Reading those three sources tells you the state; mutating them is the only way to transition. The card stays purely definitional (model, tools, prompt_blocks, sub_agents, tuning) so there is no second source of truth that can drift from the flag/rollout reality. The flag→sub-agent association lives in the flag/rollout config, not on the card — one flag MAY gate a bundle of changes beyond a single sub-agent, so the binding is many-to-many and belongs to the runtime, not the definition.

  3. All transitions are gated or operator-initiated. Automatic state advancement (e.g., “after 24h with no errors, advance”) is explicitly out of scope. Operator action (or PC5’s gate verdict) drives every transition.

PF1’s lifecycle has four states per PLT-690: dev, test, promote, rollback. Operational sub-states inside promote (initial ramp percentage, intermediate ramp percentages, fully-rolled-out at 100%) are recorded in the PC6 experiment config, not as distinct lifecycle states.

┌───────────────┐
│ dev │
└──────┬────────┘
│ developer-driven
┌───────────────┐
│ test │ (PR opens)
└──────┬────────┘
│ pre_merge gate (PC5)
│ — PASS; operator initiates ramp
┌───────────────┐
│ promote │ (cohort flag on, ramp percentage advanced over time)
│ │ pre_ramp gate fires at first non-zero step
│ │ pre_full gate fires at 100% advancement
└──────┬────────┘
│ operator action OR
│ kill-switch flips off
┌───────────────┐
│ rollback │
└───────────────┘

Note: rollback is reachable from test (kill-switch trigger during gate failures) and from promote (operator action or auto-ramp-down per PC5 §5.7); see §5.4 transition rules.

Transition rules:

  • dev → test: developer-driven. No platform gate. PR opens.
  • test → promote: PC5 pre_merge gate fires (PC5 §5.5). Pass → PR mergeable AND operator initiates ramp via PC6-style rollout config; the cohort flag activates at the first non-zero step. Inside promote, the operator advances ramp percentage over time; PC5’s pre_ramp gate fires at the first non-zero step and pre_full fires at 100% advancement. Gate failure halts ramp progression at the current percentage but does not move the lifecycle out of promote.
  • * → rollback (from test or promote): Operator action OR kill-switch flag flips off. Same path: factory refresh on next invocation drops the sub-agent (or reverts to prior Agent Definition commit per FR-7). rollback is terminal for that Agent Definition — there is no rollback → dev or rollback → test edge. Fix-forward requires authoring a new Agent Definition (see FR-12), which enters the lifecycle at dev. Decommission cleanup per FR-6 runs after the lifecycle reaches rollback if the sub-agent is being permanently removed from the codebase (rather than paused for investigation).

Promotion is a config change activating the sub-agent in the registry, not a deploy. Concretely:

  1. The sub-agent’s Agent Definition entry exists in agent_config.yaml::agents. Its server-side gate flag (the flags.on(..., identity=user_id) check detailed in §5.5) is off — the sub-agent is registered but not dispatchable. The flag lives in the flag/rollout config, not on the card.
  2. The orchestrator’s sub_agents list includes the sub-agent’s id (PC1 §5.3 orchestrator pattern).
  3. Operator initiates the test → promote transition via a rollout config (matches PC6 §5.6 schema, but for sub-agent activation instead of prompt-version selection).
  4. PC5 pre_ramp gate fires at the first non-zero ramp percentage; pre_full fires when the operator advances to 100%.
  5. Pass → Feature Flipper flag activates for the configured cohort at the declared ramp percentage.
  6. Next factory invocation reads the flag state → sub-agent becomes dispatchable for treatment-cohort users only at the current ramp percentage.

No deploy, no image build, no ECS rollout. The sub-agent’s Python module is in the deployed image already (from when it landed in the codebase); promotion is purely configuration.

Two trigger paths:

Operator-initiated rollback:

  1. Operator identifies a regression in the currently-active Agent Definition (production traffic + judge scores + manual review).
  2. Operator selects a rollback target — a prior Agent Definition (<prior-commit-id>) that is still in promote from its earlier active period (per FR-12, no Agent Definition that has reached rollback can be re-activated; the rollback target’s lifecycle state is promote, just not currently active).
  3. Operator updates the rollout config to set the active commit to the rollback target. The current Agent Definition transitions to rollback (terminal). The rollback target’s promote state is preserved unchanged — no state transition is required for the rollback target itself.
  4. Factory refresh — next invocation reads the updated rollout config and resolves the Agent Definition at the rollback target commit.
  5. NFR-2 commits 5-minute end-to-end.

Kill-switch-initiated rollback:

  1. Operator flips the Feature Flipper kill-switch flag off (e.g., ai_assistant_subagent_<id> → off).
  2. Factory refresh — sub-agent absent from dispatch surface.
  3. In-flight conversations complete on the prior sub-agent state (no torn state per FR-8); new turns route without it.

Why no consumer-agent redeploy:

  • Agent Definitions live in agent_config.yaml, read at factory invocation.
  • Feature Flipper flags are runtime-toggled.
  • Python modules for the sub-agent are already in the deployed image — they’re loaded if active, ignored if not.

Rollback target tracking (FR-7): every promotion records the prior stable Agent Definition commit — the previously-active Agent Definition whose lifecycle state is still promote. The rollback target is never an Agent Definition in rollback (per FR-12, rollback is terminal). Multiple-level rollback (V_new fails → V_prev → V_prev itself regresses) is operator-managed; each step re-activates a still-in-promote predecessor while the just-deactivated Agent Definition enters rollback. PF1 doesn’t auto-chain (matches PC6 OQ-6 disposition).

Multi-AD in promote: Multiple Agent Definitions MAY occupy promote concurrently. At any time, exactly one is the active Agent Definition (receives traffic per its ramp percentage in PC6’s rollout config); the others are rollback targets (preserved at promote with their last known ramp percentage, no traffic). When a rollback executes, the active AD transitions to rollback (terminal); a rollback target becomes active. Each in-promote AD has independent ramp-percentage state in PC6’s rollout config; PF-1’s lifecycle state machine tracks the AD’s promote/rollback/test/dev label, not the active/target distinction.

Automatic ramp-down vs lifecycle rollback transition. PC5’s post-deploy regression response (PC5 §5.7) provides operational protection by automatically ramping a regressing sub-agent down to 0% on safety/refusal judge floor breach. That auto-ramp-down operates on the cohort flag’s ramp percentage — it pauses exposure without leaving promote. The lifecycle transition to rollback is a deliberate human decision after triage; the operator either advances ramp percentage back up after a hotfix-forward fix (lifecycle stays in promote) or moves the sub-agent to rollback durably. The intermediate operational state — auto-ramp-down has fired, rollback decision pending — is observable in the rollout config’s recorded ramp percentage, not as a separate lifecycle state.

PF1 reuses PF8’s flag conventions and PC6’s cohort tuple discipline.

Gating is a server-side, per-request flag check — not a client-rendered variant. A sub-agent has no mobile surface the client gates; the server decides which sub-agents are dispatchable for a turn. The mechanism is the one PC1 already ships for its orchestrator gate (AGENT_CARD_RAMP + AGENT_CARD_RAMP_KILLSWITCH in utils/feature_flags.py): at factory/dispatch time the runtime calls flags.on(<gate_flag>, identity=user_id), and effective dispatchability = gate_on AND killswitch_on. Feature Flipper does the deterministic per-user_id hash, so a ramp percentage (5% → 100%) works server-side with no client involvement.

Flag convention (per PF8):

  • A gate flag ai_assistant_subagent_<id> (ramp %) and a paired kill-switch ai_assistant_subagent_<id>_killswitch, following the gate + _killswitch pairing PF8 commits and PC1 demonstrates. “Cohort flag” elsewhere in this spec is shorthand for the gate flag.
  • Lifecycle: created at the test → promote transition; retired when the sub-agent reaches 100% ramp inside promote AND operator decides the flag’s purpose is served.

Experiment-analysis overlay (per the Innovation Labs Experimentation Process, draft). When a sub-agent rollout is run as a measured experiment, the gating execution above is unchanged; an analysis + audience layer wraps it, owned by the experiment process, not PF1:

  • Eppo analyzes, FF assigns — never conflated. FF logs the per-user assignment to Snowflake; Eppo reads it for analysis only (no per-request Eppo SDK call). The rollout MUST conclude in Eppo before ramping treatment to 100% (else sample-ratio mismatch).
  • Parent pool (e.g. ai_assistant_user_pool) handles eligibility / exclusions / N-cap, server-side; enrollment (graph-worker) builds the personalized cohort population. These wrap the gate for measurement and audience; they are not the dispatch path.

PF1 references this overlay; it does not redefine it. The experiment doc is authoritative for the analysis/pool/enrollment mechanism.

Cohort tuple (per PC1 §5.5 + PC6 §5.7):

cohort = (
agent_definition_version,
prompt_block_registry_version,
xml_prompt_component_registry_version,
active_feature_flags,
experiment_arm_assignment,
active_sub_agents, # new in PF1
)

PF1 extends the cohort tuple with active_sub_agents — the set of sub-agent IDs the factory resolves as dispatchable for this cohort. Cardinality stays bounded (≤10 sub-agents at maturity per PC1 §11.3 verticals landscape).

Cache-friendly contract preserved: same-cohort users still share a stable prompt prefix per turn. Cohort cardinality grows by active_sub_agents set membership (typically a single-flag flip per sub-agent in flight), staying within the bound PC1 §5.5 commits.

Ramp curve. Declared per sub-agent in the rollout config, not platform-mandated. Fetch does not have a documented standard ramp curve, and PF1 does not invent one; experiment owners pick the curve appropriate to their sub-agent’s risk profile, time-to-stat-sig needs, and the observation window each step warrants. Considerations: safety-sensitive sub-agents warrant a smaller first step and longer observation windows; lower-risk sub-agents can ramp more aggressively. A platform-mandated default may be derived after several sub-agent rollouts produce empirical signal.

Decommission cleanup (FR-6) runs after the lifecycle reaches rollback if the sub-agent is being permanently removed from the codebase (rather than paused for investigation). It is a five-step path:

  1. Remove the Agent Definition entry from agent_config.yaml::agents. Marks the sub-agent as no longer dispatchable.
  2. Update orchestrator sub_agents lists in any Agent Definition that referenced the decommissioned sub-agent. CI catches dangling references (FR-11).
  3. Delete the sub-agent’s Python module(s) from the codebase. Or leave them in place (registry entry kept, flag off) for a transition window (NFR-5 commits up to one quarter), with the pending deletion tracked on the decommission ticket.
  4. Clean up unique prompt blocks — files in prompts/components/ that were only referenced by the decommissioned sub-agent’s Agent Definition. Per PC1 §5.7 file-backed registry pattern.
  5. Operator decides on eval-suite + tool retention:
    • Eval suite (PC5): dataset items, judge configs in evaluation_manifest.yaml’s categories. Operator chooses retain (for replay) or remove (cleanup).
    • Tool dependencies (PS2 connectors): connectors used only by the decommissioned sub-agent. Operator chooses retain (for reuse by future sub-agents) or remove.

Audit trail: each decommission step emits a lifecycle audit event per FR-10. The decommission is reversible up to the point of code-level deletion (Step 3).

Per FR-10, a lifecycle transition is recorded by PC6’s rollout audit (where the rollout-config change is made), carrying these PF1 transition fields:

{
"subagent_id": "earnings_coach",
"source_state": "promote",
"target_state": "rollback",
"trigger": "operator_initiated",
"agent_definition_commit": "abc123",
"cohort": {
"agent_definition_version": "5",
"active_flags": ["ai_assistant_subagent_earnings_coach"],
"ramp_step_percent": 100
}
}

Footnote on ramp_step_percent: snapshot at the rollout-config change; not the trigger — see PC-6 §5.10 for ramp-config events.

Recorded when the operator makes the rollout-config change that effects the transition (not in the agent runtime, which only reads derived state). Lands in PS5’s trace + event store per PS5 §boundary contract. Feeds:

  • PC6’s rollout dashboards (correlate prompt rollouts with sub-agent rollouts)
  • Per-vertical Grafana panels per PF8
  • Operational query path for “when did sub-agent X become active in cohort Y”

Ramp-percentage changes inside promote are likewise PC6 rollout-config events per PC6 §5.10; PF1’s transition fields ride the same PC6 audit record rather than a separate PF1 stream. What the agent runtime DOES emit (a gate denial and a kill-switch firing, at the dispatch boundary) is in §10.2.

5.8 PF1 ↔ PC5 partnership (extends PC5 §5.9 with Trigger column)

Section titled “5.8 PF1 ↔ PC5 partnership (extends PC5 §5.9 with Trigger column)”

Already detailed in §3.3. Restated for symmetry:

PF1 lifecycle transition or sub-statePC5 gate(s)Trigger
dev → testNone (developer-driven)Developer ready for review
test → promote (PR merge + operator initiates ramp)pre_merge (PR-driven, dataset-scoped)PR merge
Ramp progression inside promote — first non-zero steppre_ramp (PC6-driven, trace-sampled)Operator advances ramp percentage from 0 to first non-zero step
Ramp progression inside promote — 100% steppre_full (PC6-driven, larger trace sample)Operator advances ramp percentage to 100%
* → rollbackNoneOperator action OR kill-switch fires from any active state

PF-1 §5.8 extends PC-5 §5.9’s gate-mapping table with a Trigger column (lifecycle-action describing how each gate fires). PC-5 §5.9 is canonical for gate definitions; PF-1 §5.8 is canonical for the lifecycle-trigger pairing. Updates to gate definitions happen in PC-5; updates to trigger semantics happen in PF-1.

5.9 Cross-sub-agent dependency enforcement

Section titled “5.9 Cross-sub-agent dependency enforcement”

FR-11 commits a CI gate that blocks PRs whose changes would leave dangling sub-agent references. Concrete mechanism:

GitHub Actions workflow .github/workflows/subagent-validate-refs.yml runs on PRs that modify agent_config.yaml::agents or src/consumer_agent/agents/. The workflow invokes consumer_agent.subagents.validate_refs (new module; created as part of PF-1 implementation tracked in PLT-690) which:

(a) loads all active Agent Definitions; (b) verifies every sub_agents: entry resolves to an Agent Definition NOT in rollback; (c) for decommissions (Agent Definition being removed), verifies no other active orchestrator’s sub_agents: list still references it; (d) PR diff inspection: if the PR modifies agent_config.yaml::agents[N] where id is unchanged but the model field changed (any value), or tools list added/removed entries, or prompt_blocks list added/removed entries (per FR-12: ‘model swap, prompt block additions/removals, tool list changes’), CI MUST fail with the message: "Major Agent Definition changes require a new \agent_id`; in-place mutation of promoted Agent Definitions is prohibited (FR-12).”`

Failure of any check blocks merge.

SpecCitation
PC1 (Agent Composition)Inherits Agent Definition shape + factory-time binding + vertical integration model. PF1 commits the staged path to becoming active in the registry.
PC5 (Agent CI/CD Pipeline)Partners on the lifecycle ↔ gate mapping (§5.8 / PC5 §5.9). PC5 owns gate semantics; PF1 owns lifecycle states.
PC6 (Agent Variant CI/CD)Independent — PC6 is per-variant (Agent Definition fork; prompt-fork is the v1 worked example), PF1 is per-sub-agent. They share cohort discipline (PC1 §5.5 + PF1 §5.5).
PF5 (Vertical Scaffolding + Validation Tools)Scaffolds new sub-agent stubs conforming to PF1’s lifecycle states. PF1 commits the state shape PF5 scaffolds toward.
PF8 (Feature Flag & Cross-Vertical Observability Conventions)PF1 reuses PF8’s flag naming (ai_assistant_subagent_<id>), kill-switch lifecycle, required Grafana panels.
PS5 (Trace + Event Store)Persists PF1’s lifecycle audit events for post-hoc analysis.
PS2 (Connector Framework)Decommission cleanup (FR-6 Step 5) coordinates with PS2 on tool-connector cleanup.

Platform spec dependencies: PC1 (Agent Composition), PC5 (Agent CI/CD Pipeline).

Implementation dependencies:

  • LangChain v1, LangGraph (consumer-agent runtime)
  • Feature Flipper (cohort gating, kill-switch)
  • Opik (for audit trail correlation; partnered with PS5)

External dependencies: None.

Cross-section soft dependencies:

  • PF8 — flag naming + kill-switch conventions
  • PS5 — lifecycle audit-event persistence

R-1: Orphaned sub-agent references during decommission. Decommissioning sub-agent B while sub-agent A still references B in its sub_agents list breaks factory invocation. Mitigated by FR-11’s CI enforcement; AC-6 covers the test.

R-2: Mid-conversation kill-switch. A user mid-conversation when a sub-agent’s kill-switch flips off — does the user see torn state? Mitigated by FR-8’s in-flight tolerance (in-flight turns complete on the sub-agent they started with; new turns route without the rolled-back sub-agent).

R-3: Rollback target staleness. Same risk as PC6 OQ-6. If multiple rollbacks chain, the rollback target may itself be regressed. Mitigated by operator-managed multi-level rollback; PF1 doesn’t auto-chain.

R-4: Eval-suite + dataset cleanup ambiguity. Decommissioned sub-agents leave eval-suite artifacts (dataset items tagged with the sub-agent’s category, judge configs referencing it in the manifest). FR-6 Step 5 makes this operator-decided; the risk is that “retain for replay” becomes “forgotten forever” with no cleanup. Mitigated by NFR-5’s one-quarter decommission cleanup window forcing the decision.

R-5: Major-change re-baselining churn. FR-12 requires re-cycling on major Agent Definition changes (model swap, prompt block additions). Defining “major” too strictly causes churn; too loosely misses regressions. Mitigated by operator-override annotation (AC-9); long-term, “major” criteria codified in PC5’s threshold conventions.

R-6: Cohort tuple cardinality from concurrent sub-agent rollouts. Each sub-agent in promote at an intermediate ramp percentage adds a flag dimension to the cohort tuple. N concurrent rollouts → 2^N cohorts in the worst case. Mitigated by the bounded ≤10 sub-agents at maturity (PC1 §11.3) and Feature Flipper’s stable-assignment property.

OQ-1: RESOLVED. FR-12 defines “major change” concretely: model swap, prompt block additions/removals, tool list changes. Any of these requires authoring a NEW Agent Definition entering at dev. Finer-grained quantitative thresholds (e.g., % of system-prompt token churn) are deferred to PC5’s threshold conventions if needed later.

OQ-2: RESOLVED by NFR-5. Eval-suite retention follows NFR-5’s one-quarter decommission cleanup window: operator decides retain-vs-remove at decommission time; the decision is made within the NFR-5 window. FR-6 Step 5 carries the operator choice.

OQ-3: Cross-vertical orchestrator sub_agents list ownership. The orchestrator’s sub_agents list is platform-owned (PC1 §5.9 vertical integration model). When a vertical decommissions their sub-agent, the orchestrator’s list change crosses team boundaries. Lean: vertical drafts the PR; platform reviews. Codify in PF5’s scaffolding workflow. No external dependency.

OQ-4: Lifecycle state visibility — should there be a CLI command to list current state of every sub-agent (consumer-agent subagent list)? Lean: yes, low-effort, high operational value. No external dependency but worth a follow-on ticket.

OQ-5: Automatic advance vs operator-only. PF1’s design is operator-only between gates (no auto-advance). Lean: keep operator-only for v1; surface auto-advance as a follow-on if operational burden warrants. No external dependency.

  • State-machine transition validation: each valid transition succeeds; each invalid transition raises a clear error
  • Rollback target resolution: given a Agent Definition commit history, get_rollback_target returns the previous stable commit
  • Cohort tuple composition: active_sub_agents set is included in the cohort tuple; cardinality stays bounded
  • Kill-switch override: when the flag is off, factory invocation excludes the sub-agent regardless of other state
  • In-flight tolerance: simulated factory refresh mid-conversation doesn’t change the active sub-agent for that conversation
  • Retirement CI check: PR retiring sub-agent B with sub-agent A still referencing B in sub_agents is blocked
  • End-to-end promotion: new sub-agent → pre_merge (test → promote) → operator advances ramp percentage; pre_ramp fires at first non-zero step; pre_full fires at 100%
  • End-to-end rollback: sub-agent at 100% ramp in promote → operator initiates rollback → previous stable Agent Definition resolves at next factory invocation → NFR-2 budget verified
  • Kill-switch-initiated rollback: flag flips off → next factory invocation drops the sub-agent → lifecycle moves to rollback → in-flight conversations unaffected
  • Cohort-gated dispatch: cohort A gets the new sub-agent; cohort B doesn’t; trace events differentiate
  • Major-change re-baseline: PR with model swap → CI requires re-entry to test → all three PC5 gates run as ramp percentage advances
  • Per-sub-agent eval suites cover the sub-agent’s declared capabilities (per PC1 §5.2 + PC6 XML prompt components)
  • Lifecycle audit events visible in PS5; correlate with prompt-rollout (PC6) and DM-rollout (PD3) events for full-platform view
  • PC1: Agent Definition shape conforms to PC1 §5.2; promotion = registry activation per PC1 §5.3
  • PC5: lifecycle transition trigger calls PC5’s gate API per PC5 §5.8; verdict drives transition outcome
  • PF8: kill-switch flag naming + lifecycle matches PF8 conventions
  • PS5: lifecycle audit-event shape persists correctly in PS5’s store
  • Factory refresh fails mid-rollback: in-flight conversations continue; next factory invocation retries the rollback target resolution
  • Feature Flipper unreachable: factory falls back to fail-closed (sub-agent absent from dispatch surface)
  • PC5 gate API outage during transition: transition halts; operator notification; trace event records the gate-API failure
  • Concurrent state-transition attempts (race): only one succeeds; the other observes the new state and proceeds or aborts cleanly
  • Decommission with eval-suite cleanup partially completed: re-run idempotent; cleanup completes on retry

Phase 1 — Spec validation. PF1 reviewed and approved; cross-section contracts confirmed with PC1, PC5, PC6, PF5, PF8 reviewers.

Phase 2 — State machine + audit event implementation. Add the lifecycle state machine to consumer-agent runtime; wire audit-event emission to PS5’s store. Existing agent_config.yaml and factory.py changes shaped by §5.3.

Phase 3 — Cohort-gated rollout machinery. Wire Feature Flipper cohort + kill-switch lookups into factory invocation per §5.5. Cohort tuple extended with active_sub_agents.

Phase 4 — CI enforcement. Add CI checks per FR-11 (dangling sub-agent references) and FR-12 (major-change re-baseline). PF5 scaffolds the corresponding PR stub.

Phase 5 — First gated sub-agent rollout. Pick a low-risk new sub-agent (e.g., the first new vertical’s sub-agent after PF1 lands) and run it through the full lifecycle as the validation.

Phase 6 — Decommission worked example. When a deployed sub-agent is permanently removed (replaced by another sub-agent, or no longer needed), run the decommission cleanup contract as the first end-to-end exercise.

Emitted by the dispatch gate this runtime owns (the sub-agent gating path):

  • subagent.lifecycle.gate_failed_total by subagent_id, milestone — gate failure rate per sub-agent per milestone; correlate with PC5’s gate metrics
  • subagent.lifecycle.kill_switch_fired_total by subagent_id — kill-switch firing rate

Deferred — these measure transition, rollback, and decommission actions, which are operator rollout-config changes this runtime never observes. They are emitted where those actions occur (the rollout-config trigger and the trace/event store), not in the agent process, and land once those surfaces exist:

  • subagent.lifecycle.transition_total by subagent_id, source_state, target_state, trigger — transition volume per sub-agent per state
  • subagent.lifecycle.rollback_duration_seconds by subagent_id — rollback latency (feeds NFR-2)
  • subagent.lifecycle.decommission_cleanup_pending_total — decommissioned sub-agents with cleanup not yet complete (FR-6 + NFR-5)

PF1 is a contract spec, not deployable code. Rollback semantics apply at three layers:

  • Sub-agent-level rollback: per §5.4. Standard operational lever.
  • CI gate rollback: if a CI check (FR-11 / FR-12) produces false positives, disable in CI config independently of the spec.
  • Architecture-level rollback: reverting the in-process sub-agent model would require revisiting PC1 §3.3’s factory-time binding decision. Not expected.
#DecisionResolution
1Lifecycle scopeSub-agents only (Scout, Shopping, etc.) — not generic mesh-agent runtime.
2Promotion semanticsRegistry activation via agent_config.yaml + Feature Flipper cohort flag. No deploy, no image build, no ECS rollout.
3Rollback semanticsConfig revert + factory refresh. NFR-2 commits 5-minute target. Matches PC5/PC6 rollback patterns.
4Cohort-gated rolloutFeature Flipper flag per sub-agent (ai_assistant_subagent_<id>); cohort tuple extended with active_sub_agents per §5.5.
5Retirement contractFive-step cleanup (FR-6): config removal → reference cleanup → code deletion → prompt-block cleanup → eval/tool operator decision. NFR-5 commits one-quarter cleanup window.
6Major-change re-baseliningFR-12 requires re-cycling on major Agent Definition changes (model swap, significant prompt-block changes, tool list changes). Operator override annotation for minor changes.
7Lifecycle state machineFour states: dev, test, promote, rollback. Operational ramp-percentage progression inside promote is recorded in the PC6 experiment config, not as separate lifecycle states. Transitions are gate-driven or operator-driven, never automatic.
8In-flight toleranceIn-flight turns complete on the sub-agent they started with; new turns route to current state. Matches PC1 §5.5 factory-time binding.
  • From PC1 §5.2 (Agent Definition shape): PF1 inherits the declarative contract; commits the staged path to registry activation.
  • From PC5 §5.9 (PF1-partnership table): PF1 §3.3 + §5.8 mirror the table for readability; PC5 is the canonical commitment.
  • No content migration from other specs: PF1’s lifecycle states are new commitments codifying what’s been informal.