Skip to content

Migrating from symfonic-core v6.x to v7.0

v7.0.0 is a major release. It introduces the four v7.0 Thalamic Architecture subsystems (intent filter, N-hop spreading, STM summary, fabrication executor, nightly_nap) and removes one long-deprecated constructor kwarg. Everything else is additive with v6.x observable behaviour preserved at defaults.

TL;DR — if your code does not reference SymfonicAgent(enable_hms_prompt=...), upgrading is a drop-in.

Breaking changes

B1 — SymfonicAgent(enable_hms_prompt=...) removed

Status: Landed.

The constructor kwarg has emitted DeprecationWarning since v5.6.0. In v7.0 it raises TypeError because Python does not accept unknown keyword arguments.

Before (v6.x)

This v6.x constructor was removed in v7. It is shown only as the migration starting point.

agent = SymfonicAgent(
    model_provider=provider,
    enable_hms_prompt=True,          # deprecated kwarg
)

After (v7.0)

Legacy 9.12 example: Install symfonic-core==9.12.* for this SymfonicAgent snippet. For version 11, see the Agent migration guide.

from symfonic.agent.config import FrameworkConfig
from symfonic.agent.engine import SymfonicAgent

config = FrameworkConfig(enable_hms_prompt=True)
agent = SymfonicAgent(
    model_provider=provider,
    config=config,
)

The FrameworkConfig path has been available since v6.0 and is unchanged in v7.0.

Migration script

Run this one-liner to detect the pattern across a codebase:

grep -rn "SymfonicAgent(.*enable_hms_prompt=" path/to/your/code

If nothing matches, you are done.

Additive changes (no migration required)

FrameworkConfig knobs

Field Default v6.x behaviour at default To change behaviour
intent_filter_mode "off" Classifier never runs; full hydration every turn "observe" for telemetry only; "enforce" to skip semantic/episodic/procedural on action verdicts
hydration_max_hops 0 Single asyncio.gather of 1st-degree neighbours (byte-identical to v6.1.x) >=1 enables BFS with per-hop decay
stm_summary_mode "off" WorkingLayer.summarize() returns "" (legacy stub) "extractive" produces bullets with no LLM cost; "llm" issues one micro-call with extractive fallback
fabrication_check_mode "off" Detector never runs; v6.1.10 TOOL DISCIPLINE prompt directive is the only defence "observe" emits telemetry; "revise" forces metacog revise; "refuse" replaces draft with safe envelope
nightly_nap_enabled False nightly_nap method available but operator-invoked Schedule agent._sleep_consolidator.nightly_nap(scope) via your scheduler
nightly_nap_cron "0 3 * * *" No effect — recommendation only Set to your preferred off-peak cron expression

DomainTemplate additions

Field Default Purpose
identifier_keys [] Extra keys the fabrication detector should flag in addition to the built-in lexicon (task_id, pid, job_id, ...). Empty preserves v6.x behaviour.

AgentResponse additions

Field Default Populated when
intent_verdict None intent_filter_mode != "off"
fabrication_findings None fabrication_check_mode != "off" AND detector fires

Enabling the new subsystems

The v7.0 subsystems are opt-in. Here is a recommended adoption order:

1. Intent filter (lowest risk)

Start in observe mode for a burn-in week. The classifier emits ExtensionEvent(type="intent_classified") to callback handlers that implement on_intent_classified. Tune the corpus before flipping to enforce.

config = FrameworkConfig(intent_filter_mode="observe")

Once confident, flip to enforce:

config = FrameworkConfig(intent_filter_mode="enforce")

2. N-hop spreading

Start with hydration_max_hops=1 (explicit 1-hop, still a single BFS level but now with spreading_access_count bumping per v6.2 T02). Measure payload size and latency before raising:

config = FrameworkConfig(hydration_max_hops=2)

The default base_decay=0.5 and max_total_nodes=50 guard against runaway expansion.

3. STM summary

Start with extractive — zero LLM cost, deterministic output:

config = FrameworkConfig(stm_summary_mode="extractive")

Upgrade to "llm" if your domain needs better compression and you are willing to pay one micro-call per summary. The extractive path is the automatic fallback on LLM failure.

4. Fabrication check

Start in observe to collect findings:

config = FrameworkConfig(fabrication_check_mode="observe")

Implement on_fabrication_detected on your callback handler to capture the events. When confident the detector is not producing false positives, escalate to revise or refuse.

fabrication_check_mode='revise' requires metacognition_enabled=True. The revise path threads findings into MetacognitiveMiddleware.evaluate() to produce a genuine revised draft. Without metacog enabled there is no reviser to run, so the agent emits a UserWarning at construction and downgrades the effective mode to refuse semantics (safe-envelope replacement + fabrication_blocked event). If you want real revise behaviour, pair the two flags:

config = FrameworkConfig(
    fabrication_check_mode="revise",
    metacognition_enabled=True,
)

5. nightly_nap

Enable the entrypoint:

config = FrameworkConfig(
    nightly_nap_enabled=True,
    nightly_nap_cron="0 3 * * *",  # 03:00 UTC daily
)

Wire your scheduler (APScheduler / celery-beat / external cron) to invoke agent._sleep_consolidator.nightly_nap(scope) per tenant at the recommended cron cadence. The engine does not schedule this itself — operator responsibility.

Tests

v7.0.0 adds dedicated test files per subsystem; your test suite should remain green after the upgrade because defaults preserve v6.x behaviour. If a v6-era test fails, please file an issue — it indicates an inadvertent default flip.

  • tests/agent/triage/test_intent_classifier.py — T06
  • tests/agent/triage/test_intent_wiring.py — T07
  • tests/memory/unit/test_spreading_nhop.py — T08
  • tests/memory/unit/working/test_stm_summary.py — T09
  • tests/agent/middleware/test_fabrication.py — T10
  • tests/agent/middleware/test_fabrication_wiring.py — T11
  • tests/core/learning/test_nightly_nap.py — T12
  • tests/release/test_v7_0_0_release_gate.py — T15 pin

v7.0.2 note — DomainTemplate.description now reaches the LLM

DomainTemplate.description has been a public field since v5.6.x, but no prompt builder ever consumed it — every value set there was silently dropped. v7.0.2 wires it into both the full HMS and JIT system prompts for the first time.

If your v6.x / v7.0.0 / v7.0.1 deployment set DomainTemplate(description="..."), upgrading to v7.0.2 will cause that prose to reach the LLM's system prompt. This can affect:

  • Token budgets — the prompt will be larger in both modes.
  • LLM behaviour — the directives will actually steer responses. Review the description text before upgrading.
  • Truncation — descriptions over 2000 chars (full HMS) or 400 chars (JIT) are capped and marked ... [truncated], with a single WARNING log per agent lifetime. Raise the caps via the new FrameworkConfig.domain_description_max_chars tuple if needed:
cfg = FrameworkConfig(
    domain=DomainTemplate(description="..."),
    domain_description_max_chars=(5000, 800),  # (full_hms, jit)
)

No migration is required for callers who never populated domain.description — the rendered prompt shows a short fallback which the LLM treats as a no-op.

See docs/releases/v7.0.2.md for the full rationale including why the JIT template uses an inline line instead of a new ## 1a. section.

References