Upgrading Consumers to v1.5 Stable Bridges¶
If your project consumed POLARIS or LYRA symbols from
fcc.plugins.v1_5_preview during the v1.4.x preview window, this
tutorial is how you move to the v1.5.0 stable fcc.api.* surface
before the preview paths are removed in v1.6.0.
This tutorial targets three audiences:
- PAOM, ice_ext, and the 10 constellation vocabularies — the primary consumers that pinned against the preview namespace.
- Community plugin authors who depend on POLARIS or LYRA bridges for archive or graph-of-thought capabilities.
- Internal FCC teams maintaining scenarios, demos, or notebooks that used preview imports.
You will end this tutorial with:
- Every
fcc.plugins.v1_5_preview.*import replaced with the stablefcc.api.*(or module-level) equivalent. - A dependency pin update to
fcc-agent-team-ext>=1.5.0,<2.0.0. - Updated unit tests that pin the new import surface explicitly.
- A clear deprecation path for any code you cannot immediately move.
What changed in v1.5.0¶
v1.5.0 promoted POLARIS and LYRA from the preview namespace to stable public API. The full rationale lives in docs/migrations/v1.5.0-api-additions.md; the short version is:
- POLARIS — long-term model archive coordination.
- Preview:
fcc.plugins.v1_5_preview.polaris_bridge - Stable aggregate:
fcc.api.get_polaris_bridge,fcc.api.PolarisBridgeProtocol,fcc.api.PolarisMockBridge - Stable module home:
fcc.archive.polaris_bridge - LYRA — graph-of-thought + harmonic vocabulary expansion.
- Preview:
fcc.plugins.v1_5_preview.lyra_bridge - Stable aggregate:
fcc.api.get_lyra_bridge,fcc.api.LyraBridgeProtocol,fcc.api.LyraMockBridge - Stable module home:
fcc.knowledge.lyra_bridge
Both bridges gain the 1.x SemVer commitment — method signatures stay
compatible for the entire 1.x line. The mock fallback classes
(PolarisMockBridge, LyraMockBridge) return functional in-memory
implementations when the live backend is absent, so downstream code can
exercise the full protocol without any live service.
ADR-008 documents the
POLARIS absorption design. The LYRA bridge follows the same pattern but
remains "mock-stable" because the live lyra package has not shipped
yet (see the codename decoder LYRA
entry).
Deprecation window¶
The preview import paths keep working throughout the v1.5.x line:
| Release | Preview imports | Stable imports | Recommended action |
|---|---|---|---|
| v1.4.0 | not present | not present | — |
| v1.4.4 | preview added | not present | pin preview if you need early access |
| v1.5.0 | preview + deprecation notice | stable added | begin migration |
| v1.5.1 | preview + deprecation notice | stable | migrate |
| v1.5.2 | preview + deprecation notice | stable | migrate |
| v1.5.3 | preview + deprecation notice | stable | migrate |
| v1.6.0 | removed | stable | must be migrated |
That is one minor-release deprecation window — the standard FCC
policy established in v1.4.4. v1.6.0 will not ship the preview paths
and your imports will ImportError if still pointing there.
Step-by-step migration¶
Step 1 — Audit your imports¶
Grep your codebase for preview imports:
grep -rn "fcc.plugins.v1_5_preview" --include="*.py" src/ tests/
# Also check configs + docs
grep -rn "fcc.plugins.v1_5_preview" --include="*.md" --include="*.toml" --include="*.yaml" .
Record every occurrence. You will change every one.
Step 2 — POLARIS bridge replacements¶
Before (v1.4.x):
# Preview namespace — will be removed in v1.6.0
from fcc.plugins.v1_5_preview.polaris_bridge import (
PolarisBridgeProtocol,
PolarisMockBridge,
get_polaris_bridge,
)
bridge = get_polaris_bridge()
archive_id = bridge.push_archive(manifest_path)
models = bridge.query_models(zachman_cell="ENGINEER/WHEN")
After (v1.5.0+):
# Preferred — single public API surface, 1.x SemVer guaranteed
from fcc.api import (
PolarisBridgeProtocol,
PolarisMockBridge,
get_polaris_bridge,
)
bridge = get_polaris_bridge()
archive_id = bridge.push_archive(manifest_path)
models = bridge.query_models(zachman_cell="ENGINEER/WHEN")
Or, if you prefer module-level imports:
# Also valid — direct stable-module import
from fcc.archive.polaris_bridge import (
PolarisBridgeProtocol,
PolarisMockBridge,
get_polaris_bridge,
)
Functional equivalence. The stable imports resolve to the same Python object as the preview imports via a re-export. This is an invariant pinned by the test suite:
import fcc.api
import fcc.archive.polaris_bridge
assert fcc.api.get_polaris_bridge is fcc.archive.polaris_bridge.get_polaris_bridge
Your runtime behaviour does not change.
Step 3 — LYRA bridge replacements¶
Before (v1.4.x):
from fcc.plugins.v1_5_preview.lyra_bridge import (
LyraBridgeProtocol,
LyraMockBridge,
get_lyra_bridge,
)
bridge = get_lyra_bridge()
thoughts = bridge.graph_of_thought_query("persona", depth=2)
terms = bridge.vocab_lookup("curiosity", namespace="lyra-seed")
related = bridge.harmonic_expand("persona")
After (v1.5.0+):
# Preferred
from fcc.api import (
LyraBridgeProtocol,
LyraMockBridge,
get_lyra_bridge,
)
bridge = get_lyra_bridge()
thoughts = bridge.graph_of_thought_query("persona", depth=2)
terms = bridge.vocab_lookup("curiosity", namespace="lyra-seed")
related = bridge.harmonic_expand("persona")
Or via the stable module home:
Step 4 — Availability sentinels + no-op fallbacks¶
If your code inspects whether POLARIS / LYRA are live (backed by the
real ice_ext.plugin.bridge for POLARIS or lyra.api for LYRA) rather
than the mock:
Before (v1.4.x):
from fcc.plugins.v1_5_preview.polaris_bridge import polaris_available
if polaris_available():
# live backend
...
else:
# mock fallback
...
After (v1.5.0+):
from fcc.archive.polaris_bridge import polaris_available
if polaris_available():
# live backend
...
else:
# mock fallback
...
The polaris_available / lyra_available helpers live at module
level and are also covered by the 1.x SemVer commitment.
Step 5 — Dependency pin update¶
Update the FCC pin in every consumer:
# pyproject.toml — before
dependencies = [
"fcc-agent-team-ext>=1.4.4,<1.5.0", # forced pin for preview
]
# pyproject.toml — after
dependencies = [
"fcc-agent-team-ext>=1.5.0,<2.0.0", # stable 1.x bridge surface
]
For setup.py / setup.cfg / requirements.txt use the same
specifier. The <2.0.0 upper bound is defensive — FCC's SemVer
commits to preserving 1.x backward compatibility indefinitely.
Regenerate your lockfile (poetry lock, pip-compile, etc.) and
verify the resolver picked up 1.5.x.
Step 6 — Suppressing DeprecationWarnings during the transition¶
The preview modules issue DeprecationWarning on import. If you are
temporarily unable to migrate (e.g. a vendored fork of a library still
on preview imports), silence the warning locally — but do not silence
globally:
import warnings
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=DeprecationWarning,
module=r"fcc\.plugins\.v1_5_preview\..*",
)
# The preview import fires inside this scope
from fcc.plugins.v1_5_preview.polaris_bridge import get_polaris_bridge
bridge = get_polaris_bridge()
This is a band-aid, not a solution. Open a tracking issue in your repo so the preview imports get removed before v1.6.0.
Step 7 — Testing the new imports¶
Add a test that pins the new surface explicitly. A common pattern:
# tests/test_fcc_integration.py
"""Pin the v1.5.0 POLARIS + LYRA bridge surface."""
import fcc.api
import fcc.archive.polaris_bridge
import fcc.knowledge.lyra_bridge
def test_polaris_identity():
"""fcc.api re-exports are identity-equal to module-level homes."""
assert fcc.api.get_polaris_bridge is fcc.archive.polaris_bridge.get_polaris_bridge
assert fcc.api.PolarisBridgeProtocol is fcc.archive.polaris_bridge.PolarisBridgeProtocol
assert fcc.api.PolarisMockBridge is fcc.archive.polaris_bridge.PolarisMockBridge
def test_lyra_identity():
assert fcc.api.get_lyra_bridge is fcc.knowledge.lyra_bridge.get_lyra_bridge
assert fcc.api.LyraBridgeProtocol is fcc.knowledge.lyra_bridge.LyraBridgeProtocol
assert fcc.api.LyraMockBridge is fcc.knowledge.lyra_bridge.LyraMockBridge
def test_mock_bridge_functional():
"""PolarisMockBridge + LyraMockBridge cover the full protocol."""
pbridge = fcc.api.PolarisMockBridge()
archive_id = pbridge.push_archive("/tmp/fake-manifest.yaml")
assert archive_id.startswith("archive-")
lbridge = fcc.api.LyraMockBridge()
terms = lbridge.vocab_lookup("curiosity", namespace="lyra-seed")
assert isinstance(terms, list)
def test_no_preview_imports():
"""This package does not import preview paths anymore."""
import subprocess
result = subprocess.run(
["grep", "-r", "fcc.plugins.v1_5_preview", "src/"],
capture_output=True,
text=True,
)
assert result.returncode != 0, (
"Preview imports found — migrate before v1.6.0:\n" + result.stdout
)
The last test is the one that closes the migration. Once it passes in CI you can be confident v1.6.0 will not ImportError on your code.
SemVer contract — what you can rely on¶
For every symbol in fcc.api.__all__:
- Method signatures stay compatible for the full 1.x line. New optional parameters may be added (additive); existing parameters do not change type or semantics.
- Return types continue to satisfy the documented Protocol.
get_polaris_bridge()always returns something that structurally satisfiesPolarisBridgeProtocol— whether that is a live bridge, a mock, or a future adapter. - Protocol method contracts do not change incompatibly. Adding a new protocol method is considered a breaking change that bumps 1.x → 2.x; never within 1.x.
- Mock classes remain usable.
PolarisMockBridge()andLyraMockBridge()continue to default-construct without arguments and continue to implement the full protocol. - Availability sentinels remain callable.
polaris_available()andlyra_available()remain() -> bool.
What can change inside 1.x:
- The internal implementation (the file layout, private helpers, docstrings). None of this is contract.
- The mock's in-memory storage structure. Your tests should not pin
PolarisMockBridge._storageor similar private attributes. - The no-op + live-adapter selection logic, as long as the returned object still satisfies the protocol.
Dependency-pin guidance for downstream ecosystems¶
| Consumer | Previous pin | v1.5 stable pin | Notes |
|---|---|---|---|
| PAOM | fcc>=1.3.3,<1.4.0 |
fcc>=1.5.0,<2.0.0 |
PAOM has always consumed the stable API — the preview was optional |
| ice_ext | fcc>=1.4.1,<1.5.0 + preview imports |
fcc>=1.5.0,<2.0.0 via fcc.archive.polaris_bridge |
The primary POLARIS consumer; KGSE persona tracks future promotions |
| ATHENIUM | fcc>=1.2.1,<1.3.0 |
fcc>=1.5.0,<2.0.0 when ready |
JV L2 library, no POLARIS/LYRA dependency today |
| MNEMOSYNE | fcc>=1.2.1,<1.3.0 |
fcc>=1.5.0,<2.0.0 when ready |
JV L2 library, no POLARIS/LYRA dependency today |
| Constellation verticals (Ophiuchus, Serpens, Libra, Crater, Scutum, Norma, Pyxis, Vela, Columba, Caelum) | fcc>=1.4.0,<1.5.0 |
fcc>=1.5.0,<2.0.0 |
10 public repos at v0.2.0; bump in lockstep |
Worked example — migrating an ice_ext consumer¶
ice_ext is the canonical POLARIS consumer + the tutorial target for this migration. Here is a representative before / after diff.
Before — v1.4.4 era preview imports:
# src/ice_ext_consumer/archive_client.py
from __future__ import annotations
import warnings
from pathlib import Path
from typing import Iterator
from fcc.plugins.v1_5_preview.polaris_bridge import (
PolarisBridgeProtocol,
get_polaris_bridge,
polaris_available,
)
class ArchiveClient:
"""Legacy ArchiveClient pinned to v1.4.x preview imports."""
def __init__(self) -> None:
if not polaris_available():
warnings.warn(
"POLARIS live backend not available; using mock.",
stacklevel=2,
)
self._bridge: PolarisBridgeProtocol = get_polaris_bridge()
def push(self, manifest: Path) -> str:
return self._bridge.push_archive(manifest)
def query(self, zachman_cell: str) -> Iterator[dict]:
yield from self._bridge.query_models(zachman_cell=zachman_cell)
After — v1.5.0 stable imports:
# src/ice_ext_consumer/archive_client.py
from __future__ import annotations
import warnings
from pathlib import Path
from typing import Iterator
from fcc.api import PolarisBridgeProtocol, get_polaris_bridge
from fcc.archive.polaris_bridge import polaris_available
class ArchiveClient:
"""v1.5+ ArchiveClient — stable fcc.api surface, 1.x SemVer guaranteed."""
def __init__(self) -> None:
if not polaris_available():
warnings.warn(
"POLARIS live backend not available; using mock.",
stacklevel=2,
)
self._bridge: PolarisBridgeProtocol = get_polaris_bridge()
def push(self, manifest: Path) -> str:
return self._bridge.push_archive(manifest)
def query(self, zachman_cell: str) -> Iterator[dict]:
yield from self._bridge.query_models(zachman_cell=zachman_cell)
The diff is deliberately minimal — only the import lines change. Runtime behaviour, protocol method calls, and mock fallback semantics are preserved bit-for-bit.
Migrating scenario files + demos¶
Scenarios and demos that reference the preview module in their persona prompts or scenario JSON need parallel updates. A typical scenario JSON:
{
"id": "POLARIS-ARCHIVE-001",
"name": "Archive a manifest via the stable POLARIS bridge",
"workflow": "base",
"personas": ["LTAS"],
"setup": {
"ai_config": {
"provider": "mock",
"model": "mock-default"
},
"resources": {
"polaris_bridge_import": "fcc.api.get_polaris_bridge"
}
},
"steps": [
{
"phase": "create",
"persona": "LTAS",
"prompt": "Using the import path {{polaris_bridge_import}}, push the manifest at /tmp/fake-manifest.yaml and return the archive ID."
}
]
}
Update the polaris_bridge_import resource from
fcc.plugins.v1_5_preview.polaris_bridge.get_polaris_bridge to
fcc.api.get_polaris_bridge. Your scenario's persona prompts will
inherit the update automatically.
Migrating notebooks¶
FCC ships 45–48 notebooks under notebooks/. The v1.5.0 notebooks
already use stable imports. If you have forked a notebook that used
preview imports:
- Open the notebook + search cells for
fcc.plugins.v1_5_preview. - Replace every hit with the stable equivalent.
- Re-run the notebook top-to-bottom to confirm runtime behaviour is unchanged.
- Commit the notebook + an accompanying
*.mdexport so non-Jupyter readers see the change in the git diff.
CI integration for the migration¶
Add a CI step that enforces "no preview imports" in your consumer repo. A minimal GitHub Actions job:
# .github/workflows/no-preview-imports.yml
name: No Preview Imports
on:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Ban preview imports
run: |
if grep -rn "fcc.plugins.v1_5_preview" \
--include="*.py" --include="*.ipynb" \
--include="*.md" --include="*.yaml" \
--include="*.json" \
src/ tests/ notebooks/ docs/ scenarios/; then
echo "::error::Preview imports found — migrate to fcc.api.* before v1.6.0"
exit 1
fi
echo "OK — no preview imports"
Run this as a required check on your protected branch once you have completed the migration. It is the cheapest possible insurance against regressions.
Common pitfalls¶
Star-import shadowing. If your package uses
from fcc.api import *, be aware that __all__ on fcc.api may not
yet include every POLARIS / LYRA symbol. Name imports explicitly.
Import-time side effects. Consumers that triggered LYRA seed-graph construction at module import time will notice the mock initialises lazily in v1.5.0. Explicitly instantiate once at startup if you need the graph pre-warmed.
Version overlap. A workspace with both a preview-pinned package and a stable-pinned package may end up pulling both FCC versions if your resolver is lax. Use lockfiles + pin to a single FCC version per workspace.
Docs still pointing at preview. Search your docs for references
to fcc.plugins.v1_5_preview and update them. The
v1.5.0 API additions migration doc
and this tutorial are the only documents allowed to mention the
preview paths (as "before" examples).
Availability sentinel imported from preview. The
polaris_available / lyra_available helpers live at
fcc.archive.polaris_bridge / fcc.knowledge.lyra_bridge — not at
fcc.api top level. This is intentional — the sentinels are
module-level because they inspect the environment, not instance
state. Import them from the module, not the aggregate namespace.
Mock type assumptions. If your tests check
isinstance(bridge, PolarisMockBridge) to detect the mock, the
isinstance check still works across the migration (identity is
preserved). But if you relied on a string repr of the bridge type
in logs, re-verify the log format — the module path has changed from
fcc.plugins.v1_5_preview.polaris_bridge.PolarisMockBridge to
fcc.archive.polaris_bridge.PolarisMockBridge.
Deprecation warnings in tests. If your test runner treats
DeprecationWarning as an error (common with filterwarnings =
error in pytest config), preview imports in transitive dependencies
will break your test run. Either scope the error filter to exclude
fcc.plugins.v1_5_preview.* or finish migrating transitive consumers
first.
Timing + release-planning guidance¶
When you ship your v1.5-compatible consumer, align the release with your downstream consumers' own release cadence. A realistic timeline:
| Week | Your consumer | Downstream of your consumer |
|---|---|---|
| 0 | Merge migration PR | Pinned to your pre-migration tag |
| 1 | Tag release with migration noted in CHANGELOG | Receive release notes |
| 2-3 | Your docs + samples updated | Begin their own migration |
| 4-6 | CI enforcement (no-preview-imports job) | Verify tests pass on both FCC 1.5.x + (later) 1.6.0-rc |
| 6-8 | — | Complete their migration; pin your consumer to migrated version |
The window matters because FCC's v1.6.0 removes the preview paths. If your consumer ships a migration + your downstream has not migrated, your v1.5-pinned release still works on v1.5.x, but the clock starts ticking toward v1.6.0 removal.
Coordination with KGSE¶
The Knowledge Graph Serializer Elevator (KGSE) persona shipped in v1.5.0 explicitly to coordinate future preview-to-stable promotions. KGSE's role responsibilities include:
- Tracking ice_ext
STATUS.md§N items that request upstream promotion. - Maintaining the queue of preview-namespace symbols that are candidates for elevation.
- Producing the quarterly list RC uses during roadmap review.
If your migration surfaces a symbol that is still preview-only and
should be stable (e.g. a helper function you rely on that has not
yet been promoted), open an issue citing KGSE in the
pattern-absorption-proposal template. KGSE will pick it up within a
release cycle. See adopting universal services, Step
4 for the full proposal workflow.
Checklist for "done"¶
-
grep -r "fcc.plugins.v1_5_preview" src/returns zero matches. -
grep -r "fcc.plugins.v1_5_preview" tests/returns zero matches (except in migration-specific tests that deliberately assert the preview path is deprecated). -
pyproject.tomlpinsfcc-agent-team-ext>=1.5.0,<2.0.0. - Lockfile regenerated.
- New identity tests (Step 7) pass locally + in CI.
-
DeprecationWarningsuppressions — if any — have a tracking issue open. - Consumer README + CHANGELOG updated to call out the migration.
Next steps¶
- Adopting universal services — the broader cross-ecosystem coordination picture.
- Integrating FCC into a mature codebase — CI + Helm + OTel for a first-time consumer.
- v1.5.0 API additions — the authoritative migration reference.
- ADR-008 POLARIS absorption — architectural context.
- ADR-009 Universal services coordination — why the bridge pattern exists.
- Codename decoder — POLARIS and LYRA entries.