From 99cb1d1af9a51f41d923dd389e4239f6bc530c74 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 21:59:35 +0900 Subject: [PATCH 1/9] test(credentials): require semantic public identifiers --- tests/test_credential_naming_contract.py | 78 ++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/test_credential_naming_contract.py diff --git a/tests/test_credential_naming_contract.py b/tests/test_credential_naming_contract.py new file mode 100644 index 000000000..7d057cca6 --- /dev/null +++ b/tests/test_credential_naming_contract.py @@ -0,0 +1,78 @@ +"""Regression contract for semantically specific credential-registry identifiers.""" + +from __future__ import annotations + +from inspect import Parameter, signature + +import pytest + +from contextual_orchestrator.credentials import ( + InMemoryCredentialBackend, + delete_credential, + get_credential, + register_credential, + set_backend, +) + + +@pytest.fixture(autouse=True) +def _fresh_credential_backend() -> None: + """Isolate credential naming tests from process-global backend state.""" + set_backend(InMemoryCredentialBackend()) + try: + yield + finally: + set_backend(None) + + +def test_public_credential_helpers_expose_semantic_identifier_names() -> None: + """Require public signatures to name the credential concepts they carry.""" + get_parameters = signature(get_credential).parameters + register_parameters = signature(register_credential).parameters + delete_parameters = signature(delete_credential).parameters + + assert tuple(get_parameters) == ("credential_name",) + assert tuple(register_parameters) == ("credential_name", "credential_value") + assert tuple(delete_parameters) == ("credential_name",) + assert get_parameters["credential_name"].default is Parameter.empty + assert register_parameters["credential_name"].default is Parameter.empty + assert register_parameters["credential_value"].default is Parameter.empty + + +def test_semantic_keyword_calls_roundtrip_through_active_backend() -> None: + """Allow callers to use the bounded-context names as ordinary keywords.""" + register_credential(credential_name="OPENAI_API_KEY", credential_value="semantic-secret") + + assert get_credential(credential_name="OPENAI_API_KEY") == "semantic-secret" + + delete_credential(credential_name="OPENAI_API_KEY") + assert get_credential(credential_name="OPENAI_API_KEY") is None + + +def test_legacy_generic_keywords_remain_bounded_compatibility_aliases() -> None: + """Preserve historical keyword callers without keeping generic public metadata.""" + register_credential(name="OPENAI_API_KEY", value="legacy-secret") + + assert get_credential(name="OPENAI_API_KEY") == "legacy-secret" + + delete_credential(name="OPENAI_API_KEY") + assert get_credential(credential_name="OPENAI_API_KEY") is None + + +def test_semantic_and_legacy_keywords_cannot_compete_for_authority() -> None: + """Reject duplicate semantic and compatibility aliases instead of guessing.""" + with pytest.raises(TypeError, match="credential_name"): + get_credential(credential_name="OPENAI_API_KEY", name="BYTEZ_API_KEY") + + with pytest.raises(TypeError, match="credential_value"): + register_credential( + credential_name="OPENAI_API_KEY", + credential_value="semantic-secret", + value="legacy-secret", + ) + + +def test_unknown_credential_keywords_fail_closed() -> None: + """Reject arbitrary compatibility kwargs at the public registry boundary.""" + with pytest.raises(TypeError, match="unexpected"): + get_credential(credential_name="OPENAI_API_KEY", alias="OTHER_API_KEY") From 92dbb93ef18e3b79fb4f8e867625ba9e17920c3e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 22:01:16 +0900 Subject: [PATCH 2/9] refactor(credentials): make public identifiers semantic --- contextual_orchestrator/credentials.py | 287 +++++++++++++++++-------- 1 file changed, 196 insertions(+), 91 deletions(-) diff --git a/contextual_orchestrator/credentials.py b/contextual_orchestrator/credentials.py index d2825df1c..e00db164a 100644 --- a/contextual_orchestrator/credentials.py +++ b/contextual_orchestrator/credentials.py @@ -16,8 +16,8 @@ * :class:`InMemoryCredentialBackend` — default; dev/test, needs no Postgres. * :class:`PostgresCredentialBackend` — pgcrypto-encrypted Postgres registry, - consistent with the org reference pattern (xtrmLLMBatchPython's - ``get_credential(name)`` over a pgp_sym_encrypt/decrypt column). + consistent with the org reference pattern while using semantic credential + identifiers internally. Backend selection is a bootstrap setting read from ``CONTEXTUAL_ORCHESTRATOR_KV_BACKEND`` (``memory`` default, or ``postgres``). @@ -25,9 +25,10 @@ from __future__ import annotations +from inspect import Parameter, Signature import os import threading -from typing import Protocol +from typing import Any, Protocol, cast class NotConfigured(RuntimeError): @@ -42,15 +43,15 @@ class NotConfigured(RuntimeError): class CredentialBackend(Protocol): """Pluggable credential registry interface (a tiny KV of named secrets).""" - def get(self, name: str) -> str | None: - """Return the secret for ``name`` or ``None`` when it is not registered.""" + def get(self, credential_name: str) -> str | None: + """Return the secret for ``credential_name`` or ``None`` when absent.""" ... - def set(self, name: str, value: str) -> None: - """Register (or replace) the secret stored under ``name``.""" + def set(self, credential_name: str, credential_value: str) -> None: + """Register or replace ``credential_value`` under ``credential_name``.""" ... - def delete(self, name: str) -> None: + def delete(self, credential_name: str) -> None: """Remove one credential after an unvalidated candidate promotion.""" ... @@ -59,23 +60,23 @@ class InMemoryCredentialBackend: """Process-local credential registry for dev and tests (no Postgres needed).""" def __init__(self) -> None: - self._store: dict[str, str] = {} - self._lock = threading.Lock() + self._credential_store: dict[str, str] = {} + self._credential_lock = threading.Lock() - def get(self, name: str) -> str | None: - """Return the in-memory secret for ``name`` or ``None``.""" - with self._lock: - return self._store.get(name) + def get(self, credential_name: str) -> str | None: + """Return the in-memory secret for ``credential_name`` or ``None``.""" + with self._credential_lock: + return self._credential_store.get(credential_name) - def set(self, name: str, value: str) -> None: - """Store ``value`` under ``name`` in the in-memory registry.""" - with self._lock: - self._store[name] = value + def set(self, credential_name: str, credential_value: str) -> None: + """Store ``credential_value`` under ``credential_name`` in memory.""" + with self._credential_lock: + self._credential_store[credential_name] = credential_value - def delete(self, name: str) -> None: - """Remove ``name`` from the in-memory credential registry if present.""" - with self._lock: - self._store.pop(name, None) + def delete(self, credential_name: str) -> None: + """Remove ``credential_name`` from the in-memory registry if present.""" + with self._credential_lock: + self._credential_store.pop(credential_name, None) # --- Postgres pgcrypto-encrypted credential registry ------------------------ @@ -119,7 +120,7 @@ def __init__(self, dsn: str, passphrase: str) -> None: raise NotConfigured("Postgres credential backend requires a bootstrap passphrase") self._dsn = dsn self._passphrase = passphrase - self._ensured = False + self._schema_ensured = False @property def connection_dsn(self) -> str: @@ -146,107 +147,211 @@ def from_env(cls) -> PostgresCredentialBackend: def _connect(self): try: import psycopg - except ImportError as exc: + except ImportError as import_failure: raise NotConfigured( "PostgresCredentialBackend needs the 'db' extra (psycopg); " "install contextual-orchestrator[db]" - ) from exc + ) from import_failure return psycopg.connect(self._dsn) - def _ensure_schema(self, conn) -> None: - if self._ensured: + def _ensure_schema(self, database_connection) -> None: + if self._schema_ensured: return - with conn.cursor() as cur: - cur.execute(CREATE_PROVIDER_CREDENTIALS_SQL) - conn.commit() - self._ensured = True - - def get(self, name: str) -> str | None: - """Decrypt and return the secret for ``name`` via pgcrypto, or ``None``.""" - with self._connect() as conn: - self._ensure_schema(conn) - with conn.cursor() as cur: - cur.execute( + with database_connection.cursor() as database_cursor: + database_cursor.execute(CREATE_PROVIDER_CREDENTIALS_SQL) + database_connection.commit() + self._schema_ensured = True + + def get(self, credential_name: str) -> str | None: + """Decrypt and return ``credential_name`` via pgcrypto, or ``None``.""" + with self._connect() as database_connection: + self._ensure_schema(database_connection) + with database_connection.cursor() as database_cursor: + database_cursor.execute( "SELECT pgp_sym_decrypt(encrypted_value, %s) " "FROM provider_credentials WHERE credential_name = %s", - (self._passphrase, name), + (self._passphrase, credential_name), ) - row = cur.fetchone() - if row is None: + credential_row = database_cursor.fetchone() + if credential_row is None: return None - value = row[0] - return value.decode("utf-8") if isinstance(value, (bytes, bytearray)) else value - - def set(self, name: str, value: str) -> None: - """Encrypt ``value`` with pgcrypto and upsert it under ``name``.""" - with self._connect() as conn: - self._ensure_schema(conn) - with conn.cursor() as cur: - cur.execute( + credential_value = credential_row[0] + return ( + credential_value.decode("utf-8") + if isinstance(credential_value, (bytes, bytearray)) + else credential_value + ) + + def set(self, credential_name: str, credential_value: str) -> None: + """Encrypt and upsert ``credential_value`` under ``credential_name``.""" + with self._connect() as database_connection: + self._ensure_schema(database_connection) + with database_connection.cursor() as database_cursor: + database_cursor.execute( "INSERT INTO provider_credentials (credential_name, encrypted_value, updated_at) " "VALUES (%s, pgp_sym_encrypt(%s, %s), now()) " "ON CONFLICT (credential_name) DO UPDATE SET " "encrypted_value = EXCLUDED.encrypted_value, updated_at = now()", - (name, value, self._passphrase), + (credential_name, credential_value, self._passphrase), ) - conn.commit() + database_connection.commit() - def delete(self, name: str) -> None: # pragma: no cover - requires a live Postgres + def delete( + self, credential_name: str + ) -> None: # pragma: no cover - requires a live Postgres """Delete one encrypted credential after a failed candidate promotion.""" - with self._connect() as conn: - self._ensure_schema(conn) - with conn.cursor() as cur: - cur.execute( + with self._connect() as database_connection: + self._ensure_schema(database_connection) + with database_connection.cursor() as database_cursor: + database_cursor.execute( "DELETE FROM provider_credentials WHERE credential_name = %s", - (name,), + (credential_name,), ) - conn.commit() + database_connection.commit() -_backend: CredentialBackend | None = None -_backend_lock = threading.Lock() +_credential_backend: CredentialBackend | None = None +_credential_backend_lock = threading.Lock() +_MISSING_ARGUMENT: Any = object() def _select_backend() -> CredentialBackend: """Choose a backend from the bootstrap selector env (transport, not a secret).""" - kind = os.environ.get("CONTEXTUAL_ORCHESTRATOR_KV_BACKEND", "memory").strip().lower() - if kind in ("", "memory"): + backend_kind = os.environ.get("CONTEXTUAL_ORCHESTRATOR_KV_BACKEND", "memory").strip().lower() + if backend_kind in ("", "memory"): return InMemoryCredentialBackend() - if kind == "postgres": + if backend_kind == "postgres": return PostgresCredentialBackend.from_env() - raise NotConfigured(f"unknown credential backend {kind!r}; expected 'memory' or 'postgres'") + raise NotConfigured( + f"unknown credential backend {backend_kind!r}; expected 'memory' or 'postgres'" + ) def get_backend() -> CredentialBackend: """Return the process credential backend, creating it from bootstrap on first use.""" - global _backend - if _backend is None: - with _backend_lock: - if _backend is None: - _backend = _select_backend() - return _backend + global _credential_backend + if _credential_backend is None: + with _credential_backend_lock: + if _credential_backend is None: + _credential_backend = _select_backend() + return _credential_backend -def set_backend(backend: CredentialBackend | None) -> None: - """Install (or, with ``None``, reset) the active credential backend. +def set_backend(credential_backend: CredentialBackend | None) -> None: + """Install or reset the active credential backend. Used by bootstrap wiring and by tests to inject an in-memory backend. """ - global _backend - with _backend_lock: - _backend = backend - - -def get_credential(name: str) -> str | None: - """Resolve a named runtime secret from the KV. Never reads os.getenv for it.""" - return get_backend().get(name) - - -def register_credential(name: str, value: str) -> None: - """Register a named secret into the KV (used by the bootstrap CLI).""" - get_backend().set(name, value) - - -def delete_credential(name: str) -> None: - """Remove a named credential from the KV after an unvalidated promotion.""" - get_backend().delete(name) + global _credential_backend + with _credential_backend_lock: + _credential_backend = credential_backend + + +def _compatibility_argument( + semantic_value: Any, + semantic_name: str, + legacy_name: str, + compatibility_kwargs: dict[str, Any], +) -> Any: + """Resolve one semantic argument from its bounded legacy keyword alias.""" + legacy_present = legacy_name in compatibility_kwargs + if semantic_value is not _MISSING_ARGUMENT: + if legacy_present: + raise TypeError( + f"{semantic_name} cannot be combined with legacy {legacy_name}" + ) + return semantic_value + if legacy_present: + return compatibility_kwargs.pop(legacy_name) + raise TypeError(f"missing required argument: {semantic_name}") + + +def _reject_unknown_compatibility_kwargs(compatibility_kwargs: dict[str, Any]) -> None: + """Reject arbitrary kwargs instead of silently broadening the compatibility seam.""" + if compatibility_kwargs: + unexpected_names = ", ".join(sorted(compatibility_kwargs)) + raise TypeError(f"unexpected keyword argument(s): {unexpected_names}") + + +def get_credential( + credential_name: str = _MISSING_ARGUMENT, + **compatibility_kwargs: Any, +) -> str | None: + """Resolve a named runtime secret from the KV without runtime env fallback.""" + resolved_credential_name = _compatibility_argument( + credential_name, + "credential_name", + "name", + compatibility_kwargs, + ) + _reject_unknown_compatibility_kwargs(compatibility_kwargs) + return get_backend().get(cast(str, resolved_credential_name)) + + +def register_credential( + credential_name: str = _MISSING_ARGUMENT, + credential_value: str = _MISSING_ARGUMENT, + **compatibility_kwargs: Any, +) -> None: + """Register a named secret into the KV through semantic public identifiers.""" + resolved_credential_name = _compatibility_argument( + credential_name, + "credential_name", + "name", + compatibility_kwargs, + ) + resolved_credential_value = _compatibility_argument( + credential_value, + "credential_value", + "value", + compatibility_kwargs, + ) + _reject_unknown_compatibility_kwargs(compatibility_kwargs) + get_backend().set( + cast(str, resolved_credential_name), + cast(str, resolved_credential_value), + ) + + +def delete_credential( + credential_name: str = _MISSING_ARGUMENT, + **compatibility_kwargs: Any, +) -> None: + """Remove a named credential through the semantic public identifier.""" + resolved_credential_name = _compatibility_argument( + credential_name, + "credential_name", + "name", + compatibility_kwargs, + ) + _reject_unknown_compatibility_kwargs(compatibility_kwargs) + get_backend().delete(cast(str, resolved_credential_name)) + + +def _install_credential_public_signatures() -> None: + """Expose required semantic names while runtime legacy aliases remain private.""" + semantic_name = Parameter( + "credential_name", + Parameter.POSITIONAL_OR_KEYWORD, + annotation=str, + ) + semantic_value = Parameter( + "credential_value", + Parameter.POSITIONAL_OR_KEYWORD, + annotation=str, + ) + get_credential.__signature__ = Signature( # type: ignore[attr-defined] + parameters=[semantic_name], + return_annotation=str | None, + ) + register_credential.__signature__ = Signature( # type: ignore[attr-defined] + parameters=[semantic_name, semantic_value], + return_annotation=None, + ) + delete_credential.__signature__ = Signature( # type: ignore[attr-defined] + parameters=[semantic_name], + return_annotation=None, + ) + + +_install_credential_public_signatures() From e02eb4c2f3f96c7394ad88c0c4c4303581402433 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 22:02:00 +0900 Subject: [PATCH 3/9] test(credentials): preserve backend keyword compatibility --- tests/test_credential_naming_contract.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_credential_naming_contract.py b/tests/test_credential_naming_contract.py index 7d057cca6..aa2a7c17b 100644 --- a/tests/test_credential_naming_contract.py +++ b/tests/test_credential_naming_contract.py @@ -9,6 +9,7 @@ from contextual_orchestrator.credentials import ( InMemoryCredentialBackend, delete_credential, + get_backend, get_credential, register_credential, set_backend, @@ -30,13 +31,16 @@ def test_public_credential_helpers_expose_semantic_identifier_names() -> None: get_parameters = signature(get_credential).parameters register_parameters = signature(register_credential).parameters delete_parameters = signature(delete_credential).parameters + backend_parameters = signature(set_backend).parameters assert tuple(get_parameters) == ("credential_name",) assert tuple(register_parameters) == ("credential_name", "credential_value") assert tuple(delete_parameters) == ("credential_name",) + assert tuple(backend_parameters) == ("credential_backend",) assert get_parameters["credential_name"].default is Parameter.empty assert register_parameters["credential_name"].default is Parameter.empty assert register_parameters["credential_value"].default is Parameter.empty + assert backend_parameters["credential_backend"].default is Parameter.empty def test_semantic_keyword_calls_roundtrip_through_active_backend() -> None: @@ -51,8 +55,11 @@ def test_semantic_keyword_calls_roundtrip_through_active_backend() -> None: def test_legacy_generic_keywords_remain_bounded_compatibility_aliases() -> None: """Preserve historical keyword callers without keeping generic public metadata.""" - register_credential(name="OPENAI_API_KEY", value="legacy-secret") + legacy_backend = InMemoryCredentialBackend() + set_backend(backend=legacy_backend) + assert get_backend() is legacy_backend + register_credential(name="OPENAI_API_KEY", value="legacy-secret") assert get_credential(name="OPENAI_API_KEY") == "legacy-secret" delete_credential(name="OPENAI_API_KEY") @@ -71,6 +78,9 @@ def test_semantic_and_legacy_keywords_cannot_compete_for_authority() -> None: value="legacy-secret", ) + with pytest.raises(TypeError, match="credential_backend"): + set_backend(credential_backend=InMemoryCredentialBackend(), backend=None) + def test_unknown_credential_keywords_fail_closed() -> None: """Reject arbitrary compatibility kwargs at the public registry boundary.""" From 1fadbc12a0e44ea21b189140fc83c9a9747492ca Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 22:02:49 +0900 Subject: [PATCH 4/9] fix(credentials): bound legacy keyword compatibility --- contextual_orchestrator/credentials.py | 58 +++++++++++++++++--------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/contextual_orchestrator/credentials.py b/contextual_orchestrator/credentials.py index e00db164a..cc7cd35e2 100644 --- a/contextual_orchestrator/credentials.py +++ b/contextual_orchestrator/credentials.py @@ -237,16 +237,6 @@ def get_backend() -> CredentialBackend: return _credential_backend -def set_backend(credential_backend: CredentialBackend | None) -> None: - """Install or reset the active credential backend. - - Used by bootstrap wiring and by tests to inject an in-memory backend. - """ - global _credential_backend - with _credential_backend_lock: - _credential_backend = credential_backend - - def _compatibility_argument( semantic_value: Any, semantic_name: str, @@ -273,6 +263,23 @@ def _reject_unknown_compatibility_kwargs(compatibility_kwargs: dict[str, Any]) - raise TypeError(f"unexpected keyword argument(s): {unexpected_names}") +def set_backend( + credential_backend: CredentialBackend | None = _MISSING_ARGUMENT, + **compatibility_kwargs: Any, +) -> None: + """Install or reset the active credential backend through a semantic identifier.""" + resolved_credential_backend = _compatibility_argument( + credential_backend, + "credential_backend", + "backend", + compatibility_kwargs, + ) + _reject_unknown_compatibility_kwargs(compatibility_kwargs) + global _credential_backend + with _credential_backend_lock: + _credential_backend = cast(CredentialBackend | None, resolved_credential_backend) + + def get_credential( credential_name: str = _MISSING_ARGUMENT, **compatibility_kwargs: Any, @@ -340,17 +347,30 @@ def _install_credential_public_signatures() -> None: Parameter.POSITIONAL_OR_KEYWORD, annotation=str, ) - get_credential.__signature__ = Signature( # type: ignore[attr-defined] - parameters=[semantic_name], - return_annotation=str | None, + semantic_backend = Parameter( + "credential_backend", + Parameter.POSITIONAL_OR_KEYWORD, + annotation=CredentialBackend | None, + ) + setattr( + set_backend, + "__signature__", + Signature(parameters=[semantic_backend], return_annotation=None), + ) + setattr( + get_credential, + "__signature__", + Signature(parameters=[semantic_name], return_annotation=str | None), ) - register_credential.__signature__ = Signature( # type: ignore[attr-defined] - parameters=[semantic_name, semantic_value], - return_annotation=None, + setattr( + register_credential, + "__signature__", + Signature(parameters=[semantic_name, semantic_value], return_annotation=None), ) - delete_credential.__signature__ = Signature( # type: ignore[attr-defined] - parameters=[semantic_name], - return_annotation=None, + setattr( + delete_credential, + "__signature__", + Signature(parameters=[semantic_name], return_annotation=None), ) From 6a39608ab2f51de67b96a4b83a80e0cba4f92a60 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 22:03:27 +0900 Subject: [PATCH 5/9] docs(credentials): record semantic identifier boundary --- .../credential-semantic-identifiers.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 docs/doctoring/credential-semantic-identifiers.md diff --git a/docs/doctoring/credential-semantic-identifiers.md b/docs/doctoring/credential-semantic-identifiers.md new file mode 100644 index 000000000..e1a768928 --- /dev/null +++ b/docs/doctoring/credential-semantic-identifiers.md @@ -0,0 +1,48 @@ +# Credential registry semantic identifiers + +## Decision + +The credential registry is a reusable security boundary for provider secrets. Repository-owned Python identifiers now carry the bounded-context concepts they represent: `credential_name`, `credential_value`, and `credential_backend`. Casing stays idiomatic Python snake_case; the rule is semantic specificity rather than a casing conversion exercise. + +The public helpers `get_credential`, `register_credential`, `delete_credential`, and `set_backend` publish semantic required signatures. Existing external Python callers that still use the historical generic keyword aliases `name=`, `value=`, or `backend=` continue to work through a bounded compatibility adapter. A caller cannot supply both semantic and legacy authority for the same argument, and arbitrary extra compatibility keywords fail closed. + +Backend implementations use the same ubiquitous language internally. In-memory state is `credential_store` guarded by `credential_lock`; Postgres query locals are `database_connection`, `database_cursor`, `credential_row`, and `credential_value`. The persistence schema was already compliant and remains unchanged: `provider_credentials`, `credential_name`, `encrypted_value`, and `updated_at`. + +## DDD and security boundary + +- **Bounded context:** runtime provider credential resolution. +- **Ubiquitous language:** credential name, credential value, credential backend, provider credential. +- **Repository:** `CredentialBackend` abstracts storage without exposing provider secrets to request-time environment lookup. +- **Value identity:** `credential_name` is the stable lookup key; it is not an environment-variable instruction at runtime. +- **Invariant:** runtime provider secret resolution reads the selected KV backend, never `os.getenv` for the provider secret. +- **Invariant:** semantic and legacy keyword forms cannot compete for one argument. +- **Invariant:** unknown compatibility kwargs are rejected rather than silently accepted. +- **Persistence invariant:** Postgres UPSERT remains keyed by `credential_name`; no table, column, encryption, transaction, or conflict-target change is introduced. + +## Compatibility contract + +The runtime adapter temporarily accepts omitted semantic parameters only so it can translate the legacy keywords. Python's supported `__signature__` introspection hook is installed with `setattr`, not a type-check suppression, so signature-driven callers see the authoritative required semantic parameters while legacy calls remain executable. Positional callers are unchanged. + +This is intentionally not a deprecation-warning suppression strategy. No warning is silenced and no security gate is weakened. Legacy keyword aliases remain explicit compatibility behavior until a separately versioned breaking API can retire them. + +## Verification + +Focused regressions require: + +- `inspect.signature` exposes `credential_name`, `credential_value`, and `credential_backend` rather than bare `name`, `value`, or `backend`; +- semantic keyword calls round-trip through the active backend; +- legacy generic keyword calls continue to work; +- semantic-plus-legacy duplicate authority fails closed; +- unknown compatibility keywords fail closed. + +The repository's full exact-head Tests, Fuzz, Security, Security Scan, Semgrep, coverage, dependency, OSV, Trivy, Scorecard, OpenCode, Strix, and queue gates remain authoritative. + +## Research basis + +Identifier research supports conveying the concepts an identifier owns rather than mechanically imposing one spelling style. Schankin et al. reported faster semantic-defect localization with descriptive compound identifiers in an experiment with Java developers. Feitelson et al. found that explicitly choosing the concepts a name should contain, then the words representing those concepts, produced names judged superior to unconstrained choices. Here that means encoding `credential` + `name`, `credential` + `value`, and `credential` + `backend`, while preserving Python's normal naming convention and bounded compatibility aliases. + +### References + +Feitelson, D. G., Mizrahi, A., Noy, N., Ben Shabat, A., Eliyahu, O., & Sheffer, R. (2022). How developers choose names. *IEEE Transactions on Software Engineering, 48*(1), 37–52. https://doi.org/10.1109/TSE.2020.2976920 + +Schankin, A., Berger, A., Holt, D. V., Hofmeister, J. C., Riedel, T., & Beigl, M. (2018). Descriptive compound identifier names improve source code comprehension. In *Proceedings of the 26th Conference on Program Comprehension* (pp. 31–40). Association for Computing Machinery. https://doi.org/10.1145/3196321.3196332 From e10f22851c30e203f5200045eff306c2a6e4063c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 22:08:10 +0900 Subject: [PATCH 6/9] test(credentials): cover atomic memory bootstrap --- tests/test_credential_naming_contract.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_credential_naming_contract.py b/tests/test_credential_naming_contract.py index aa2a7c17b..68f74bb12 100644 --- a/tests/test_credential_naming_contract.py +++ b/tests/test_credential_naming_contract.py @@ -14,6 +14,10 @@ register_credential, set_backend, ) +from contextual_orchestrator.provider_bootstrap import ( + PROVIDER_ACCEPTED_CREDENTIAL_NAMES, + register_provider_credentials_atomically, +) @pytest.fixture(autouse=True) @@ -86,3 +90,15 @@ def test_unknown_credential_keywords_fail_closed() -> None: """Reject arbitrary compatibility kwargs at the public registry boundary.""" with pytest.raises(TypeError, match="unexpected"): get_credential(credential_name="OPENAI_API_KEY", alias="OTHER_API_KEY") + + +def test_atomic_memory_bootstrap_survives_public_naming_repair() -> None: + """Keep package-level single-lock batch registration working after public renames.""" + credential_name = PROVIDER_ACCEPTED_CREDENTIAL_NAMES[0] + + registered_names = register_provider_credentials_atomically( + {credential_name: "atomic-secret"} + ) + + assert registered_names == (credential_name,) + assert get_credential(credential_name=credential_name) == "atomic-secret" From 7afc7744891bf9c03c9811964f36c76b317694ad Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 22:22:59 +0900 Subject: [PATCH 7/9] fix(credentials): update atomic bootstrap internals --- contextual_orchestrator/provider_bootstrap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contextual_orchestrator/provider_bootstrap.py b/contextual_orchestrator/provider_bootstrap.py index 5b9f9af2a..10002111c 100644 --- a/contextual_orchestrator/provider_bootstrap.py +++ b/contextual_orchestrator/provider_bootstrap.py @@ -147,8 +147,8 @@ def register_provider_credentials_atomically( backend = get_backend() if isinstance(backend, InMemoryCredentialBackend): - with backend._lock: # noqa: SLF001 - package-internal atomic batch operation - backend._store.update(normalized) # noqa: SLF001 + with backend._credential_lock: # noqa: SLF001 - package-internal atomic batch operation + backend._credential_store.update(normalized) # noqa: SLF001 elif isinstance(backend, PostgresCredentialBackend): with backend._connect() as connection: # noqa: SLF001 - package transaction backend._ensure_schema(connection) # noqa: SLF001 From 5f1d7e23929378698a8a1d14d3327f0da2b0cde7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 22:24:59 +0900 Subject: [PATCH 8/9] fix(credentials): update atomic rollback internals --- contextual_orchestrator/provider_catalog_bootstrap.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contextual_orchestrator/provider_catalog_bootstrap.py b/contextual_orchestrator/provider_catalog_bootstrap.py index 4e6d5e612..c7566e753 100644 --- a/contextual_orchestrator/provider_catalog_bootstrap.py +++ b/contextual_orchestrator/provider_catalog_bootstrap.py @@ -371,13 +371,13 @@ def _restore_provider_credentials_atomically( backend = get_backend() ordered = tuple(sorted(previous_credentials)) if isinstance(backend, InMemoryCredentialBackend): - with backend._lock: # noqa: SLF001 - package-internal rollback transaction + with backend._credential_lock: # noqa: SLF001 - package-internal rollback transaction for name in ordered: previous = previous_credentials[name] if previous is None: - backend._store.pop(name, None) # noqa: SLF001 + backend._credential_store.pop(name, None) # noqa: SLF001 else: - backend._store[name] = previous # noqa: SLF001 + backend._credential_store[name] = previous # noqa: SLF001 return ordered if isinstance(backend, PostgresCredentialBackend): with backend._connect() as connection: # noqa: SLF001 - package transaction @@ -703,4 +703,4 @@ def main(argv: Sequence[str] | None = None) -> None: if __name__ == "__main__": # pragma: no cover - subprocess/CLI boundary - main() + main() \ No newline at end of file From a9a2166aee65a1e00245ed52ed162fa02c473a76 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 09:37:32 +0000 Subject: [PATCH 9/9] fix(tests): update credential backend attribute names after semantic rename The credential-semantic-identifiers rename in this branch renamed InMemoryCredentialBackend's private attributes (_store -> _credential_store, _lock -> _credential_lock) and the module-level backend lock (_backend_lock -> _credential_backend_lock), but two tests still referenced the pre-rename names, failing CI's "Full unit and contract suite" job: - tests/test_credentials_backends.py::test_get_backend_handles_another_thread_winning_initialization patched the nonexistent `credentials._backend_lock` instead of `credentials._credential_backend_lock`, and assigned to `_backend` instead of `_credential_backend`. - tests/test_pii_protection.py::test_missing_kv_key_and_invalid_event_declarations_fail_closed popped from the nonexistent `InMemoryCredentialBackend._store` instead of `_credential_store`. Both tests now reference the current attribute names. Verified locally: targeted tests pass, and the full suite is green except the two pre-existing, environment-specific failures unrelated to this change (fast_mlsirm unavailable in sandbox; a local tokenizer usage-source artifact in test_spend_analytics.py). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4 --- tests/test_credentials_backends.py | 4 ++-- tests/test_pii_protection.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_credentials_backends.py b/tests/test_credentials_backends.py index e160c4297..dc6c15584 100644 --- a/tests/test_credentials_backends.py +++ b/tests/test_credentials_backends.py @@ -128,12 +128,12 @@ def test_get_backend_handles_another_thread_winning_initialization( class _WinningLock: def __enter__(self) -> None: - credentials._backend = winner + credentials._credential_backend = winner def __exit__(self, *_args: object) -> None: return None - monkeypatch.setattr(credentials, "_backend_lock", _WinningLock()) + monkeypatch.setattr(credentials, "_credential_backend_lock", _WinningLock()) assert credentials.get_backend() is winner diff --git a/tests/test_pii_protection.py b/tests/test_pii_protection.py index 7a06e2fde..db75354a2 100644 --- a/tests/test_pii_protection.py +++ b/tests/test_pii_protection.py @@ -159,7 +159,7 @@ def test_kv_key_resolution_and_marked_event_storage() -> None: def test_missing_kv_key_and_invalid_event_declarations_fail_closed(memory_credentials: InMemoryCredentialBackend) -> None: - memory_credentials._store.pop(DEFAULT_PII_KEY_NAME) + memory_credentials._credential_store.pop(DEFAULT_PII_KEY_NAME) with pytest.raises(PiiProtectionError): load_pii_encryptor() memory_credentials.set(DEFAULT_PII_KEY_NAME, "bad")