These decisions answer which implementation choices remain load-bearing in the StoryMatrix source tree and what each choice means for runtime behavior.

🏛️ Clean-architecture layering

Decision: Domain code remains independent; Application depends on Domain; Infrastructure depends on both; Crew owns LLM orchestration (src/storymatrix/domain/, src/storymatrix/application/, src/storymatrix/infrastructure/, src/storymatrix/crew/).

Context: Domain entities and policies need no provider or framework imports, while use cases require abstract service capabilities and infrastructure supplies concrete adapters.

Consequence: The domain-isolation test rejects imports from storymatrix.config, storymatrix.application, storymatrix.infrastructure, and storymatrix.crew; adapter replacement occurs at the container boundary (tests/unit/domain/test_domain_isolation.py, src/storymatrix/infrastructure/container.py).

💤 Lazy dependency-injection imports

Decision: Heavy or optional implementations load inside provider factory functions and lambdas rather than at container-module import time (src/storymatrix/infrastructure/container.py).

Context: Optional multimedia stacks include C extensions such as torch and pydub, and a CPU-only host needs the application to start without importing every provider.

Consequence: dependency-injector providers construct only selected services, reducing startup coupling while preserving concrete adapter wiring (src/storymatrix/infrastructure/container.py).

🆔 Deterministic UUID5 identity

Decision: Identity helpers use UUID5 with stable namespaces and normalized name strings (src/storymatrix/domain/utils/identity.py).

Context: Repeated processing needs stable IDs for characters, segments, and file assets instead of random identifiers.

Consequence: STORYMATRIX_NAMESPACE is 6ba7b810-9dad-11d1-80b4-00c04fd430c8; ASSET_NAMESPACE is 1b4e28ba-2fa1-11d2-883f-0016d3cca427. Character IDs hash storymatrix_character_{normalized_name}; segment IDs hash storymatrix_segment|{scene_index}|{segment_index}|{segment_type}|{content.strip()}; asset IDs hash the file path under ASSET_NAMESPACE (src/storymatrix/domain/utils/identity.py).

🧮 CPU-only and resource-aware operation

Decision: Provider wiring supports CPU-safe and low-resource execution through lazy imports, local adapters, and APP__DEV_LOCAL_ONLY / APP__LOW_RESOURCE_MODE settings (src/storymatrix/infrastructure/container.py, src/storymatrix/config/models.py).

Context: Local hosts can lack GPU support and paid-provider credentials; optional C-extension packages and network providers cannot be unconditional startup requirements.

Consequence: The container can select mock or local services, and low-resource mode reduces concurrency and favors local speech providers (src/storymatrix/infrastructure/container.py, src/storymatrix/application/services/audio_production_service.py, src/storymatrix/cli/main.py).

⚙️ Pydantic v2 settings

Decision: Configuration models use pydantic-settings BaseSettings, ConfigDict, typed fields, and nested settings classes (src/storymatrix/config/models.py).

Context: StoryMatrix needs typed defaults, environment-prefix mapping, path and URL validation, and nested application, service, provider, and database configuration.

Consequence: Settings classes expose validated Python values and environment prefixes such as APP__, SERVICES__API__, and provider-specific prefixes; the application consumes one typed configuration tree (src/storymatrix/config/models.py).

💾 Checkpoint-based resumability

Decision: The production pipeline records completed stage class names and checkpoint metadata in checkpoint.json under the artifacts path (src/storymatrix/application/production/pipeline.py, src/storymatrix/application/production/context.py).

Context: Nine asynchronous stages produce intermediate state, and a failed run needs a deterministic restart boundary.

Consequence: Each successful stage saves a checkpoint, failures save one before re-raising, and --resume skips completed stage names in original order so execution begins at the first missing stage (src/storymatrix/application/production/pipeline.py).

🛑 Local-only cost gate

Decision: APP__DEV_LOCAL_ONLY=true drives provider selection toward mock or local implementations and blocks paid or external generation paths (src/storymatrix/config/models.py, src/storymatrix/infrastructure/container.py).

Context: Development and tests need reproducible runs without accidental paid API calls or network egress.

Consequence: DI selector keys choose mock/local services for LLM, image, audio search, TTS, SFX, and music paths according to each provider branch; MusicGen explicitly rejects local-only use (src/storymatrix/infrastructure/container.py, src/storymatrix/infrastructure/adapters/music/musicgen_adapter.py).

🎚️ FFmpeg montage service

Decision: Montage assembly uses an FFmpeg-backed service with configured ffmpeg and ffprobe executables (src/storymatrix/infrastructure/services/ffmpeg_montage.py, src/storymatrix/infrastructure/container.py).

Context: Final production combines narration, effects, music, timing, and output encoding into distributable media.

Consequence: The container exposes FFmpeg and pydub montage implementations behind a selector, while the FFmpeg service performs external media processing and probes media metadata (src/storymatrix/infrastructure/container.py, src/storymatrix/infrastructure/services/ffmpeg_montage.py).