From ffe7aff3f088e6be07ba418d8af896b7b917fc23 Mon Sep 17 00:00:00 2001 From: "agent-kurodo[bot]" <268466204+agent-kurodo[bot]@users.noreply.github.com> Date: Sat, 23 May 2026 03:40:03 +0000 Subject: [PATCH 1/4] =?UTF-8?q?refactor(config):=20rename=20Config=20?= =?UTF-8?q?=E2=86=92=20OrcapodConfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/orcapod/config.py | 16 +++++------ tests/test_orcapod_config.py | 55 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 tests/test_orcapod_config.py diff --git a/src/orcapod/config.py b/src/orcapod/config.py index f36a514a7..7a7260246 100644 --- a/src/orcapod/config.py +++ b/src/orcapod/config.py @@ -4,24 +4,24 @@ @dataclass(frozen=True) -class Config: - """Immutable configuration object.""" +class OrcapodConfig: + """Immutable OrcaPod configuration object.""" system_tag_hash_n_char: int = 12 schema_hash_n_char: int = 12 path_hash_n_char: int = 20 def with_updates(self, **kwargs) -> Self: - """Create a new Config instance with updated values.""" + """Create a new ``OrcapodConfig`` instance with updated values.""" return replace(self, **kwargs) - def merge(self, other: "Config") -> "Config": + def merge(self, other: "OrcapodConfig") -> "OrcapodConfig": """Merge with another config, other takes precedence.""" - if not isinstance(other, Config): - raise TypeError("Can only merge with another Config instance") + if not isinstance(other, OrcapodConfig): + raise TypeError("Can only merge with another OrcapodConfig instance") # Get all non-default values from other - defaults = Config() + defaults = OrcapodConfig() updates = {} for field_name in self.__dataclass_fields__: other_value = getattr(other, field_name) @@ -33,4 +33,4 @@ def merge(self, other: "Config") -> "Config": # Module-level default config - created at import time -DEFAULT_CONFIG = Config() +DEFAULT_CONFIG = OrcapodConfig() diff --git a/tests/test_orcapod_config.py b/tests/test_orcapod_config.py new file mode 100644 index 000000000..598de1d22 --- /dev/null +++ b/tests/test_orcapod_config.py @@ -0,0 +1,55 @@ +"""Tests for OrcapodConfig class naming (ENG-514).""" + + +class TestOrcapodConfigModule: + def test_can_import_orcapod_config_from_config_module(self): + from orcapod.config import OrcapodConfig # noqa: F401 + + def test_orcapod_config_is_instantiable_with_defaults(self): + from orcapod.config import OrcapodConfig + + cfg = OrcapodConfig() + assert cfg.system_tag_hash_n_char == 12 + assert cfg.schema_hash_n_char == 12 + assert cfg.path_hash_n_char == 20 + + def test_default_config_is_orcapod_config_instance(self): + from orcapod.config import DEFAULT_CONFIG, OrcapodConfig + + assert isinstance(DEFAULT_CONFIG, OrcapodConfig) + + def test_orcapod_config_with_updates(self): + from orcapod.config import OrcapodConfig + + cfg = OrcapodConfig() + updated = cfg.with_updates(system_tag_hash_n_char=8) + assert updated.system_tag_hash_n_char == 8 + assert cfg.system_tag_hash_n_char == 12 # original unchanged + + def test_orcapod_config_merge(self): + from orcapod.config import OrcapodConfig + + base = OrcapodConfig() + other = OrcapodConfig(system_tag_hash_n_char=8) + merged = base.merge(other) + assert merged.system_tag_hash_n_char == 8 + assert merged.schema_hash_n_char == 12 # unchanged default + + def test_orcapod_config_merge_type_error(self): + import pytest + + from orcapod.config import OrcapodConfig + + cfg = OrcapodConfig() + with pytest.raises(TypeError): + cfg.merge("not a config") # type: ignore[arg-type] + + +class TestOrcapodConfigTopLevelExport: + def test_can_import_orcapod_config_from_orcapod(self): + from orcapod import OrcapodConfig # noqa: F401 + + def test_orcapod_config_in_all(self): + import orcapod + + assert "OrcapodConfig" in orcapod.__all__ From e83d6baacb910f26de912185afd82e636c8110db Mon Sep 17 00:00:00 2001 From: "agent-kurodo[bot]" <268466204+agent-kurodo[bot]@users.noreply.github.com> Date: Sat, 23 May 2026 03:49:15 +0000 Subject: [PATCH 2/4] =?UTF-8?q?refactor(config):=20update=20all=20Config?= =?UTF-8?q?=20=E2=86=92=20OrcapodConfig=20references?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/orcapod/core/base.py | 10 +++++----- src/orcapod/core/data_function.py | 6 +++--- src/orcapod/core/datagrams/datagram.py | 4 ++-- src/orcapod/core/function_pod.py | 4 ++-- src/orcapod/core/nodes/function_node.py | 8 ++++---- src/orcapod/core/nodes/operator_node.py | 6 +++--- src/orcapod/core/nodes/source_node.py | 4 ++-- src/orcapod/core/operators/static_output_pod.py | 4 ++-- src/orcapod/core/sources/base.py | 4 ++-- src/orcapod/core/sources/cached_source.py | 4 ++-- src/orcapod/core/sources/db_table_source.py | 4 ++-- src/orcapod/core/sources/postgresql_table_source.py | 4 ++-- src/orcapod/core/sources/spiraldb_table_source.py | 4 ++-- src/orcapod/core/sources/sqlite_table_source.py | 4 ++-- src/orcapod/core/sources/stream_builder.py | 4 ++-- tests/test_core/operators/test_merge_join.py | 4 ++-- tests/test_core/operators/test_operators.py | 8 ++++---- 17 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/orcapod/core/base.py b/src/orcapod/core/base.py index 00c5fc096..836f25276 100644 --- a/src/orcapod/core/base.py +++ b/src/orcapod/core/base.py @@ -6,7 +6,7 @@ from typing import Any import orcapod.contexts as contexts -from orcapod.config import DEFAULT_CONFIG, Config +from orcapod.config import DEFAULT_CONFIG, OrcapodConfig from orcapod.types import ContentHash logger = logging.getLogger(__name__) @@ -75,7 +75,7 @@ class DataContextMixin: def __init__( self, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, **kwargs, ): super().__init__(**kwargs) @@ -85,7 +85,7 @@ def __init__( self._orcapod_config = config @property - def orcapod_config(self) -> Config: + def orcapod_config(self) -> OrcapodConfig: return self._orcapod_config @property @@ -117,7 +117,7 @@ class ContentIdentifiableBase(DataContextMixin, ABC): def __init__( self, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, **kwargs: Any, ) -> None: """ @@ -360,7 +360,7 @@ def __init__( self, label: str | None = None, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ): # Init provided here for explicit listing of parmeters super().__init__(label=label, data_context=data_context, config=config) diff --git a/src/orcapod/core/data_function.py b/src/orcapod/core/data_function.py index b599787c2..2648246bb 100644 --- a/src/orcapod/core/data_function.py +++ b/src/orcapod/core/data_function.py @@ -11,7 +11,7 @@ from uuid_utils import uuid7 -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.contexts import DataContext from orcapod.core.base import TraceableBase from orcapod.core.datagrams import Datagram, Data @@ -135,7 +135,7 @@ def __init__( version: str = "v0.0", label: str | None = None, data_context: str | DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, executor: DataFunctionExecutorProtocol | None = None, ): super().__init__(label=label, data_context=data_context, config=config) @@ -374,7 +374,7 @@ def __init__( output_schema: SchemaLike | Sequence[type] | None = None, label: str | None = None, data_context: str | DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, executor: PythonFunctionExecutorProtocol | None = None, ) -> None: diff --git a/src/orcapod/core/datagrams/datagram.py b/src/orcapod/core/datagrams/datagram.py index c12eebe67..77a0c6790 100644 --- a/src/orcapod/core/datagrams/datagram.py +++ b/src/orcapod/core/datagrams/datagram.py @@ -26,7 +26,7 @@ from uuid_utils import uuid7 from orcapod import contexts -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.core.base import ContentIdentifiableBase from orcapod.protocols.semantic_types_protocols import TypeConverterProtocol from orcapod.semantic_types import infer_python_schema_from_pylist_data @@ -68,7 +68,7 @@ def __init__( meta_info: Mapping[str, DataValue] | None = None, record_id: str | None = None, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ) -> None: if isinstance(data, pa.RecordBatch): data = pa.Table.from_batches([data]) diff --git a/src/orcapod/core/function_pod.py b/src/orcapod/core/function_pod.py index dc0ea8362..4c91fd2f3 100644 --- a/src/orcapod/core/function_pod.py +++ b/src/orcapod/core/function_pod.py @@ -9,7 +9,7 @@ from orcapod import contexts from orcapod.channels import ReadableChannel, WritableChannel -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.core.base import TraceableBase from orcapod.core.data_function import CachedDataFunction, PythonDataFunction from orcapod.core.streams.base import StreamBase @@ -65,7 +65,7 @@ def __init__( tracker_manager: TrackerManagerProtocol | None = None, label: str | None = None, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ) -> None: super().__init__( label=label, diff --git a/src/orcapod/core/nodes/function_node.py b/src/orcapod/core/nodes/function_node.py index 9254967cf..02b3da5e0 100644 --- a/src/orcapod/core/nodes/function_node.py +++ b/src/orcapod/core/nodes/function_node.py @@ -23,7 +23,7 @@ from orcapod import contexts from orcapod.channels import ReadableChannel, WritableChannel -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.core.cached_function_pod import CachedFunctionPod from orcapod.core.streams.arrow_table_stream import ArrowTableStream from orcapod.core.streams.base import StreamBase @@ -96,7 +96,7 @@ def __init__( input_stream: StreamProtocol, tracker_manager: TrackerManagerProtocol | None = None, label: str | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, table_scope: Literal["pipeline_hash", "content_hash"] = "pipeline_hash", ): if tracker_manager is None: @@ -450,7 +450,7 @@ def __init__( input_stream: StreamProtocol, tracker_manager: TrackerManagerProtocol | None = None, label: str | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, table_scope: Literal["pipeline_hash", "content_hash"] = "pipeline_hash", ): super().__init__( @@ -664,7 +664,7 @@ def __init__( input_stream: StreamProtocol, tracker_manager: TrackerManagerProtocol | None = None, label: str | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, # Optional DB params for persistent mode: pipeline_database: ArrowDatabaseProtocol | None = None, result_database: ArrowDatabaseProtocol | None = None, diff --git a/src/orcapod/core/nodes/operator_node.py b/src/orcapod/core/nodes/operator_node.py index a38fde042..84933a0f3 100644 --- a/src/orcapod/core/nodes/operator_node.py +++ b/src/orcapod/core/nodes/operator_node.py @@ -23,7 +23,7 @@ from orcapod import contexts from orcapod.channels import Channel, ReadableChannel, WritableChannel -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.core.operators.static_output_pod import StaticOutputOperatorPod from orcapod.core.streams.arrow_table_stream import ArrowTableStream from orcapod.core.streams.base import StreamBase @@ -74,7 +74,7 @@ def __init__( input_streams: Collection[StreamProtocol], tracker_manager: TrackerManagerProtocol | None = None, label: str | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, table_scope: Literal["pipeline_hash", "content_hash"] = "pipeline_hash", ) -> None: """Initialize the shared operator-node state. @@ -517,7 +517,7 @@ def __init__( input_streams: tuple[StreamProtocol, ...] | list[StreamProtocol], tracker_manager: TrackerManagerProtocol | None = None, label: str | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, # Optional DB params for persistent mode: pipeline_database: ArrowDatabaseProtocol | None = None, cache_mode: CacheMode = CacheMode.OFF, diff --git a/src/orcapod/core/nodes/source_node.py b/src/orcapod/core/nodes/source_node.py index 92ec12599..bdc65502e 100644 --- a/src/orcapod/core/nodes/source_node.py +++ b/src/orcapod/core/nodes/source_node.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any from orcapod import contexts -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.core.base import TraceableBase from orcapod.errors import SourceSpecMismatchError, UnboundSourceError from orcapod.protocols.core_protocols import DataProtocol, TagProtocol @@ -55,7 +55,7 @@ def __init__( data_schema: Schema, data_context: str | contexts.DataContext | None = None, label: str | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ) -> None: super().__init__(label=label, data_context=data_context, config=config) self._name = name diff --git a/src/orcapod/core/operators/static_output_pod.py b/src/orcapod/core/operators/static_output_pod.py index a3bcff694..b7c03fc20 100644 --- a/src/orcapod/core/operators/static_output_pod.py +++ b/src/orcapod/core/operators/static_output_pod.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any, cast from orcapod.channels import ReadableChannel, WritableChannel -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.contexts import DataContext from orcapod.core.base import TraceableBase from orcapod.core.streams.base import StreamBase @@ -269,7 +269,7 @@ def __init__( upstreams: tuple[StreamProtocol, ...] = (), label: str | None = None, data_context: DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ) -> None: self._pod = pod self._upstreams = upstreams diff --git a/src/orcapod/core/sources/base.py b/src/orcapod/core/sources/base.py index 7589e0828..41301b04b 100644 --- a/src/orcapod/core/sources/base.py +++ b/src/orcapod/core/sources/base.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any from orcapod import contexts -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.core.streams.base import StreamBase from orcapod.protocols.core_protocols import StreamProtocol from orcapod.types import ColumnConfig, Schema @@ -60,7 +60,7 @@ def __init__( source_id: str | None = None, label: str | None = None, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ) -> None: super().__init__( label=label, diff --git a/src/orcapod/core/sources/cached_source.py b/src/orcapod/core/sources/cached_source.py index 5b1e5a5cd..b924e0cdb 100644 --- a/src/orcapod/core/sources/cached_source.py +++ b/src/orcapod/core/sources/cached_source.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any from orcapod import contexts -from orcapod.config import Config +from orcapod.config import OrcapodConfig from orcapod.core.sources.base import RootSource from orcapod.core.streams.arrow_table_stream import ArrowTableStream from orcapod.protocols.core_protocols import DataProtocol, SourceProtocol, TagProtocol @@ -59,7 +59,7 @@ def __init__( source_id: str | None = None, label: str | None = None, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ) -> None: if data_context is None: data_context = source.data_context_key diff --git a/src/orcapod/core/sources/db_table_source.py b/src/orcapod/core/sources/db_table_source.py index 9e80d4630..725656cb2 100644 --- a/src/orcapod/core/sources/db_table_source.py +++ b/src/orcapod/core/sources/db_table_source.py @@ -23,7 +23,7 @@ import pyarrow as pa from orcapod import contexts - from orcapod.config import Config + from orcapod.config import OrcapodConfig from orcapod.protocols.db_connector_protocol import DBConnectorProtocol else: pa = LazyModule("pyarrow") @@ -69,7 +69,7 @@ def __init__( source_id: str | None = None, label: str | None = None, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, *, _query: str | None = None, ) -> None: diff --git a/src/orcapod/core/sources/postgresql_table_source.py b/src/orcapod/core/sources/postgresql_table_source.py index 9229789a7..0e026ff38 100644 --- a/src/orcapod/core/sources/postgresql_table_source.py +++ b/src/orcapod/core/sources/postgresql_table_source.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: from orcapod import contexts - from orcapod.config import Config + from orcapod.config import OrcapodConfig class PostgreSQLTableSource(DBTableSource): @@ -69,7 +69,7 @@ def __init__( source_id: str | None = None, label: str | None = None, data_context: str | contexts.DataContext | None = None, - config: Config | None = None, + config: OrcapodConfig | None = None, ) -> None: self._dsn = dsn # store before try — needed by to_config even if super() raises connector = PostgreSQLConnector(dsn) # outside try — if this raises, finally never runs diff --git a/src/orcapod/core/sources/spiraldb_table_source.py b/src/orcapod/core/sources/spiraldb_table_source.py index b7059ffcd..507c23e05 100644 --- a/src/orcapod/core/sources/spiraldb_table_source.py +++ b/src/orcapod/core/sources/spiraldb_table_source.py @@ -42,7 +42,7 @@ if TYPE_CHECKING: from orcapod import contexts - from orcapod.config import Config + from orcapod.config import OrcapodConfig class SpiralDBTableSource(DBTableSource): @@ -98,7 +98,7 @@ def __init__( source_id: str | None = None, label: str | None = None, data_context: "str | contexts.DataContext | None" = None, - config: "Config | None" = None, + config: "OrcapodConfig | None" = None, overrides: dict[str, str] | None = None, ) -> None: self._project_id = project_id diff --git a/src/orcapod/core/sources/sqlite_table_source.py b/src/orcapod/core/sources/sqlite_table_source.py index 82e6d4673..833636b30 100644 --- a/src/orcapod/core/sources/sqlite_table_source.py +++ b/src/orcapod/core/sources/sqlite_table_source.py @@ -29,7 +29,7 @@ if TYPE_CHECKING: from orcapod import contexts - from orcapod.config import Config + from orcapod.config import OrcapodConfig class SQLiteTableSource(DBTableSource): @@ -79,7 +79,7 @@ def __init__( source_id: str | None = None, label: str | None = None, data_context: str | contexts.DataContext | None = None, - config: "Config | None" = None, + config: "OrcapodConfig | None" = None, ) -> None: self._db_path = db_path connector = SQLiteConnector(db_path) diff --git a/src/orcapod/core/sources/stream_builder.py b/src/orcapod/core/sources/stream_builder.py index d790c64af..14a3f7d3a 100644 --- a/src/orcapod/core/sources/stream_builder.py +++ b/src/orcapod/core/sources/stream_builder.py @@ -21,7 +21,7 @@ if TYPE_CHECKING: import pyarrow as pa - from orcapod.config import Config + from orcapod.config import OrcapodConfig from orcapod.contexts import DataContext else: pa = LazyModule("pyarrow") @@ -59,7 +59,7 @@ class SourceStreamBuilder: config: Orcapod config (controls hash character counts). """ - def __init__(self, data_context: DataContext, config: Config) -> None: + def __init__(self, data_context: DataContext, config: OrcapodConfig) -> None: self._data_context = data_context self._config = config diff --git a/tests/test_core/operators/test_merge_join.py b/tests/test_core/operators/test_merge_join.py index 3be9c8ab6..9a84dc1b3 100644 --- a/tests/test_core/operators/test_merge_join.py +++ b/tests/test_core/operators/test_merge_join.py @@ -641,10 +641,10 @@ def test_two_system_tag_columns_produced(self, left_source, right_source): def test_system_tag_canonical_positions(self, left_source, right_source): """System tag columns should carry canonical position indices matching stable sort by pipeline_hash.""" - from orcapod.config import Config + from orcapod.config import OrcapodConfig from orcapod.system_constants import constants - n_char = Config().system_tag_hash_n_char + n_char = OrcapodConfig().system_tag_hash_n_char op = MergeJoin() result = op.static_process(left_source, right_source) diff --git a/tests/test_core/operators/test_operators.py b/tests/test_core/operators/test_operators.py index 483ed4376..0b47ce698 100644 --- a/tests/test_core/operators/test_operators.py +++ b/tests/test_core/operators/test_operators.py @@ -1313,11 +1313,11 @@ def test_system_tag_position_maps_to_correct_source(self, three_sources): - schema_hash matching the original source's schema_hash - stream_hash matching the input stream's pipeline_hash - canonical index matching the position""" - from orcapod.config import Config + from orcapod.config import OrcapodConfig from orcapod.system_constants import constants src_a, src_b, src_c = three_sources - n_char = Config().system_tag_hash_n_char + n_char = OrcapodConfig().system_tag_hash_n_char # Independently determine expected position → source mapping sources = [src_a, src_b, src_c] @@ -1408,11 +1408,11 @@ def test_intermediate_operators_produce_different_stream_hash(self): With an intermediate MapData, stream_hash comes from the DynamicPodStream which has a different pipeline_hash than the original source.""" - from orcapod.config import Config + from orcapod.config import OrcapodConfig from orcapod.core.sources.arrow_table_source import ArrowTableSource from orcapod.system_constants import constants - n_char = Config().system_tag_hash_n_char + n_char = OrcapodConfig().system_tag_hash_n_char src_a = ArrowTableSource( pa.table( From 3060c574911c09fdbf787a04f5287b8725fba24d Mon Sep 17 00:00:00 2001 From: "agent-kurodo[bot]" <268466204+agent-kurodo[bot]@users.noreply.github.com> Date: Sat, 23 May 2026 03:55:05 +0000 Subject: [PATCH 3/4] feat(config): export OrcapodConfig from top-level orcapod package --- src/orcapod/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/orcapod/__init__.py b/src/orcapod/__init__.py index 7e07bd1d2..f0ea3d234 100644 --- a/src/orcapod/__init__.py +++ b/src/orcapod/__init__.py @@ -1,3 +1,4 @@ +from .config import OrcapodConfig from .core.function_pod import ( FunctionPod, function_pod, @@ -14,6 +15,7 @@ from . import types # noqa: F401 __all__ = [ + "OrcapodConfig", "FunctionPod", "function_pod", "Pipeline", From 5616905db11eddd048f19616eb89059e106ed643 Mon Sep 17 00:00:00 2001 From: "agent-kurodo[bot]" <268466204+agent-kurodo[bot]@users.noreply.github.com> Date: Sat, 23 May 2026 04:05:09 +0000 Subject: [PATCH 4/4] chore(plans): add ENG-514 rename implementation plan --- ...26-05-23-rename-config-to-orcapodconfig.md | 542 ++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 superpowers/plans/2026-05-23-rename-config-to-orcapodconfig.md diff --git a/superpowers/plans/2026-05-23-rename-config-to-orcapodconfig.md b/superpowers/plans/2026-05-23-rename-config-to-orcapodconfig.md new file mode 100644 index 000000000..b78eda2e2 --- /dev/null +++ b/superpowers/plans/2026-05-23-rename-config-to-orcapodconfig.md @@ -0,0 +1,542 @@ +# Rename Config → OrcapodConfig Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use sensei:subagent-driven-development (recommended) or sensei:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Rename the `Config` class in `orcapod/config.py` to `OrcapodConfig` everywhere it appears — class definition, all internal usages, all test usages — and add it to the top-level public API. + +**Architecture:** Pure identifier rename with no logic changes. `config.py` is the single source of truth; all 15 consumer source files and 2 test files reference the class by import. The top-level `__init__.py` does not currently expose `Config`, but will expose `OrcapodConfig` after this rename. No backward-compat alias — project CLAUDE.md prohibits shims pre-v0.1.0. + +**Tech Stack:** Python 3.12+, `uv run` for all commands, `pytest` for tests. + +--- + +## File Map + +| Action | File | +|---|---| +| Modify | `src/orcapod/config.py` — rename class + update internal refs | +| Modify | `src/orcapod/__init__.py` — add OrcapodConfig to public API | +| Modify | `src/orcapod/core/base.py` — import + 3 type hints | +| Modify | `src/orcapod/core/datagrams/datagram.py` — import + 1 type hint | +| Modify | `src/orcapod/core/data_function.py` — import + 2 type hints | +| Modify | `src/orcapod/core/function_pod.py` — import + 1 type hint | +| Modify | `src/orcapod/core/nodes/function_node.py` — import + 3 type hints | +| Modify | `src/orcapod/core/nodes/operator_node.py` — import + 2 type hints | +| Modify | `src/orcapod/core/nodes/source_node.py` — import + 1 type hint | +| Modify | `src/orcapod/core/operators/static_output_pod.py` — import + 1 type hint | +| Modify | `src/orcapod/core/sources/base.py` — import + 1 type hint | +| Modify | `src/orcapod/core/sources/cached_source.py` — import + 1 type hint | +| Modify | `src/orcapod/core/sources/db_table_source.py` — TYPE_CHECKING import + 1 type hint | +| Modify | `src/orcapod/core/sources/postgresql_table_source.py` — TYPE_CHECKING import + 1 type hint | +| Modify | `src/orcapod/core/sources/spiraldb_table_source.py` — TYPE_CHECKING import + 1 type hint | +| Modify | `src/orcapod/core/sources/sqlite_table_source.py` — TYPE_CHECKING import + 1 type hint | +| Modify | `src/orcapod/core/sources/stream_builder.py` — TYPE_CHECKING import + 1 type hint | +| Modify | `tests/test_core/operators/test_operators.py` — 2 local imports + usages | +| Modify | `tests/test_core/operators/test_merge_join.py` — 1 local import + usage | +| Create | `tests/test_orcapod_config.py` — new tests for OrcapodConfig name | + +--- + +### Task 1: Write the Failing Test + +**Files:** +- Create: `tests/test_orcapod_config.py` + +- [ ] **Step 1: Write the failing test** + +```python +# tests/test_orcapod_config.py +"""Tests for OrcapodConfig class naming (ENG-514).""" + + +class TestOrcapodConfigModule: + def test_can_import_orcapod_config_from_config_module(self): + from orcapod.config import OrcapodConfig # noqa: F401 + + def test_orcapod_config_is_instantiable_with_defaults(self): + from orcapod.config import OrcapodConfig + + cfg = OrcapodConfig() + assert cfg.system_tag_hash_n_char == 12 + assert cfg.schema_hash_n_char == 12 + assert cfg.path_hash_n_char == 20 + + def test_default_config_is_orcapod_config_instance(self): + from orcapod.config import DEFAULT_CONFIG, OrcapodConfig + + assert isinstance(DEFAULT_CONFIG, OrcapodConfig) + + def test_orcapod_config_with_updates(self): + from orcapod.config import OrcapodConfig + + cfg = OrcapodConfig() + updated = cfg.with_updates(system_tag_hash_n_char=8) + assert updated.system_tag_hash_n_char == 8 + assert cfg.system_tag_hash_n_char == 12 # original unchanged + + def test_orcapod_config_merge(self): + from orcapod.config import OrcapodConfig + + base = OrcapodConfig() + other = OrcapodConfig(system_tag_hash_n_char=8) + merged = base.merge(other) + assert merged.system_tag_hash_n_char == 8 + assert merged.schema_hash_n_char == 12 # unchanged default + + def test_orcapod_config_merge_type_error(self): + import pytest + + from orcapod.config import OrcapodConfig + + cfg = OrcapodConfig() + with pytest.raises(TypeError): + cfg.merge("not a config") # type: ignore[arg-type] + + +class TestOrcapodConfigTopLevelExport: + def test_can_import_orcapod_config_from_orcapod(self): + from orcapod import OrcapodConfig # noqa: F401 + + def test_orcapod_config_in_all(self): + import orcapod + + assert "OrcapodConfig" in orcapod.__all__ +``` + +- [ ] **Step 2: Run test to verify it fails** + +```bash +cd /path/to/orcapod-python # replace with actual repo path +uv run pytest tests/test_orcapod_config.py -v +``` + +Expected: FAIL — `ImportError: cannot import name 'OrcapodConfig' from 'orcapod.config'` + +--- + +### Task 2: Rename Class in config.py + +**Files:** +- Modify: `src/orcapod/config.py` + +- [ ] **Step 1: Replace the entire file with the renamed version** + +Replace the full contents of `src/orcapod/config.py` with: + +```python +# config.py +from dataclasses import dataclass, replace +from typing import Self + + +@dataclass(frozen=True) +class OrcapodConfig: + """Immutable OrcaPod configuration object.""" + + system_tag_hash_n_char: int = 12 + schema_hash_n_char: int = 12 + path_hash_n_char: int = 20 + + def with_updates(self, **kwargs) -> Self: + """Create a new ``OrcapodConfig`` instance with updated values.""" + return replace(self, **kwargs) + + def merge(self, other: "OrcapodConfig") -> "OrcapodConfig": + """Merge with another config, other takes precedence.""" + if not isinstance(other, OrcapodConfig): + raise TypeError("Can only merge with another OrcapodConfig instance") + + # Get all non-default values from other + defaults = OrcapodConfig() + updates = {} + for field_name in self.__dataclass_fields__: + other_value = getattr(other, field_name) + default_value = getattr(defaults, field_name) + if other_value != default_value: + updates[field_name] = other_value + + return self.with_updates(**updates) + + +# Module-level default config - created at import time +DEFAULT_CONFIG = OrcapodConfig() +``` + +- [ ] **Step 2: Run the new tests to verify they pass (except the top-level export tests)** + +```bash +uv run pytest tests/test_orcapod_config.py::TestOrcapodConfigModule -v +``` + +Expected: All 6 tests in `TestOrcapodConfigModule` PASS. +`TestOrcapodConfigTopLevelExport` will still fail — that is expected until Task 4. + +- [ ] **Step 3: Commit** + +```bash +git add src/orcapod/config.py tests/test_orcapod_config.py +git commit -m "refactor(config): rename Config → OrcapodConfig" +``` + +--- + +### Task 3: Update All Consumer Source and Test Files + +**Files:** +- Modify: all 15 source files that import `Config` +- Modify: 2 test files that locally import `Config` + +Every file that imports `Config` from `orcapod.config` needs two kinds of edits: +1. The import line: `Config` → `OrcapodConfig` +2. Every type hint or usage: `Config` → `OrcapodConfig` + +`ColumnConfig`, `NodeConfig`, `PipelineConfig` are distinct classes and must **not** be changed. +The `sed` pattern `\bConfig\b` (whole-word match) safely renames only the bare word `Config`. + +- [ ] **Step 1: Update the 10 source files with direct (non-TYPE_CHECKING) imports** + +For each file listed below, make the two edits shown. + +**`src/orcapod/core/base.py`** — import line 9, type hints lines 78, 88, 120, 363: + +``` +Old: from orcapod.config import DEFAULT_CONFIG, Config +New: from orcapod.config import DEFAULT_CONFIG, OrcapodConfig +``` +Then replace every remaining `Config` (type hint only — `ColumnConfig`, `NodeConfig` are not present in this file): +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (lines 78, 120, 363) +- `def orcapod_config(self) -> Config:` → `def orcapod_config(self) -> OrcapodConfig:` (line 88) + +**`src/orcapod/core/datagrams/datagram.py`** — import line 29, type hint line 71: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 71) + +**`src/orcapod/core/data_function.py`** — import line 14, type hints lines 138, 377: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (lines 138, 377) + +**`src/orcapod/core/function_pod.py`** — import line 12, type hint line 68: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 68) + +**`src/orcapod/core/nodes/function_node.py`** — import line 26, type hints lines 99, 453, 667: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (lines 99, 453, 667) + +**`src/orcapod/core/nodes/operator_node.py`** — import line 26, type hints lines 77, 520: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (lines 77, 520) + +**`src/orcapod/core/nodes/source_node.py`** — import line 15, type hint line 58: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 58) + +**`src/orcapod/core/operators/static_output_pod.py`** — import line 11, type hint line 272: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 272) + +**`src/orcapod/core/sources/base.py`** — import line 8, type hint line 63: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 63) + +**`src/orcapod/core/sources/cached_source.py`** — import line 8, type hint line 62: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 62) + +- [ ] **Step 2: Update the 5 TYPE_CHECKING source files** + +These files import `Config` inside `if TYPE_CHECKING:` blocks. Same two edits each. + +**`src/orcapod/core/sources/db_table_source.py`** — TYPE_CHECKING import line 26, type hint line 72: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 72) + +**`src/orcapod/core/sources/postgresql_table_source.py`** — TYPE_CHECKING import line 22, type hint line 72: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config | None = None,` → `config: OrcapodConfig | None = None,` (line 72) + +**`src/orcapod/core/sources/spiraldb_table_source.py`** — TYPE_CHECKING import line 45, type hint line 101: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: "Config | None" = None,` → `config: "OrcapodConfig | None" = None,` (line 101) + +**`src/orcapod/core/sources/sqlite_table_source.py`** — TYPE_CHECKING import line 32, type hint line 82: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: "Config | None" = None,` → `config: "OrcapodConfig | None" = None,` (line 82) + +**`src/orcapod/core/sources/stream_builder.py`** — TYPE_CHECKING import line 24, type hint line 62: + +``` +Old: from orcapod.config import Config +New: from orcapod.config import OrcapodConfig +``` +- `config: Config` → `config: OrcapodConfig` (line 62) + +- [ ] **Step 3: Update the 2 test files with local Config imports** + +**`tests/test_core/operators/test_operators.py`** — 2 local imports at lines 1316 and 1411: + +At line 1316 (inside a test function body): +``` +Old: from orcapod.config import Config + ... + n_char = Config().system_tag_hash_n_char +New: from orcapod.config import OrcapodConfig + ... + n_char = OrcapodConfig().system_tag_hash_n_char +``` + +At line 1411 (inside a second test function body): +``` +Old: from orcapod.config import Config + ... + n_char = Config().system_tag_hash_n_char +New: from orcapod.config import OrcapodConfig + ... + n_char = OrcapodConfig().system_tag_hash_n_char +``` + +**`tests/test_core/operators/test_merge_join.py`** — 1 local import at line 644: + +``` +Old: from orcapod.config import Config + ... + n_char = Config().system_tag_hash_n_char +New: from orcapod.config import OrcapodConfig + ... + n_char = OrcapodConfig().system_tag_hash_n_char +``` + +- [ ] **Step 4: Run the full test suite to verify no regressions** + +```bash +uv run pytest tests/ -v --ignore=tests/test_orcapod_config.py::TestOrcapodConfigTopLevelExport -x +``` + +Expected: All existing tests PASS. The `TestOrcapodConfigTopLevelExport` class in `test_orcapod_config.py` will still fail — skip or ignore it for now; it passes after Task 4. + +- [ ] **Step 5: Commit** + +```bash +git add \ + src/orcapod/core/base.py \ + src/orcapod/core/datagrams/datagram.py \ + src/orcapod/core/data_function.py \ + src/orcapod/core/function_pod.py \ + src/orcapod/core/nodes/function_node.py \ + src/orcapod/core/nodes/operator_node.py \ + src/orcapod/core/nodes/source_node.py \ + src/orcapod/core/operators/static_output_pod.py \ + src/orcapod/core/sources/base.py \ + src/orcapod/core/sources/cached_source.py \ + src/orcapod/core/sources/db_table_source.py \ + src/orcapod/core/sources/postgresql_table_source.py \ + src/orcapod/core/sources/spiraldb_table_source.py \ + src/orcapod/core/sources/sqlite_table_source.py \ + src/orcapod/core/sources/stream_builder.py \ + tests/test_core/operators/test_operators.py \ + tests/test_core/operators/test_merge_join.py +git commit -m "refactor(config): update all Config → OrcapodConfig references" +``` + +--- + +### Task 4: Add OrcapodConfig to Top-Level __init__.py + +**Files:** +- Modify: `src/orcapod/__init__.py` + +- [ ] **Step 1: Add the import and __all__ entry** + +Current `src/orcapod/__init__.py`: + +```python +from .core.function_pod import ( + FunctionPod, + function_pod, +) +from .core.nodes.source_node import SourceNode +from .pipeline import Pipeline, PipelineJob + +# Subpackage re-exports for clean public API +from . import databases # noqa: F401 +from . import nodes # noqa: F401 +from . import operators # noqa: F401 +from . import sources # noqa: F401 +from . import streams # noqa: F401 +from . import types # noqa: F401 + +__all__ = [ + "FunctionPod", + "function_pod", + "Pipeline", + "PipelineJob", + "SourceNode", + "databases", + "nodes", + "operators", + "sources", + "streams", + "types", +] +``` + +New `src/orcapod/__init__.py` — add `OrcapodConfig` import after the existing imports and add it to `__all__`: + +```python +from .config import OrcapodConfig +from .core.function_pod import ( + FunctionPod, + function_pod, +) +from .core.nodes.source_node import SourceNode +from .pipeline import Pipeline, PipelineJob + +# Subpackage re-exports for clean public API +from . import databases # noqa: F401 +from . import nodes # noqa: F401 +from . import operators # noqa: F401 +from . import sources # noqa: F401 +from . import streams # noqa: F401 +from . import types # noqa: F401 + +__all__ = [ + "OrcapodConfig", + "FunctionPod", + "function_pod", + "Pipeline", + "PipelineJob", + "SourceNode", + "databases", + "nodes", + "operators", + "sources", + "streams", + "types", +] +``` + +- [ ] **Step 2: Run the full test suite including top-level export tests** + +```bash +uv run pytest tests/ -v -x +``` + +Expected: ALL tests PASS, including `TestOrcapodConfigTopLevelExport`. + +- [ ] **Step 3: Commit** + +```bash +git add src/orcapod/__init__.py +git commit -m "feat(config): export OrcapodConfig from top-level orcapod package" +``` + +--- + +### Task 5: Final Verification + +- [ ] **Step 1: Run the full test suite one final time** + +```bash +uv run pytest tests/ -v +``` + +Expected: All tests PASS with zero failures. + +- [ ] **Step 2: Verify the public API by hand** + +```bash +uv run python -c " +from orcapod import OrcapodConfig +from orcapod.config import OrcapodConfig, DEFAULT_CONFIG +cfg = OrcapodConfig(system_tag_hash_n_char=8) +print('OrcapodConfig:', cfg) +print('DEFAULT_CONFIG:', DEFAULT_CONFIG) +print('isinstance check:', isinstance(DEFAULT_CONFIG, OrcapodConfig)) +" +``` + +Expected output: +``` +OrcapodConfig: OrcapodConfig(system_tag_hash_n_char=8, schema_hash_n_char=12, path_hash_n_char=20) +DEFAULT_CONFIG: OrcapodConfig(system_tag_hash_n_char=12, schema_hash_n_char=12, path_hash_n_char=20) +isinstance check: True +``` + +- [ ] **Step 3: Confirm no stale `Config` references remain in source or tests** + +```bash +grep -rn '\bConfig\b' src/orcapod/ tests/ \ + --include="*.py" \ + | grep -v "ColumnConfig\|NodeConfig\|PipelineConfig\|CacheConfig\|DatabaseConfig\|# config\|_config\b" +``` + +Expected: Zero lines output. If any lines appear, fix them before creating the PR. + +--- + +## Self-Review Checklist + +- **Spec coverage:** + - ✅ Class definition site renamed (`config.py`) + - ✅ All internal usages — imports, type hints, isinstance checks, `__init__` defaults, factory functions (`config.py` `merge()` method, `DEFAULT_CONFIG`) + - ✅ Public re-export — `OrcapodConfig` added to top-level `orcapod.__init__` + - ✅ Tests and fixtures updated (`test_operators.py`, `test_merge_join.py`) + - ✅ New tests written (`test_orcapod_config.py`) + - ✅ No backward-compat alias (per project CLAUDE.md — greenfield pre-v0.1.0) + - ✅ All tests pass + +- **Out of scope (do NOT touch):** + - `ColumnConfig`, `NodeConfig`, `PipelineConfig` — distinct classes, unchanged + - Any restructuring of fields, defaults, or behavior of OrcapodConfig