You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+12-3Lines changed: 12 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
---
9
9
10
+
## [0.15.1] - 2026-03-31
11
+
12
+
### Changed
13
+
14
+
-**Env prefix convention simplified** — Removed the `^APCORE_[A-Z0-9]` reservation rule from namespace registration. Sub-packages now use single-underscore prefixes (`APCORE_MCP`, `APCORE_OBSERVABILITY`, `APCORE_SYS`) instead of the double-underscore form. The longest-prefix-match dispatch algorithm already disambiguates correctly; the previous restriction was unnecessary.
@@ -16,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
25
-**Namespace Registration (§9.5)** — `Config.register_namespace(name, schema, env_prefix, defaults)` API with cross-language examples (Python, TypeScript, Rust, Go, Java). Global (class-level) registry shared across Config instances. Late registration permitted with explicit `reload()` to apply. No `unregister_namespace` in this version
17
26
-**Unified Configuration File (§9.6)** — Single YAML file with namespace-partitioned sections. Automatic mode detection: legacy mode (no `apcore:` key, fully backward compatible) vs namespace mode (`apcore:` key present). `_config` reserved namespace for meta-configuration (`strict`, `allow_unknown`)
18
27
-**Mount Mechanism (§9.7)** — `config.mount(namespace, from_file|from_dict)` for attaching external configuration sources without requiring a unified file. Primary integration path for third-party projects with existing config systems
19
-
-**Per-Namespace Env Override (§9.8)** — Each namespace declares its own `env_prefix`. Longest-prefix-match dispatch algorithm resolves ambiguity. `APCORE__MCP` double-underscore convention for apcore sub-packages to avoid collision with `APCORE_` prefix. Compatibility note for apflow's simpler env convention
28
+
-**Per-Namespace Env Override (§9.8)** — Each namespace declares its own `env_prefix`. Longest-prefix-match dispatch algorithm resolves ambiguity. `APCORE_MCP` double-underscore convention for apcore sub-packages to avoid collision with `APCORE_` prefix. Compatibility note for apflow's simpler env convention
20
29
-**Namespace-Aware Access API (§9.9)** — `config.get("namespace.key.path")` with dot-path namespace resolution algorithm. `config.namespace(name)` for full subtree retrieval. `config.bind(ns, type)` / `config.get_typed(path, type)` for typed access. `Config.registered_namespaces()` for introspection
21
30
-**Validation Algorithm A12-NS (§9.10)** — Extended A12 for namespace mode: validates `apcore` namespace with original algorithm, validates registered namespaces against their JSON Schema, handles unknown namespaces per strict/allow_unknown settings
22
31
-**Hot-Reload Namespace Support (§9.11)** — `config.reload()` re-reads YAML, re-detects mode, re-applies namespace defaults and env overrides, re-validates, and re-reads mounted files
@@ -32,8 +41,8 @@ Three mechanisms addressing ecosystem consistency across apcore, apcore-mcp, apc
32
41
-**Error Formatter Registry (§8.8)** — Shared `ErrorFormatter` protocol and registration point. apcore-mcp and apcore-a2a each independently implement protocol-specific error mappers (MCP camelCase/sanitization, A2A JSON-RPC code mapping); this registry makes the contract explicit and discoverable. Adoption is SHOULD-level for ecosystem adapters — apcore does not ship adapter-specific formatters. New error code: `ERROR_FORMATTER_DUPLICATE`
33
42
34
43
-**apcore Built-in Namespace Registrations (§9.15)** — The framework pre-registers two namespaces for its own subsystems, applying the Config Bus pattern to apcore's own internal configuration. Both promote existing flat keys already present in apcore-python's `config.py`; migration is 1:1 with no breaking changes:
35
-
-**`observability`** (`APCORE__OBSERVABILITY`) — Extracts `apcore.observability.*` flat keys (tracing, metrics, logging, error_history, platform_notify) into a dedicated namespace. Adapter packages (apcore-mcp, apcore-a2a, apcore-cli) **should** read from this namespace rather than using independent logging defaults
36
-
-**`sys_modules`** (`APCORE__SYS`) — Promotes `apcore.sys_modules.*` flat keys into a dedicated namespace. `register_sys_modules()` prefers `config.namespace("sys_modules")` in namespace mode with `config.get("sys_modules.*")` legacy fallback
44
+
-**`observability`** (`APCORE_OBSERVABILITY`) — Extracts `apcore.observability.*` flat keys (tracing, metrics, logging, error_history, platform_notify) into a dedicated namespace. Adapter packages (apcore-mcp, apcore-a2a, apcore-cli) **should** read from this namespace rather than using independent logging defaults
45
+
-**`sys_modules`** (`APCORE_SYS`) — Promotes `apcore.sys_modules.*` flat keys into a dedicated namespace. `register_sys_modules()` prefers `config.namespace("sys_modules")` in namespace mode with `config.get("sys_modules.*")` legacy fallback
37
46
38
47
-**Event Type Naming and Collision Fix (§9.16)** — Resolves two confirmed collisions in apcore-python's emitted event types:
39
48
-`"module_health_changed"` was used for two distinct events (toggle on/off vs. error rate recovery); replaced by canonical names `apcore.module.toggled` and `apcore.health.recovered`
|`schema`| MAY | JSON Schema document (inline object or file path). When provided, the namespace section is validated against this schema during `Config.validate()`. When `nil`, the namespace is registered for isolation and env override only — no structural validation is performed. |
4494
-
|`env_prefix`| MAY | Uppercase prefix for environment variable overrides (e.g., `APFLOW`). When `nil`, no environment variable overrides are applied for this namespace. Must match pattern: `^[A-Z][A-Z0-9]*(_[A-Z0-9]+|__[A-Z][A-Z0-9]*)*$`. The `__` (double-underscore) form is used to avoid collision with the `APCORE_` prefix (e.g., `APCORE__MCP`). |
4494
+
|`env_prefix`| MAY | Uppercase prefix for environment variable overrides (e.g., `APFLOW`). When `nil`, no environment variable overrides are applied for this namespace. Must match pattern: `^[A-Z][A-Z0-9]*(_[A-Z0-9]+|__[A-Z][A-Z0-9]*)*$`. The `__` (double-underscore) form is used to avoid collision with the `APCORE_` prefix (e.g., `APCORE_MCP`). |
4495
4495
|`defaults`| MAY | Default configuration values for this namespace. Merged before file data (lowest priority). |
Env prefix conflicts arise when one registered prefix is a string prefix of another, making it ambiguous which namespace owns a given env var. The following rules prevent this:
4943
4943
4944
4944
1. Each `env_prefix`**must** be unique across all registered namespaces. Attempting to register a duplicate `env_prefix`**must** raise `CONFIG_ENV_PREFIX_CONFLICT`.
4945
-
2. Any `env_prefix` that starts with `APCORE_` (i.e., matches `^APCORE_[A-Z0-9]`) **must** raise `CONFIG_ENV_PREFIX_CONFLICT`. This prevents collision with the `apcore` namespace's `APCORE_` prefix. The double-underscore form (`^APCORE__[A-Z]`, e.g., `APCORE__MCP`) is explicitly permitted and dispatched via longest-prefix-match (see dispatch algorithm below).
4945
+
2. Any `env_prefix` that starts with `APCORE_` (i.e., matches `^APCORE_[A-Z0-9]`) **must** raise `CONFIG_ENV_PREFIX_CONFLICT`. This prevents collision with the `apcore` namespace's `APCORE_` prefix. The double-underscore form (`^APCORE_[A-Z]`, e.g., `APCORE_MCP`) is explicitly permitted and dispatched via longest-prefix-match (see dispatch algorithm below).
4946
4946
3. The prefix `APCORE` is reserved for the `apcore` namespace. Attempting to register it for another namespace **must** raise `CONFIG_NAMESPACE_RESERVED`.
4947
4947
4948
-
**Resolving the `APCORE` / `APCORE__MCP` ambiguity:**
4948
+
**Resolving the `APCORE` / `APCORE_MCP` ambiguity:**
4949
4949
4950
4950
A naive prefix scheme would make `APCORE_MCP` (for apcore-mcp) collide with `APCORE_` (for apcore, key path `mcp.*`). The ecosystem convention resolves this by requiring apcore ecosystem packages to use a double-underscore separator between `APCORE` and the sub-package name in their env prefix:
4951
4951
4952
4952
| Package | Namespace | Env Prefix | Why safe |
4953
4953
|---------|-----------|------------|----------|
4954
4954
| apcore |`apcore`|`APCORE`| Base prefix |
4955
-
| apcore-mcp |`apcore-mcp`|`APCORE__MCP`|`APCORE__MCP_` is not a valid `APCORE_` key (double `__` creates invalid path) |
4956
-
| apcore-a2a |`apcore-a2a`|`APCORE__A2A`| Same reasoning |
4957
-
| apcore-cli |`apcore-cli`|`APCORE__CLI`| Same reasoning |
4955
+
| apcore-mcp |`apcore-mcp`|`APCORE_MCP`|`APCORE_MCP_` is not a valid `APCORE_` key (double `__` creates invalid path) |
4956
+
| apcore-a2a |`apcore-a2a`|`APCORE_A2A`| Same reasoning |
4957
+
| apcore-cli |`apcore-cli`|`APCORE_CLI`| Same reasoning |
| django-apcore |`django-apcore`|`DJANGO_APCORE`| No `DJANGO` namespace registered — no prefix collision |
4960
4960
4961
-
This works because the `APCORE_` prefix matcher stops at the first `_` boundary. An env var like `APCORE__MCP_TRANSPORT` starts with `APCORE__` (double underscore), which the `APCORE_` prefix handler would interpret as key path `apcore._mcp.transport` — not a valid apcore config path. The `APCORE__MCP_` prefix handler correctly claims it.
4961
+
This works because the `APCORE_` prefix matcher stops at the first `_` boundary. An env var like `APCORE_MCP_TRANSPORT` starts with `APCORE_` (double underscore), which the `APCORE_` prefix handler would interpret as key path `apcore._mcp.transport` — not a valid apcore config path. The `APCORE_MCP_` prefix handler correctly claims it.
4962
4962
4963
4963
However, this convention introduces complexity. Implementations **must** use **longest-prefix-match** when dispatching env vars to namespaces:
| apcore-mcp |`apcore-mcp`|`APCORE__MCP`| Yes — `APCORE_` is a prefix of `APCORE_MCP_`; use `APCORE__MCP` to disambiguate |`apcore-mcp.schema.json`|
5269
-
| apcore-a2a |`apcore-a2a`|`APCORE__A2A`| Same as above |`apcore-a2a.schema.json`|
5270
-
| apcore-cli |`apcore-cli`|`APCORE__CLI`| Same as above |`apcore-cli.schema.json`|
5268
+
| apcore-mcp |`apcore-mcp`|`APCORE_MCP`| Yes — `APCORE_` is a prefix of `APCORE_MCP_`; use `APCORE_MCP` to disambiguate |`apcore-mcp.schema.json`|
5269
+
| apcore-a2a |`apcore-a2a`|`APCORE_A2A`| Same as above |`apcore-a2a.schema.json`|
5270
+
| apcore-cli |`apcore-cli`|`APCORE_CLI`| Same as above |`apcore-cli.schema.json`|
5271
5271
| apflow |`apflow`|`APFLOW`| No — disjoint from `APCORE_`|`apflow.schema.json`|
5272
5272
| django-apcore |`django-apcore`|`DJANGO_APCORE`| No — no `DJANGO` namespace registered |`django-apcore.schema.json`|
5273
5273
| fastapi-apcore |`fastapi-apcore`|`FASTAPI_APCORE`| No — no `FASTAPI` namespace registered |`fastapi-apcore.schema.json`|
5274
5274
| flask-apcore |`flask-apcore`|`FLASK_APCORE`| No — no `FLASK` namespace registered |`flask-apcore.schema.json`|
5275
5275
| nestjs-apcore |`nestjs-apcore`|`NESTJS_APCORE`| No — no `NESTJS` namespace registered |`nestjs-apcore.schema.json`|
5276
5276
| axum-apcore |`axum-apcore`|`AXUM_APCORE`| No — no `AXUM` namespace registered |`axum-apcore.schema.json`|
5277
5277
5278
-
> **Why `APCORE__MCP` and not `APCORE_MCP`?** The prefix `APCORE_` (for the `apcore` namespace) is a string prefix of `APCORE_MCP_`. An env var like `APCORE_MCP_TRANSPORT` is ambiguous: does it set `apcore → mcp.transport` or `apcore-mcp → transport`? The double-underscore convention (`APCORE__MCP_`) breaks the ambiguity because `APCORE_` never matches `APCORE__MCP_TRANSPORT` as an apcore key (the double underscore creates an invalid key path `_mcp.transport`). Framework integrations like `DJANGO_APCORE` do not have this problem because no `DJANGO` namespace is registered, so `DJANGO_APCORE_*` is unambiguous.
5278
+
> **Why `APCORE_MCP` and not `APCORE_MCP`?** The prefix `APCORE_` (for the `apcore` namespace) is a string prefix of `APCORE_MCP_`. An env var like `APCORE_MCP_TRANSPORT` is ambiguous: does it set `apcore → mcp.transport` or `apcore-mcp → transport`? The double-underscore convention (`APCORE_MCP_`) breaks the ambiguity because `APCORE_` never matches `APCORE_MCP_TRANSPORT` as an apcore key (the double underscore creates an invalid key path `_mcp.transport`). Framework integrations like `DJANGO_APCORE` do not have this problem because no `DJANGO` namespace is registered, so `DJANGO_APCORE_*` is unambiguous.
5279
5279
5280
5280
#### 9.13.2 Third-Party Package Integration
5281
5281
@@ -5416,7 +5416,7 @@ Extracts the existing `observability.*` flat keys from the `apcore` namespace in
**Migration:**`register_sys_modules()`**must** prefer `config.namespace("sys_modules")` in namespace mode, falling back to `config.get("sys_modules.*")` in legacy mode. No breaking change.
Copy file name to clipboardExpand all lines: README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -880,7 +880,7 @@ apcore-a2a:
880
880
881
881
Each package registers its own namespace with `Config.register_namespace()`. Third-party projects can also participate via `config.mount()` without modifying their existing configuration files. See the protocol spec for the full integration spectrum — from zero-coupling to full unification.
882
882
883
-
**Environment variable overrides** work per namespace: `APCORE_EXECUTOR_DEFAULT__TIMEOUT=5000` for apcore, `APFLOW_API_TIMEOUT=60` for apflow, `APCORE__MCP_PORT=9000` for apcore-mcp.
883
+
**Environment variable overrides** work per namespace: `APCORE_EXECUTOR_DEFAULT__TIMEOUT=5000` for apcore, `APFLOW_API_TIMEOUT=60` for apflow, `APCORE_MCP_PORT=9000` for apcore-mcp.
Env vars are dispatched to namespaces using **longest-prefix-match**. Sort all registered `envPrefix` values by length descending; the first match wins.
- Double `__` in the suffix → literal `_` in the key
79
79
- Single `_` in the suffix → `.` separator in the key
80
80
81
-
**Reserved prefix:** Any env var matching `APCORE_[A-Z0-9]` is reserved for apcore's legacy flat-key override scheme and cannot be used as a namespace `envPrefix`. Double-underscore `APCORE__` prefixes are allowed for apcore sub-package namespaces.
81
+
**Reserved prefix:** Any env var matching `APCORE_[A-Z0-9]` is reserved for apcore's legacy flat-key override scheme and cannot be used as a namespace `envPrefix`. Double-underscore `APCORE_` prefixes are allowed for apcore sub-package namespaces.
82
82
83
83
## Namespace Access
84
84
@@ -198,8 +198,8 @@ apcore pre-registers two namespaces at startup:
0 commit comments