circuitforge-core/docs/developer/adding-module.md
pyr0ball 383897f990
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
Release — PyPI / release (push) Waiting to run
feat: platforms module + docs + scripts
- platforms/: eBay platform adapter (snipe integration layer)
- docs/: developer guide, module reference, getting-started docs
- scripts/: utility scripts for development and deployment
2026-04-24 15:23:16 -07:00

3.8 KiB

Adding a Module to cf-core

This guide walks through extracting a pattern from a product into a shared cf-core module. The goal is to move battle-tested implementations here once they've stabilized in at least two products.

When to add a module

Add a module when:

  • The same pattern exists in two or more products with minor variations
  • The interface is stable enough that changing it would require coordinated updates across products
  • The code has no product-specific business logic baked in

Do not add a module for:

  • One-off utilities that only one product needs
  • Anything still in active design flux
  • Product-specific configuration or policy decisions

Module structure

circuitforge_core/
└── mymodule/
    ├── __init__.py        # Public API — what products import
    ├── base.py            # Core implementation
    └── backends/          # Optional: pluggable backends
        ├── __init__.py
        ├── local.py
        └── cloud.py

Keep the public API in __init__.py clean. Products should import from circuitforge_core.mymodule, not from internal submodules.

Step 1: Define the interface

Write the public interface first — the classes and functions products will call. Get this right before implementing, because changing it requires updating every product shim.

# circuitforge_core/mymodule/__init__.py

from .base import MyThing, get_my_thing

__all__ = ["MyThing", "get_my_thing"]

Step 2: Implement with a stub

Start with a minimal working implementation. Stub out anything uncertain:

# circuitforge_core/mymodule/base.py

class MyThing:
    def __init__(self, config: dict):
        self._config = config

    def do_thing(self, input: str) -> str:
        raise NotImplementedError("Override in product or backend")

Step 3: Write tests

Tests go in circuitforge_core/tests/test_mymodule.py. Use pytest. The cf env has pytest installed.

conda run -n cf python -m pytest tests/test_mymodule.py -v

Cover:

  • Happy path with realistic input
  • Missing config / bad input (fail loudly, not silently)
  • Cloud vs local mode if applicable

Step 4: Update pyproject.toml

Add any new dependencies:

[project.optional-dependencies]
mymodule = ["some-dep>=1.0"]

Use optional dependency groups so products that don't use the module don't pay the install cost.

Step 5: Write the docs page

Add docs/modules/mymodule.md following the pattern of the existing module docs. Include:

  • Import path
  • Why this module exists / design rationale
  • Full public API with examples
  • Any gotchas or non-obvious behavior
  • Status (Stable / Stub)

Update docs/modules/index.md and mkdocs.yml to include the new page.

Step 6: Update products

In each product that uses the pattern:

  1. Add a shim if the product needs to override behavior
  2. Replace the inline implementation with imports from cf-core
  3. Run the product's tests

The shim pattern:

# myproduct/app/mything.py
from circuitforge_core.mymodule import get_my_thing as _base_get_my_thing
from .config import get_settings

def get_my_thing():
    settings = get_settings()
    return _base_get_my_thing(config=settings.mything_config)

Licensing boundary

The module's license depends on what it does:

Code License
Discovery, pipeline, data access MIT
LLM inference, AI features, fine-tuned model access BSL 1.1
Anything that would give SaaS competitors a free AI product BSL 1.1

When in doubt, BSL 1.1. See the licensing guide for the full decision tree.

Versioning

cf-core uses semantic versioning. Adding a new module with a stable API is a minor version bump. Breaking an existing interface is a major bump and requires coordinated updates to all products.

Update pyproject.toml and CHANGELOG.md before merging.