Migration Guide: v0.3 → v0.4¶
A short checklist for upgrading from Machina v0.3.x to v0.4.0. Six changes can break existing code or deployments; everything else is additive or a fix.
Breaking Changes¶
1. Writes need confirmation by default¶
What changed: Agent(confirmations=...) now defaults to True (the
confirmations: YAML key mirrors it). Mutating tool calls — create_work_order,
execute_workflow — require a human confirmation before they execute.
Impact on programmatic callers: with confirmations on and no confirmer
supplied, a write is fail-safe — it is not executed. The agent proposes the
action and waits for a follow-up confirming message (the two-turn flow). If you
drive the agent programmatically and expect autonomous writes:
# Opt out of the confirmation gate (restores the 0.3 behaviour):
agent = Agent(..., confirmations=False)
# Or in machina.yaml:
# confirmations: false
# Or keep the gate and supply a synchronous confirmer:
async def confirmer(prompt: str) -> bool:
... # render prompt, return the user's decision
await agent.handle_message_full(text, confirmer=confirmer, user_id="...")
trigger_workflow is a deliberate direct-execution path guarded by sandbox
only; it is not gated by confirmations.
Known limitations:
- Anonymous senders cannot confirm (fail-safe withhold). On a shared async
channel, the two-turn confirmation binds the pending write to the sender's
user_id. If the channel cannot supply a trusted sender identity (emptyuser_id), the write is withheld — not stored and not executed — so an unidentified participant can never confirm it. - Pending confirmations expire. A pending write is only valid for the
immediate follow-up; it expires after one hour (
_PENDING_ACTION_TTL_SECONDS), so a much-later bare "ok" is treated as a fresh message, not a stale confirmation. - Email sender identity is NOT authenticated. The email channel derives
chat_idanduser_idfrom the unauthenticated, spoofableFrom:header (no DKIM/SPF/allowlist). Two-turn write confirmation over email therefore provides no integrity guarantee — a spoofed sender could confirm a write. For write-confirmation use, gate the email connector behind sandbox mode or a verified-sender allowlist, or avoid confirmations over email.
2. Reinstall the [docs-rag] extra¶
The Chroma vector store now comes from langchain-chroma (and the optional
HuggingFace embedder from langchain-huggingface). pip does not remember
extras, so upgrade with the extra named:
A bare pip install -U machina-ai leaves langchain-chroma missing; the
document store then falls back to keyword search and logs a warning naming the
missing package.
3. EntityResolver.resolve_best removed; ResolvedEntity needs a confidence¶
resolve_best had no production callers and is gone — use resolve() and read
the ranked list. ResolvedEntity.confidence is now required:
from machina.agent.entity_resolver import ResolvedEntity
# v0.3
entity = ResolvedEntity(asset)
# v0.4
entity = ResolvedEntity(asset, confidence=1.0, match_reason="exact_id")
4. Custom connectors: declare failure-mode catalogs¶
The runtime no longer reads a private _failure_modes attribute. A connector
that serves a failure-mode catalog must declare the capability and implement
the public method:
from typing import ClassVar
from machina.connectors.capabilities import Capability
from machina.domain.failure_mode import FailureMode
class MyCmmsConnector:
capabilities: ClassVar[frozenset[Capability]] = frozenset(
{Capability.READ_ASSETS, Capability.READ_FAILURE_MODES}
)
async def read_failure_modes(self) -> list[FailureMode]:
...
5. MCP bearer tokens: at least 32 characters¶
The MCP server refuses to start when a token in MACHINA_MCP_TOKENS_JSON (or
the legacy MACHINA_MCP_TOKENS) is shorter than 32 characters. Generate tokens
with openssl rand -hex 32. In practice this affects only new setups: the
streamable-HTTP transport could not serve tool calls in v0.3.x.
6. simulated_sensor and cli are no longer YAML connector types¶
Agent.from_config builds connectors from the one registry machina describe
lists. Construct SimulatedSensorConnector in Python and pass it in
connectors=[...] (see examples/reference/predictive_pipeline/agent.py);
declare the CLI under channels: — or leave channels: out, since the CLI is
the default.
Behaviour Changes to Check¶
- MCP HTTP binds
127.0.0.1by default. Inside a container, or behind a proxy on another machine, pass--host 0.0.0.0. Requests whoseHost(orOrigin) is not inmcp.allowed_hosts/mcp.allowed_originsare rejected; the defaults cover loopback names only, so list your public host name when you put a proxy in front. See MCP Auth. /healthwithout a token returns only{"status": "healthy"}; connector names, sandbox mode and version need a valid token.- Citations in
AgentResponse.textare renumbered[1]..[N]by first appearance, andAgentResponse.citationsfollows that display order. diagnose_failureresults carry a numericconfidence,matching_indicatorsandrecommended_actionsper entry, capped at 5; empty results always include anote.- Custom workflows that bound
{analyze_alarm.primary_code}into a work order should switch to{analyze_alarm.failure_mode_for_write}, the confidence-gated accessor the built-inalarm_to_workordernow uses. get_maintenance_scheduleis offered to the LLM only when a connector declaresread_maintenance_plans, and returns the plans (no due dates).- Workflow write detection no longer counts
setinsideasset,datasetoroffset, so an action such ascmms.read_assetsis no longer treated as a write;upsertnow is. If a custom action is a write but its name does not look like one, mark the stepis_write=Trueso sandbox mode intercepts it. - Excel/CSV and SQL settings are validated: unknown keys are refused by
name. The SQL connector's
read_writemode no longer declaresupdate_work_order, which it cannot perform. - Excel/CSV and SQL writes: Excel updates rewrite only the changed cells
and refuse fields no column maps, and reads before
connect()raiseConnectorError. A work-order row with an invalid value (an unknown status) is now skipped with a warning; in v0.3 it madeconnect()fail. SQL creates needidininsert_columnsto stay idempotent; a unique constraint on the ID column guards against other writers.
New (No Migration Needed)¶
machina mcp serve— the MCP server from themachinaCLI.${VAR:-default}placeholders in YAML configs.Asset.aliases— curated per-plant synonyms, matched like the asset name.- Answerable disambiguation — reply to "which asset?" by ID, name or ordinal.
Capability.READ_FAILURE_MODESon the Generic CMMS, SQL and Excel/CSV connectors.- YAML
auth/paginationblocks for the Generic CMMS, SAP PM and Maximo connectors, and YAML-buildable Excel/CSV and SQL connectors. - LLM token usage and cost recorded on
agent.tracer.
Deprecations¶
- Raw-string capabilities (
list[str]declarations, raw strings passed tofind_by_capability) still work in v0.4, with aDeprecationWarningon the latter. Their removal moves from v0.4 to v0.5 — declarefrozenset[Capability]. MACHINA_MCP_TOKENSremains deprecated; useMACHINA_MCP_TOKENS_JSON.
Checklist¶
- [ ] Upgrade with your extras:
pip install -U "machina-ai[litellm,docs-rag]"(addmcp,excel,sql, … as you use them) - [ ] Programmatic agents that write: set
confirmations=Falseor pass aconfirmer - [ ] Code constructing
ResolvedEntity: passconfidence= - [ ] Custom connectors with a failure-mode catalog: declare
READ_FAILURE_MODESand implementread_failure_modes() - [ ] MCP over HTTP: tokens of 32+ characters,
--hostandallowed_hostsfor containers and proxies - [ ] Custom workflows:
failure_mode_for_write,is_write=Truewhere needed - [ ] YAML configs with
type: simulated_sensorortype: cliunderconnectors:: move them to Python orchannels: - [ ] Run your tests