All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
-
Decision D-24 —
Context.create()signature unified across all SDKs (#66). Reduced to the six caller-supplied fields only:identity,trace_parent,cancel_token,data,services,global_deadline. Two prior inputs are removed from the public factory surface:executor— Executor self-binds at pipeline entry under the new normative §"Contract: Executor binding to Context" (docs/features/core-executor.md). This unifies three previously distinct binding scenarios under one rule: local construction viaContext.create(), cross-process deserialize, and hot-reload survivor restore.caller_id— zero production / test / doc callers across the 25-repo ecosystem. Top-level Contexts always havecaller_id = null; the value is managed exclusively byContext.child(). Reserved name for future revisions.
Added
cancel_tokenas a first-class parameter, eliminating the post-hocctx.cancel_token = tokenanti-pattern documented in 9 production sites acrossapcore-mcp-{python,typescript},apcore-typescript/async-task.ts(which had to cast awayreadonly),axum-apcore,django-apcore,fastapi-apcore.Rust
TraceParentstruct gains atracestate: Vec<(String, String)>field to align with Python/TS shape; the redundantContextBuilder::tracestate()setter is removed.Two new normative sections clarify distributed semantics:
- §"Contract: Distributed cancellation" —
cancel_tokenis local-only; cross-process cancellation MUST go through out-of-band channels (e.g., AsyncTaskStore task_id lookup). - §"Contract:
global_deadlinedistributed semantics" —global_deadlinedoes not propagate across process boundaries; callers needing wall-clock deadline propagation SHOULD store it incontext.dataunder an extension key.
Conformance fixture
context_create.jsonvalidates cross-SDK parameter parity, removal ofexecutor/caller_id, idempotent same-executor rebinding, and cross-executor conflict behavior.
-
Decision D-17 —
TaskStoreis async across all SDKs (docs/features/async-tasks.md§1.1). Pluggable backends like Redis or SQL cannot satisfy a syncTaskStorecontract without blocking the runtime's event loop. New normative: everyTaskStoremethod MUST be asynchronous in Python (async def), TypeScript (returnsPromise<T>), and Rust (async fnvia#[async_trait]).InMemoryTaskStoreMUST still expose async signatures even though its operations are CPU-only — uniform shape lets stores compose generically. Supersedes the partially-sync contract present in apcore-python and apcore-typescript through v0.21.x. Found via/apcore-skills:sync(finding A-D-AT-04). -
Decision D-18 —
cancel()MUST be a real interrupt across SDKs (docs/features/async-tasks.md§Cancellation Integration). Cooperative-flag-only cancellation is non-conforming. In TypeScript specifically,CancelTokenMUST be backed by anAbortControllerand MUST exposesignal: AbortSignalonContextso modules usingfetch/setTimeout/ Web Streams participate in real abort. Closes the cross-SDK divergence where Pythonasyncio.Task.cancel()and Rusttokio handle.abort()interrupted in-flight work but the TS flag-only path silently completed module side effects. Found via finding A-D-AT-02. -
Decision D-19 —
call_with_trace/callWithTracesharescall()error semantics (docs/features/core-executor.md§Trace Variants). The trace variant MUST run the sameon_errormiddleware chain, apply the same cancellation short-circuit (D-20), and apply the sameMiddlewareChainErrorunwrap (D-22). The trace is the observable record of execution — including any middleware recovery — not a sanitized projection. Closes the divergence where TS+Rustcall_with_tracerethrew unconditionally while Python ranon_errorrecovery. Found via finding A-D-EXEC-004. -
Decision D-20 — Cancellation short-circuits the
on_errorchain (docs/features/core-executor.md§Cancellation Short-Circuit).ExecutionCancelledErrorMUST be detected after pipeline-error unwrap and propagated directly, bypassingon_error. Rationale: cancellation is a caller-driven request to stop, not a recoverable failure; allowingon_errormiddleware to observe it lets logging middleware swallow it or retry middleware reissue aRetrySignalthat restarts the loop. Closes the gap in apcore-rust where missing short-circuit allowed middleware to recover cancellation. Found via finding A-D-EXEC-003. -
Decision D-21 — Cancel token MUST be checked at two pipeline points (
docs/features/core-executor.md§Cancel Token Mid-Pipeline Check). The pipeline MUST observecancel_tokencancellation at Step 2 (Call-Chain Guard) and again at Step 8 (Execute), in addition to honoring it insidemodule.execute()itself. Single-check implementations leak compute (the pipeline runs ACL/middleware/validation even though the caller has already cancelled) and are non-conforming. Closes the divergence where TS checked at step 2 but Python+Rust only checked at step 8 (and Rust missed step 8 entirely). Found via finding A-D-EXEC-002. -
Decision D-22 —
MiddlewareChainErrorMUST be unwrapped before propagation (docs/features/core-executor.md§Error Unwrap Rule). When a middleware (before/after/on_error) raises a domain-typed error likeApprovalDeniedError, the chain machinery may wrap it inMiddlewareChainErrorfor diagnostics. The executor MUST unwrap before propagating, surfacing the original typed cause. SDKs MUST NOT replace the cause with a genericModuleExecuteError. Closes the gap where Python wrapped toModuleExecuteErrorwhile TS+Rust unwrapped — breaking MCP/A2A bridges that key off the typed error. Found via finding A-D-EXEC-005. -
Decision D-23 —
get_status/list_tasksMUST return shallow copies in every SDK (docs/features/async-tasks.md§Contract: AsyncTaskManager.get_status). Closes the contract contradiction where the prior spec said "the live dataclass reference (Python); callers MUST NOT rely on mutation" while finding A-D-AT-06 mandated copies. Python now returnsdataclasses.replace(info), TypeScript returns{ ...info }, Rust returns a clone — uniform mutation-safety contract across all three SDKs. Found while resolving A-D-AT-06. -
docs/features/registry-system.md— Registration Ordering Invariants now explicitly apply to every register path. Added a!!! warning "Applies to every registration path"admonition clarifying the invariants apply uniformly to the publicregister()API, internal helpers (register_internalused by sys-modules), and discovery-driven paths (discover(),register_discovered, hot-reload). SDKs MUST NOT create per-path exceptions; if a discover-timeon_loadcallback needs to enumerate sibling modules, the callback MUST be re-shaped as a post-discover hook rather than as grounds for an early-visibility carveout. Closes implementation drift where apcore-python and apcore-typescript implemented deferred-publish onregister()but inserted into the visible map BEFOREon_loadon the discover path. Found via findings A-D-REG-003 and A-D-REG-004.
- §9.16.2 Canonical Core Event Types table aligned with v0.22.0 #36 rename.
docs/spec/protocol-spec.md§9.16 still listed the pre-v0.22.0 canonical namesapcore.module.registered/apcore.module.unregistered(subsystemmodule) andapcore.error.threshold_exceeded/apcore.latency.threshold_exceeded(categorieserror/latencyas the subsystem segment, which violates theapcore.<subsystem>.<event>convention). The v0.22.0 rename had shipped only indocs/features/event-system.md(legacy-aliases table + canonical-events table), leaving the normative spec table contradicting the feature spec, the conformance fixtureevent_naming.json, the registry-system / rfc-ephemeral-modules / system-modules docs, and all three SDKs' v0.22.0 behavior. Updated rows 6035-6036 toapcore.registry.module_registered/apcore.registry.module_unregisteredand rows 6040-6041 toapcore.health.error_threshold_exceeded/apcore.health.latency_threshold_exceeded; updated the §9.16.1 example row; rewrote the §9.16.2 preamble + collision-resolution note to distinguish the v0.18.0 short-form removals (Cohort A) from the v0.22.0 subsystem-segment renames (Cohort B). - User-facing examples no longer subscribe to v0.22.0-removed legacy event names.
docs/features/apcore-client.md(Python / TypeScript / Rust Production Setup tabs and theevent_typeparameter example),docs/features/event-system.md(three Subscriber examples),docs/features/observability.md("Events emitted" table), anddocs/features/system-modules.md(Configuration YAML comments) all referencedapcore.error.threshold_exceeded/apcore.latency.threshold_exceeded. Replaced with the v0.22.0 canonicalapcore.health.error_threshold_exceeded/apcore.health.latency_threshold_exceeded. The legacy names remain documented in theevent-system.mdrename table for historical reference; no other doc still teaches users to subscribe to the removed names. Found via/apcore-skills:sync --scope core(findings B-001 through B-004).
- §9.9.5 Reserved Namespace Query — new normative API requirement (#60). All SDKs MUST expose a public, read-only query API returning the set of reserved top-level namespace names (
apcore,_configat minimum). The query API is the single source of truth used byregister_namespaceto enforceCONFIG_NAMESPACE_RESERVED(§9.5.1 rules 3 and 4). Intended for third-party consumers (custom CLIs, framework integrations) that accept user-supplied namespace names and want fail-fast pre-validation. Class-level / module-level access (noConfiginstance required). Cross-language examples added for Python (Config.reserved_namespaces()), TypeScript (Config.reservedNamespaces), and Rust (Config::reserved_namespaces()). docs/features/event-system.md— Event Delivery Semantics section (#61). Normative cross-subscriber delivery contract: every subscriber type (built-in and user-registered) MUST accept aretryblock (max_attempts/initial_backoff_ms/max_backoff_ms/backoff_multiplier) governing retry on transient delivery failure; permanent failure MUST emitapcore.event.delivery_failed(with full payload schema) which itself MUST NOT be retried;subscribe()SHOULD accept an optionalon_failurecallback. New optionalidfield on every subscriber config surfaces a stablesubscriber_idin DLQ events. Thea2asubscriberskill_idis now configurable (default"apevo.event_receiver"). TheWebhookSubscribervsA2ASubscribercomparison table is updated: both now apply the unified retry policy. Discovered during apcore-a2a upgrade — closes the silent-drop gap for in-process subscribers that previously had no retry / DLQ path.docs/features/streaming.md— Streaming Module Interface section (#62). ExplicitStreamingModuleinterface replaces duck-typing: Python@runtime_checkableProtocol withisinstancedetection; TypeScript interface +Symbol.for("apcore.streaming")marker +isStreamingModuletype-narrowing helper (transitional fallback to method-presence detection with a one-shot deprecation log; marker becomes MUST at next major); Rusttrait StreamingModule: Moduleaccessed viaModule::as_streaming() -> Option<&dyn StreamingModule>, coexisting with the existingModule::stream() -> Option<ChunkStream>(the two paths MUST stay consistent per module). DefinesStreamingInterfaceErrorraised at module-load time when a declared-streaming module's signature does not match the interface. Adapter / bridge code (apcore-a2a, apcore-mcp) MUST use the standard detection mechanism, not barehasattr/typeofchecks.docs/features/context-object.md— Typed Access via ContextKey[T] section (#63). Promotes the existingContextKey[T]API (design-context-annotations-acl.md §1.4) as the recommended pattern for stable, schema-bearing state oncontext.data. Cross-language examples with the correct key-anchored API (KEY.set(ctx, value)/KEY.get(ctx, default)/KEY.exists(ctx)/KEY.delete(ctx)/KEY.scoped(suffix)) for Python / TypeScript / Rust. Aligns the third-party namespace rule with the existingext.*prefix mandated by middleware-hardening §1.1. Framework-reserved key list sourced from spec §1.5 (TRACING_SPANS / TRACING_SAMPLED / METRICS_STARTS / LOGGING_START / REDACTED_OUTPUT / RETRY_COUNT_BASE). No runtime behavior change — purely a discoverability / best-practice elevation.docs/features/middleware-system.md— Duplicate Middleware Detection section (#64). Normative SHOULD that SDKs detect prior registration of a middleware sharing the same identity (Python:f"{module}.{qualname}"; TS:"<module-specifier>:<ClassName>"with constructor-name fallback; Rust:std::any::type_name::<T>()) and emit aWARNING-level log naming both registration sites. Detection MUST be non-blocking and MUST NOT mutate registration order. Per-registrationallow_duplicateflag SHOULD be available for intentional stacking; customidentity_key(vendor-prefixed;apcore.*reserved) lets the same class be registered for distinct purposes without triggering the warning.docs/features/registry-system.md— Registration Ordering Invariants section (#65). Strong-guarantee invariant: a module MUST NOT become visible to discovery APIs (get/list/get_definition) until allon_loadcallbacks have completed successfully. On callback failure the module MUST NOT become visible and the registry MUST emitapcore.registry.module_load_failedwith{module_id, callback_name, error_type, error_message, timestamp}. The existingContract: Registry.registerside-effects ordering is updated to a deferred-publish pattern: reserve in-flight slot under registry lock → release lock → runon_loadunder per-module init lock → atomically publish or roll back. Callbacks for distinct modules MAY still run concurrently.conformance/fixtures/event_delivery_semantics.json(#61) — 4 cross-language test cases: retry succeeds before exhaustion (verifies attempt count + backoff delays), permanent failure emitsapcore.event.delivery_failedwith full normative payload, DLQ-subscriber failure is not re-retried (no infinite loop), SDK-generatedsubscriber_idwhen config omitsid.conformance/fixtures/registry_load_ordering.json(#65) — 4 cross-language test cases: visibility only after successfulon_load, callback failure blocks visibility and emitsapcore.registry.module_load_failed, concurrent same-ID rejected withDUPLICATE_MODULE_ID, concurrent distinct-IDs run in parallel (wall-clock assertion).
A2ASubscriberdelivery behavior — now retries on 5xx / connection / timeout (#61). Previously single-attempt: A2A failures returned silently after one try. Now A2A applies the unifiedretrypolicy (defaultmax_attempts: 3) likeWebhookSubscriber. SDK consumers that relied on single-attempt semantics MUST setretry: { max_attempts: 1 }explicitly. Side effects of retries (duplicate downstream processing on retried 5xx) are the receiver's responsibility — implement idempotency at the A2A endpoint.Registry.register— concurrent same-ID registration semantics tightened (#65). Previously a race between tworegister()calls for the samemodule_idcould resolve via lock-ordering with one succeeding and one failing late. Now an in-flight loading set rejects the second caller immediately withInvalidInputError(code=DUPLICATE_MODULE_ID). Callers that previously assumed race-tolerant insertion MUST serialize per-module-ID registration explicitly. The new behavior is symmetric with sequential same-ID registration, which has always raisedDUPLICATE_MODULE_ID.
- Issues #61–#65 were discovered during the apcore-a2a upgrade audit and target gaps that bleed into adapter / bridge implementations. They are feature-level changes;
docs/spec/protocol-spec.mdis unchanged. SDK rollout follow-ups will be filed inapcore-python,apcore-typescript, andapcore-rust. - Behavior changes (A2A retry, registry concurrent same-ID) are normative spec changes, not implementation drift. SDKs implementing v0.22.0 MUST adopt the new behavior on the next minor release; SDK CHANGELOGs SHOULD restate these
Changedentries.
- §2.5 Reserved Words —
ephemeraladded to framework reserved list (RFCrfc-ephemeral-modules.mdaccepted). New reserved namespaceephemeral.*for programmatically-generated runtime modules synthesized by LLM agents (à la ToolMaker, ACL 2025). StandardRegistry.register()only —register_internal()MUST rejectephemeral.*IDs. Reserved-namespace semantics table added below the YAML block. Seedocs/spec/rfc-ephemeral-modules.mdfor the full namespace contract (registration / lifecycle / audit / sandboxing). - §4.4 ModuleAnnotations — new
discoverable: booleanfield (defaulttrue). Controls visibility in enumeration surfaces (Registry.list(),Registry.find(), manifest export, MCPtools/list).falsehides the module from discovery while keeping it callable by exact ID.ephemeral.*modules SHOULD setdiscoverable: false. - §5.6 Module Interface Protocol — optional
preview()method (RFCrfc-preview-method.mdaccepted). Modules implementpreview(inputs, context) -> PreviewResult | nullto self-report structured-diff predictions of state changes the call would produce. Sits alongsidepreflight()(warnings) — they are orthogonal surfaces. Exception semantics mirrorpreflight()(advisory warning viamodule_previewcheck entry; does not fail validation). Pseudocode interface block +optional_methodsYAML both updated. - §12.8 PreflightResult schema — new
predicted_changes: List<Change>field;ChangeandPreviewResulttypes defined.Changehas requiredaction/target/summary(free-form strings) and optionalbefore/aftersnapshots;x-*extension fields are permitted (cross-SDK encoding patterns documented in the RFC).PreflightCheckResult.checkenum extended withmodule_preview. - §4.6 conventions table — three new
x-*AI-routing keys (D-57). Documentation-only registration of three metadata conventions surfaced by the 2025–2026 LLM-agent / tool-use frontier-research alignment audit:x-reasoning-demand(low|medium|high) — hint of the minimum reasoning capability the calling agent needs; consumed by upstream model routers for tier selection. Aligns with RouteLLM (ICLR 2025), xRouter (arXiv 2510.08439), and Cost-Aware Model Orchestration (arXiv 2512.01099).x-required-context-keys— array ofcontext.datakey names the module reads, enabling orchestrators to inject required state ahead of the call. Does not include framework-owned Context fields (trace_id,caller_id, etc.).x-supports-dry-run(boolean) — module-level signal thatExecutor.validate()(§12.2) is meaningful for this module.
docs/spec/2026-05-decision-log.md— D-57 (§4.6 reasoning/context/dry-run conventions) — marked resolved. Resolution status block updated to include D-57.
- No SDK behavior change for §4.6 D-57 conventions. All three SDKs treat module
metadataas a free-form dict /Record<string, unknown>/serde_json::Value; the framework explicitly does not validate metadata content (§4.6 "Metadata Design Principles"). Newx-*keys are additive conventions only. - Strict-mode export unaffected by D-57. §4.16 mandates that
to_strict_schema()strips allx-*extension fields, so the new keys do not surface in strict-mode output. - SDK rollout for §2.5 / §4.4 / §5.6 / §12.8 spec promotions is synchronized. All three SDKs ship the full v0.21.0 surface (Stage 2
Module.preview()+ Stage 3ephemeral.*namespace +discoverableannotation + audit single-emit +register_internal()rejection):apcore-pythonv0.21.0 — pyproject.toml bumped; PR #26 + iter-11 alignment shipped Stage 3; commit203a9a6shipped Stage 2.apcore-typescriptv0.21.0 — package.json bumped; PR #29 + iter-11 shipped Stage 2 (with TypeBoxType.Unsafeform forChange.x-*); commit577b09bshipped Stage 3.apcore-rustv0.21.0 — Cargo.toml bumped; PR #25 shipped Stage 2 prerequisite (#[non_exhaustive]hygiene); commitsafb6e05+e6abb7bshipped Stage 2Module::preview()+ Stage 3 ephemeral/discoverable.
- Conformance fixture
annotations_extra_round_trip.jsondeferred. Perrfc-ephemeral-modules.md"Transitional fixture handling during multi-SDK rollout", the fixture is NOT updated to requirediscoverableuntil all 3 SDKs have shipped support. SDKs implementingdiscoverableMAY make their conformance test runner pilot-tolerant in the interim. Synchronized fixture update will land in a follow-up PR after TypeScript and Rust shipdiscoverable.
UsageExporterProtocol/interface/trait +NoopUsageExporter+PeriodicUsageExporterin all 3 SDKs (#45 §3, D-55). Push-style usage summary export distinct from pull-stylePrometheusExporter.PeriodicUsageExporterpollsUsageCollector.summary()everyinterval_seconds(default 3600 = 1 hour) and callsexporter.export(summary).stop()halts the loop, awaitsexporter.shutdown(), and is idempotent. apcore SDKs do not ship transport-bound exporters (HTTP, Kafka, etc.) — those are explicitly out-of-tree. Documented indocs/features/observability.md"## UsageExporter (push-style)". Conformance:conformance/fixtures/usage_exporter.json(3 cases).- TypeScript registry
_filterIdConflicts8-stage decomposition matches Rust + Python (D-32, D-56). TypeScript_discoverDefaultrefactored to expose_filterIdConflictsas a separate private helper between candidate enumeration and registration;_registerInOrderreduced to pure registration. Cross-language conformance no longer special-cases TS — all 3 SDKs follow the canonical 8-stage Algorithm A04 shape. conformance/fixtures/usage_exporter.json(#45 §3, D-55) — 3 cross-language test cases:NoopUsageExporterno-op,PeriodicUsageExporterpushes summary at interval,stop()is idempotent and drains in-flight exports.conformance/fixtures/sensitive_keys_default.json(#43 §5, D-54) — 4 cross-language test cases: canonical 16-entry default list shape, legacy_secret_*prefix redaction under default, common credential-term redaction (case-insensitive substring), operator override replaces (does not merge) the default.docs/spec/2026-05-decision-log.md—## D-54 — sensitive_keys canonical default list,## D-55 — UsageExporter push interface (#45 §3),## D-56 — TS _discoverDefault 8-stage refactor— all marked resolved. Resolution status block updated to reflect D-30, D-31, D-32, D-54, D-55, D-56 as resolved.docs/features/async-tasks.md—Contract: ReaperHandle.stopblock (audit N-001) — Normative cross-language declaration thatReaperHandle.stop()MUST be async in all three SDKs (async defin Python,Promise<void>in TypeScript,async fnin Rust) with drain semantics — cancel + await termination — andidempotent: true.conformance/fixtures/trace_context.json(#35) — 8 cross-language test cases for the TraceContext W3C alignment: orderedtracestateroundtrip, 32-entry cap, malformed-entry tolerance, case-insensitiveTraceparent/TRACESTATEheader lookup, dynamictrace_flagshonoring on extract→inject, acceptedparent_idoverride (^[0-9a-f]{16}$), andINVALID_PARENT_IDrejection of malformed overrides.docs/features/event-system.md— Event Naming Convention section (#36) — Normativeapcore.<subsystem>.<event>form for framework-emitted events, glob-subscription examples (apcore.registry.*/apcore.health.*) across Python/TypeScript/Rust, and a deprecation table mapping legacy names (module_registered,module_unregistered,apcore.error.threshold_exceeded,apcore.latency.threshold_exceeded) to their canonical replacements with v0.22.0 removal target. Adds a "Configuration-driven subscribers" section listing all five built-in factories (webhook,a2a,file,stdout,filter) with a multi-subscriber YAML example.docs/features/system-modules.md— Contextual Auditing subsection (#45.2) — Normative rule that control modules (update_config,toggle_feature,reload_module) MUST includecaller_idand (when present) a redactedidentitysnapshot in their emitted audit events;caller_iddefaults to the literal string"@external"when unauthenticated;x-sensitiveIdentity fields are replaced with"<redacted>". Cross-language Python/TypeScript/Rust examples of the audit event payload shape.- Public SubscriberFactory API in Python SDK (parity with TS+Rust) (#36).
conformance/fixtures/event_naming.json(#36 / D-34) — 8 cross-language test cases: canonicalapcore.registry.module_registered/apcore.registry.module_unregistered, dual-emit of legacy names withdeprecated:trueduring v0.21.x, glob subscription matching forapcore.registry.*andapcore.health.*, canonicalapcore.health.error_threshold_exceeded/apcore.health.latency_threshold_exceeded, and cross-subsystem glob isolation.conformance/fixtures/contextual_audit.json(#45.2 / D-35) — 7 cross-language test cases:caller_idpropagation inapcore.config.updated,@externaldefault fornull/emptycaller_id, redactedidentitysnapshot inclusion forapcore.module.toggled,x-sensitivefield redaction (e.g.,bearer_token→<redacted>),caller_id+identityinapcore.module.reloaded, and audit event emission even when noAuditStoreis configured.docs/spec/2026-05-decision-log.mdD-34 (event naming canonicalization) and D-35 (contextual auditing for control plane) — both marked resolved with action: rename + dual-emit during v0.21.x for D-34; payload extension for D-35.- Pipeline
StepMiddlewareextension point in all 3 SDKs (#33). Lifecycle-shaped API (before_step/after_step/on_step_error) mirroring module-levelMiddleware— onion ordering, first-recovery-wins on errors, async support across Python/TypeScript/Rust. Documented indocs/features/middleware-system.md"Pipeline Step Middleware (Issue #33)" with three contract blocks. Conformance:conformance/fixtures/pipeline_step_middleware.json(6 cases). - Python
BatchSpanProcessorfor non-blocking span export (#43, parity with TS+Rust). Cross-SDK parity contract — identical default tunables (max_queue_size=2048,max_export_batch_size=512,schedule_delay_ms=5000,export_timeout_ms=30000), identicalon_end/force_flush/shutdownlifecycle, identical drop-on-full-queue semantics. Documented indocs/features/observability.md"Batch span processing" section. docs/spec/2026-05-decision-log.md— D-36 (Pipeline StepMiddleware), D-37 (Pipeline configuration fail-fast), D-38 (BatchSpanProcessor cross-SDK parity), all resolved.StorageBackendtrait/interface in all 3 SDKs withInMemoryStorageBackenddefault;ErrorHistory,UsageCollector,MetricsCollectoraccept injected backend (#43).OverridesStoreandFileOverridesStorein TypeScript SDK (parity with Python+Rust) (#45.1).Registry.discover_multi_class/Registry.discoverMultiClassmethod in Python+TS (D-15).docs/features/observability.md—## Pluggable storage backendsand## ErrorHistory eviction performancesubsections documenting the cross-SDKStorageBackendsurface,InMemoryStorageBackenddefault, out-of-tree policy for Redis/Postgres/S3, and the lazy-deletion semantics of the O(log N) min-heap eviction.docs/features/system-modules.md—## Persistent Overrides — pluggable OverridesStoresubsection coveringOverridesStoreparity across all 3 SDKs, with cross-languageFileOverridesStorewiring during APCore construction and the missing-path-on-first-run guarantee.docs/features/multi-module-discovery.md— UpdatedRegistry.discover_multi_classContract block to explicitly document that all 3 SDKs expose the discovery routine as a method onRegistry; added a per-SDK method/internal-helper mapping table and cross-language usage examples (D-15).docs/features/async-tasks.md— Notes documenting the PythonTaskStore.put → saverename + deprecation alias (D-10), the PythonTaskInfo.attempt_number → retry_countrename + deprecation property (D-13), removal of Python'sTaskStatus.RETRYING(D-12), and the RustRetryConfig::default().max_retries: 3 → 0alignment (D-14).docs/spec/2026-05-decision-log.md—## Resolution status — 2026-05-03 addendummarking D-10, D-12, D-13, D-14, D-15, D-19, D-25, D-27, and D-28 resolved; added new entries## D-39 — StorageBackend cross-SDK abstraction(resolved) and## D-40 — TS overrides persistence parity(resolved).conformance/fixtures/storage_backend.json— 5 cross-language test cases coveringStorageBackendsave+get round-trip, list-with-prefix filtering, idempotent delete, namespace isolation, and save-overwrites semantics.conformance/fixtures/overrides_store.json— 5 cross-language test cases coveringOverridesStore: save persists across reopen, startup applies overrides after base config, in-memory store for tests, missing path on first run is OK, delete idempotency.- Granular reload via
path_filterinput inReloadModuleacross all 3 SDKs (#45.4). - Rust
Config::reload_from_disk()for refreshing static config without binary restart (#45.5). - Error fingerprinting in
ErrorHistoryacross all 3 SDKs — dedup by (error_code, top-frame hash, sanitized message template) instead of exact message (#43 §4). - Configurable redaction via
obs.redaction.regex_patternsandobs.redaction.sensitive_keysConfig keys (#43 §5).
- Default
obs.redaction.sensitive_keysredaction list expanded to canonical 16-entry superset across all 3 SDKs (#43 §5, D-54). Default now ships as["_secret_*", "password", "passwd", "secret", "token", "api_key", "apikey", "apiKey", "access_key", "private_key", "authorization", "auth", "credential", "cookie", "session", "bearer"]. The leading_secret_*glob preserves the legacy_secret_-prefix behavior under the canonical default; the redundantapiKey/apikeypair is intentional so cross-SDK fixtures byte-match. Operator overrides viaobs.redaction.sensitive_keysinapcore.yamlreplace (do not merge with) the default. Documented indocs/features/observability.md"## Canonical defaultsensitive_keys". - Trace context (#35) —
docs/features/observability.mdW3C Trace Context section addstracestatepropagation (ordered, 32-entry cap, malformed-entry tolerance), case-insensitive header lookup (Traceparent/TRACEPARENT/traceparentall match), dynamictrace_flagshonoring (sampling flag from incoming request, NOT hardcoded), and an optionalparent_idargument oninject()validated against^[0-9a-f]{16}$withINVALID_PARENT_IDrejection. Cross-language Python/TypeScript/Rust examples added. docs/features/async-tasks.mdReaper example (sync B-004 + audit N-001) — Python tab now uses the canonical syncstart_reaper(ttl_seconds=, sweep_interval_ms=)signature returningReaperHandle, andawait reaper_handle.stop()for graceful shutdown (was previously syncstop()). TS and Rust tabs unchanged.docs/features/observability.mdPrometheus example (sync B-005) — All three tabs show wiring ofUsageCollectorintoPrometheusExporter(Pythonusage_collector=parameter, TS options-object, Rustwith_usage_collectorbuilder), with an admonition explaining the cross-language constructor-vs-builder shape difference.docs/features/multi-module-discovery.mdPython import path (sync B-001 / B-002) — Correctedfrom apcore.discovery import multi_classtofrom apcore import multi_class, removing an internal contradiction with the rest of the document. The TS tab now references the existingmultiClass()decorator.- Event names normalized to
apcore.<subsystem>.<event>form (#36). Legacy names dual-emitted; removal target v0.22.0. - Control-plane modules (
update_config,toggle_feature,reload) now includecaller_id+identityin audit events (#45.2). - Pipeline configuration is now fail-fast: missing step references and unmet
requires/providesraise errors instead of logging warnings (#33).ConfigurationErroris raised at YAML parse time for unknown step names inpipeline.configure[]andpipeline.step_middleware[].PipelineDependencyErroris raised at strategy construction time for unsatisfied capability declarations. Documented indocs/features/middleware-system.md"Configuration safety" subsection. Conformance:conformance/fixtures/pipeline_failfast_config.json(4 cases). ErrorHistoryeviction is now O(log N) via a min-heap (#43).- Python
TaskStore.put→save(deprecated alias retained);TaskInfo.attempt_number→retry_count(deprecated alias retained);TaskStatus.RETRYINGremoved (D-10, D-12, D-13). - Rust
RetryConfig::default().max_retriesis now0(was3) (D-14). - Rust streaming chunk merge raises
STREAM_CHUNK_NOT_OBJECTfor non-object chunks (D-19). - Rust
update_configraisesCONFIG_KEY_RESTRICTEDfor restricted keys (D-25). - Rust
UsageCollectornow computes trend from samples and supports period filter (D-27). - Rust
ContextLoggeroutput schema aligned with Python+TS (lowercase level, nestedextra,module_id) (D-28). - Python
start_reapernow uses(ttl_seconds, sweep_interval_ms)and returnsReaperHandlematching TS+Rust; old kwargs aliased with DeprecationWarning (D-11). OverridesStoreis now a pluggable trait/interface/protocol in all 3 SDKs (#45.1, D-47);FileOverridesStoreandInMemoryOverridesStoreship as defaults.- Reaper default
sweep_interval_msaligned across 3 SDKs to300_000(D-48). - TypeScript
RetryConfig.computeDelay→computeDelayMs; RustRetryConfig::delay_for_attempt→compute_delay_ms(old names deprecated, removal target v0.22.0) (D-08, D-49). - Rust
inject()now propagates inboundtrace_flagsfrom context data (D-50). - Rust
inject()now returns an error on malformedparent_idoverride, matching PY+TS (D-51). - Rust pipeline
ConfigurationErroris a distinct error code fromPipelineDependencyError(D-52). - TypeScript redaction reads canonical
obs.redaction.{regex_patterns,sensitive_keys}Config keys; legacyobservability.redaction.{field_patterns,value_patterns}honored with deprecation warning (D-53).
- Cross-language naming alignment for the
context_namespacemiddleware module (audit N-003) — Python and Rust shippedContextWriter/NamespaceCheck; TypeScript shippedContextKeyWriter/ContextKeyValidation. TypeScript renamed inapcore-typescriptv0.20.x to match the Python+Rust majority; the prior TS names are retained as deprecated aliases for one release cycle. No spec text change needed — all three SDKs now agree on the canonical names. - Async
on_errormiddleware in Python+TS now detects awaitable/thenable RETURN values rather than inspecting function shape, fixing silent Promise leaks forpartial-wrapped handlers (#42).
- D-30: clarified that
pre_approval_hookis Python-only.docs/features/multi-module-discovery.mdadds a "Python-onlypre_approval_hook" subsection explaining that Python imports the file at scan time so the hook protects against arbitrary code execution; TypeScript and Rust parse static AST/source and never import code from disk for discovery, so the hook is not present in those SDKs. Cross-language tabs show the call shape in each SDK. - D-31: documented per-language file extension scanning defaults and skip patterns.
docs/features/multi-module-discovery.mdadds a "File extensions and skip patterns" subsection with a per-SDK table: Python.py(skip__pycache__/,*.pyc, leading_), TypeScript.ts/.js(skip*.d.ts,*.test.*,*.spec.*), Rust.rsonly (configurable viawith_extensions).
-
docs/spec/design-durability-boundary.md— Durability Boundary design document. Vendor-neutral architectural document enumerating apcore's stable hooks for retry/replay/workflow layers built on top (Context JSON serialization, Approval Phase B_approval_tokenretry contract, theTaskStoreinterface, the six extension points, retry-safety annotations,context.data+ §4.6x-extension mechanism), explicit non-goals (mid-pipeline checkpointing, multi-call workflow orchestration, cost governance, first-class fields for application-level concerns apcore does not consume), five concrete integration patterns (transient retry, crash-durable single-call retry, long-running pause for external decisions, cross-process invocation, logical-call deduplication), and a gap watchlist of deliberately-deferred items. Establishes that apcore is the module standard, durable execution is a runtime concern; both can coexist without apcore absorbing workflow surface. -
docs/features/system-modules.md— System Modules Hardening section (Issue #45) — Five production-hardening extensions: (1) optionaloverrides_path/ KV-store persistence forsystem.control.update_configandsystem.control.toggle_featurechanges, loaded after base config on startup via pluggableOverridesStoreinterface; (2) contextual audit trail — all state-modifying control modules extractcontext.identityand append structuredAuditEntryrecords (timestamp, action, actor_id, actor_type, trace_id, before/after change) via a pluggableAuditStoreinterface; (3) Prometheus integration forUsageCollector— five new gauges/counters (apcore_usage_calls_total,apcore_usage_error_rate,apcore_usage_p50/p95/p99_latency_ms) exported via the existing/metricsendpoint with configurableexport_timeout_ms; (4) granularpath_filterglob field onsystem.control.reload_modulefor bulk reload in dependency topological order, mutually exclusive withmodule_id(MODULE_RELOAD_CONFLICTon conflict); (5) startup failure handling —fail_on_error: bool = Falseparameter for Python/TypeScriptregister_sys_modules(),failOnError: boolean = falsefor TypeScript, andResult<(), SysModuleError>return type for Rust (never panics or returnsOption). Updatesregister_sys_modulescontract block with newfail_on_errorparameter andSYS_MODULE_REGISTRATION_FAILEDerror code. Closes Issue #45. -
conformance/fixtures/system_modules_hardening.json— 10 cross-language test cases covering: overrides persisted on update_config, overrides loaded after base config on startup, audit entry actor_id extracted from context.identity, audit entry before/after change for toggle_feature, Prometheus /metrics includes apcore_usage_calls_total, path_filter reloads matching modules in topological order, module_id + path_filter conflict raises MODULE_RELOAD_CONFLICT, fail_on_error=True raises SysModuleRegistrationError, fail_on_error=False logs and continues, and Rust register_sys_modules returns Result not Option. -
docs/features/observability.md— Observability Hardening section (Issue #43) — Six production-hardening extensions: (1) pluggableObservabilityStoreinterface withInMemoryObservabilityStoredefault and optionalRedisObservabilityStore/SqlObservabilityStorebackends injected at construction time; (2)BatchSpanProcessorfor non-blocking OTEL export with configurablemax_queue_size(2048),schedule_delay_ms(5000),max_export_batch_size(512), andexport_timeout_ms(30000) — drops spans on full queue withspans_droppedcounter; (3) O(log N)ErrorHistoryeviction using a min-heap keyed onlast_seen_atplus an O(1) module-id index, replacing the O(M) ring-buffer scan; (4) SHA-256 content-addressable error fingerprinting with UUID/ID/timestamp normalization for accurate deduplication; (5)RedactionConfigwith globfield_patternsand regexvalue_patternsapplied at log time (union with existingx-sensitiverules); (6) K8s/Prometheus integration hooks (/metrics,/healthz,/readyz) with requiredapcore_module_calls_total,apcore_module_errors_total,apcore_module_duration_secondsmetrics and ServiceMonitor annotation guidance. AddsPrometheusExporter.exportcontract block. Closes Issue #43. -
conformance/fixtures/observability_hardening.json— 10 cross-language test cases covering: default InMemoryObservabilityStore construction, BatchSpanProcessor buffering without immediate export, BatchSpanProcessor drop-on-full-queue withspans_droppedincrement, min-heap eviction of oldestlast_seen_atentry, fingerprint-based deduplication (count increment, no duplicate entry), UUID normalization producing identical fingerprints, different error codes producing distinct fingerprints, field-pattern redaction (*password*), value-pattern redaction (^Bearer .*), and Prometheus export containing all three required metric names. -
docs/features/async-tasks.md— AsyncTaskManager Evolution section (Issue #34) — Three new capability extensions: (1) pluggableTaskStoreinterface withInMemoryTaskStoredefault and optionalRedisTaskStore/SqlTaskStorebackends injected at construction time; (2) per-task retry configuration (max_retries,retry_delay_ms,backoff_multiplier,max_retry_delay_ms) with exponential backoff scheduling andFAILEDterminal state after exhaustion; (3) opt-in Reaper background task for automatic TTL-based deletion of terminal-state tasks, guarded byreaper_enabled: true. IncludesTaskStore.saveandAsyncTaskManager.start_reapercontract blocks. Closes Issue #34. -
conformance/fixtures/async_task_evolution.json— 10 cross-language test cases covering: default InMemoryTaskStore construction, custom store injection, save-and-get round-trip, list-by-status filtering, retry scheduling on first failure, backoff multiplier delay computation, max-retries-exhausted → FAILED transition, Reaper disabled by default, Reaper deletes expired terminal tasks, and Reaper skips PENDING/RUNNING tasks regardless of age. -
docs/features/schema-system.md— Schema System Hardening section (Issue #44) — Five hardening areas: (1) union type standardization — all SDKs MUST evaluate ALL branches ofanyOf/oneOf(not short-circuit on first); (2) recursive schema support via lazy$refresolution (model_rebuildin Python,Type.Recursivein TypeScript,Box<T>in Rust); (3) Rust validator parity — normative requirement to supportallOf/anyOf/oneOf/notand numerical/string constraints, withjsonschema-rsas recommended path; (4) semantic format mapping —SHOULDmapdate-time,date,time,email,uri,uuid,ipv4,ipv6to language-native types; (5) content-addressable schema cache keyed by SHA-256 of canonical JSON — deduplicates identical schemas loaded from different paths. AddsSchema.validate_union,Schema.validate_recursive, andSchema.content_hashcontract blocks. Closes Issue #44. -
conformance/fixtures/schema_hardening_union.json— 8 test cases for anyOf all-branches evaluation, oneOf exactly-one enforcement, oneOf ambiguous-match error, allOf all-satisfied and one-fails cases. -
conformance/fixtures/schema_hardening_recursive.json— 6 test cases for TreeNode$ref: "#"recursive schema at depths 1, 3, and 5; invalid child type; empty and absent children arrays. -
conformance/fixtures/schema_hardening_constraints.json— 12 test cases for minimum/maximum, exclusiveMinimum boundary, minLength/maxLength, pattern match/no-match, andnotkeyword. -
conformance/fixtures/schema_hardening_formats.json— 9 test cases for semantic format mapping across all 8 canonical formats; invalid formats produce warn_logged=true, not hard errors. -
conformance/fixtures/schema_hardening_cache.json— 5 test cases for content-hash deduplication: same-content two-paths, different-content, key-order canonical normalization, pre/post-$refresolution, empty schema. -
NestContextFactory in
nestjs-apcore(Issue #35) — NewNestContextFactoryservice (src/context/nest-context.factory.ts) reads W3Ctraceparentheader (header key normalized to lowercase),x-correlation-id/x-request-idcorrelation headers (stored incontext.data["x-correlation-id"]), andx-user-id/Authorization: Bearerfor identity. 13 unit tests covering all fixture semantics. Exported from package index. This completes the framework-factories task for Issue #35 — Django, Flask, and NestJS all now have default ContextFactory implementations. Also fixesPROTOCOL_SPEC.md §8.5error response example (trace_idupdated from dashed UUID to 32-char lowercase hex, consistent with §5.7 and §10.5 changes shipped in v0.19.0). Closes Issue #35. -
PROTOCOL_SPEC §5.16 — Pipeline Control Flow Requirements — Normative rules for fail-fast error handling, O(1) step lookups, replace semantic for step configuration,
run_untiltermination predicate, and step-level middleware ordering. Closes Issue #33. -
docs/features/core-executor.md— Pipeline Hardening section — Implementation guidance for all 5 hardening areas (fail-fast, replace semantic, step-level middleware,run_until, O(1) lookups) with cross-language Python/TypeScript/Rust examples and aPipeline.configure_stepcontract block. -
conformance/fixtures/pipeline_hardening.json— 5 test cases for fail-fast, continue-on-ignored-error, replace-semantic,run_untilearly termination, and O(1) lookup verification. -
docs/features/event-system.md— Event Management Hardening section — Cross-language SubscriberFactory parity (TypeScript + Rust examples), three new built-in subscriber types (file,stdout,filter), circuit-breaker resilience spec withOPEN/CLOSED/HALF_OPENstate machine,apcore.subscriber.circuit_opened/circuit_closedevents,SubscriberCircuitBreaker.on_failurecontract. Closes Issue #36. -
conformance/fixtures/event_management_hardening.json— 10 test cases for SubscriberFactory registration, built-in subscriber types, filter pass/discard, circuit-breaker state transitions. -
PROTOCOL_SPEC §2.1.1 — Multi-Class Discovery — Opt-in mode allowing multiple Module classes per file. ID is derived as
base_id.snake_case(ClassName). Single-class files produce unchanged IDs (backward-compatible). IntroducesMODULE_ID_CONFLICTerror for classes that produce the same snake_case segment. Closes Issue #32. -
docs/features/multi-module-discovery.md— Full feature spec with discovery algorithm, conflict detection, backward compatibility guarantees, and cross-language usage examples. -
conformance/fixtures/multi_module_discovery.json— 8 test cases for ID derivation, snake_case conversion, conflict detection, and backward compatibility. -
docs/features/middleware-system.md— Middleware Architecture Hardening section — Context namespacing rules (_apcore.*vsext.*),CircuitBreakerMiddlewarespec (per-module error tracking, rolling window, OPEN/HALF_OPEN/CLOSED state machine,CircuitBreakerOpenError,apcore.circuit.opened/closedevents),TracingMiddlewarespec (OTLP-compatible span lifecycle, graceful no-op when OpenTelemetry is absent), YAML-driven declarative middleware configuration (tracing,circuit_breaker,logging,customtypes), and async handler detection fix (inspect.iscoroutinefunctionin Python,handler.constructor.namein TypeScript). IncludesMiddleware.detect_asynccontract block. Closes Issue #42. -
conformance/fixtures/middleware_hardening.json— 10 test cases covering context namespace validation (valid_apcore.*, validext.*, violation), circuit breaker state transitions (opens at threshold, short-circuits in OPEN, probes in HALF_OPEN, closes on success), tracing span creation and no-op without OTel, and async coroutine detection correctness.
- PROTOCOL_SPEC §7 — Approval Phase B Resume semantics clarified. Added a normative paragraph after the Step 5 Algorithm formalizing existing reference behavior: when a caller retries an
APPROVAL_PENDINGcall by injecting_approval_tokenintoarguments, the executor MUST re-enter the pipeline from Step 1 with no preserved intermediatePipelineContextstate. Pre-approval middleware side effects re-execute on resume; middleware needing at-most-once semantics across an approval gate SHOULD inspect_approval_tokenitself. No behavioral change — documents existing implementation. Cross-referencesdocs/spec/design-durability-boundary.md§2.2.
docs/features/async-tasks.md— TypeScriptawaitmissing on async methods. The TypeScript tab was callingmanager.submit(),manager.cancel(), andmanager.shutdown()withoutawait, while the Python and Rust equivalents correctly usedawait/.await. All three calls are now correctly awaited.docs/features/event-system.md— "Via Direct EventEmitter" section missing TypeScript and Rust tabs. The section contained only bare Python code with no tab wrapper. Added=== "Python"/=== "TypeScript"/=== "Rust"tabs consistent with every other behavioral section in the file.docs/features/observability.md— Lowercasemustin normative Requirements section.InMemoryExporter must be boundedcorrected toInMemoryExporter MUST be boundedper RFC 2119.PROTOCOL_SPEC.md §5.16— RFC 2119 keywords unbolded. AllMUST,MUST NOT, andSHOULDoccurrences in the §5.16 Pipeline Control Flow Requirements section (intro sentence + 5 numbered items) were plain text. Updated to**MUST**/**MUST NOT**/**SHOULD**consistent with the rest of the specification.conformance/README.md— Non-Standard Test Patterns section incomplete.schema_hardening_recursive.json(shared root-levelschema) andschema_hardening_formats.json(root-levelformat_mappingsreference metadata) were not documented alongside the existingcontext_serialization.jsonandannotations_extra_round_trip.jsonentries. Added entries explaining both patterns for SDK test runner authors.
- PROTOCOL_SPEC §5.7 —
trace_idformat pattern tightened fromformat: uuid(ambiguous: dashed vs hex) topattern: "^[0-9a-f]{32}$"(32-char lowercase hex, W3C Trace Context compatible). Resolves internal spec contradiction between §5.7 (format: uuid), §10.5 example (dashed UUID), and §10.5traceparent_header(hex without dashes). - PROTOCOL_SPEC §10.5 — Trace ID Format section rewritten to align with W3C Trace Context Level 2. Adds explicit
external_trace_parent_handlingrules: strict 32-hex validation only; all-zero and all-f rejected per W3C; no auto-normalization (dashed UUID stripping, case folding) atContext.create— pushed to TraceParent parser or user's ContextFactory. Implementations MUST regenerate + log WARN on invalid input; MUST NOT raise.
- PROTOCOL_SPEC §5.15.2 —
DEPENDENCY_VERSION_MISMATCHerror code (new, non-retryable). Raised when a module independencies.requiresexists but its registered version does not satisfy the declaredversionconstraint. Fordependencies.optionalthe same situation logs WARN and skips the dependency edge. conformance/fixtures/dependency_version_constraints.json— 15 cross-SDK test cases covering exact match,>=/<=, range (>=1.0.0,<2.0.0), caret (^1.2.3, including^0.2.3major-zero semantics), tilde (~1.2.3), partial-version shortcuts ("1"matches1.x.x), no-constraint accepts any, and optional-dependency skip-on-mismatch.- PROTOCOL_SPEC §5.7 — Note on external correlation IDs. Documents that existing projects' request/correlation identifiers (
X-Request-ID, ULID, AWS X-Ray, etc.) SHOULD be preserved incontext.data["x-correlation-id"]alongsidetrace_id, not used to overwrite it. Establishes the dual-ID model at the spec level. - PROTOCOL_SPEC §10.5 — Forward-compatibility Note. Non-normative acknowledgment that
trace_idformat is a versioned contract, not a permanent guarantee. Future protocol versions MAY introduce alternative formats or structured trace IDs for multi-agent or non-linear trace topologies. docs/guides/integrating-existing-projects.md— New user guide covering Django, Express, and Actix integration patterns for projects already carrying their own request-ID / correlation-ID system.conformance/fixtures/context_trace_parent.json— Cross-language fixture with 10 test cases covering valid 32-hex, dashed UUID rejection, uppercase rejection, W3C-invalid all-zero/all-f rejection, length errors, non-hex characters, empty string, and the no-trace_parent baseline.docs/spec/DECLARATIVE_CONFIG_SPEC.mdv1.0 — New unified specification for declarative YAML configuration across all three SDKs. Covers bindings YAML (§3), pipeline config (§4), entry-point meta (§5),auto_schemasemantics (§6), canonical error model with exact message templates (§7), configurable policy limits (§9). Establishes core principles: YAML syntax 100% consistent across SDKs; never silently drop fields; auto-processing as default; JSON Schema for structure, apcore.yaml for policy.schemas/binding.schema.jsonupdates — Relaxedtargetregex to support TypeScript ESM specifiers (./relative,@scope/pkg) and Rust handler-map keys.auto_schemafield now accepts boolean or"true"/"permissive"/"strict"enum. Schema mode mutex rules rewritten fromoneOftoallOf+if/then(supports implicit auto default when no mode specified).DisplayOverlaychanged fromadditionalProperties: falsetopropertyNamespattern +additionalProperties: SurfaceOverridefor future surface extensibility.Annotationsexpanded from 5 to 12 fields (addedstreaming,cacheable,cache_ttl,cache_key_fields,paginated,pagination_style,extra) to align withmodule-meta.schema.json.versionfield pattern moved to configurable policy (version_require_semver). Addedspec_versiontop-level field.schemas/apcore-config.schema.json—pipelineandvalidationsections — NewPipelineConfigdefinition withremove,configure,stepsstructure;PipelineStepwithtype/handlermutual exclusion,after/beforepositioning, and metadata fields (match_modules,ignore_errors,pure,timeout_ms). NewValidationConfigwith configurable policy limits for bindings (description_max_length,documentation_max_length,tags_pattern,version_require_semver) and pipeline (step_name_max_length,timeout_ms_max).conformance/fixtures/binding_yaml_canonical.yaml— Cross-SDK binding YAML conformance fixture with 3 entries testing explicit auto_schema (permissive), explicit input/output schemas with display overlay, and auto_schema strict mode. All three SDKs must parse this fixture identically.conformance/fixtures/binding_errors.json— 6 canonical error message test cases for cross-SDK byte-for-byte message parity (BindingFileInvalidError,BindingSchemaModeConflictError,BindingSchemaInferenceFailedError,PipelineHandlerNotSupportedError,BindingInvalidTargetError,BindingModuleNotFoundError).
- PROTOCOL_SPEC §8 Error Code Registry — Added missing
DEPENDENCY_VERSION_MISMATCHentry alongsideDEPENDENCY_NOT_FOUND. Previously the behavior was implied by §5.3 version constraint syntax (^,~, ranges) without a corresponding error code in §8, leaving SDKs without a normative failure mode. - SDK compliance with §5.15.2
DEPENDENCY_NOT_FOUND— All three SDKs (apcore-python,apcore-typescript,apcore-rust) now raise the spec-mandatedDEPENDENCY_NOT_FOUNDerror code for missing required dependencies. Previously all three raisedMODULE_LOAD_ERROR, silently diverging from the spec. - SDK
CircularDependencyError.details.cycle_pathparity — Rust SDK now carriescycle_pathas structured details, matching Python (details["cycle_path"]) and TypeScript (details.cyclePath). Previously Rust only placed the path in the message string, forcing downstream consumers to parse it. - PROTOCOL_SPEC §8.5 —
trace_idexample updated to 32-char lowercase hex. The error response example still used the old dashed UUID form (550e8400-e29b-41d4-a716-446655440000), contradicting the §5.7 and §10.5 tightening shipped in this same release. Updated to4bf92f3577b34da6a3ce929d0e0e4736.
- The pre-existing
MUST NOT allow externally provided unvalidated trace_idrule in §10.5 is superseded by the explicit validation/normalization pipeline — "unvalidated" is now impossible because every input is either accepted verbatim or replaced with a fresh trace_id.
Breaking changes in this release. See
MIGRATION-v0.18.mdfor the consolidated migration guide covering all four repositories.
APCoreconstructor simplified — removedconfig_pathparameter; useConfig.load()instead (e.g.APCore(config=Config.load("apcore.yaml"))). This applies to all SDKs: Python, TypeScript, and Rust. The explicit two-step pattern keeps the constructor focused and avoids mutual-exclusivity validation.- System module JSON Schemas — 6 new output schemas in
schemas/:sys-control-reload-module,sys-control-toggle-feature,sys-control-update-config,sys-health-module,sys-health-summary,sys-manifest-full. Each declares$schema,$id,additionalProperties: false, and descriptions on every property. - Conformance README coverage gap table — Documents 6 algorithms (A01, A07, A12, A21, A22, A23) that lack conformance fixtures.
- Conformance README non-standard patterns section — Documents
sub_casesandforbidden_root_keystest patterns for SDK implementers. - PROTOCOL_SPEC §2.7 —
canonical_idmaximum length raised from 128 to 192 characters. Motivated by deep-namespace languages (Java/.NET/Spring FQN-derived IDs) where snake_case-converted fully-qualified names can exceed 128 in edge cases. 192 is filesystem-safe (192 + ".binding.yaml" = 205 bytes < 255-byte filename limit on ext4/xfs/NTFS/APFS/btrfs) and remains withinVARCHAR(255)for typical persistence layers. MCP alias 64-char hard limit (OpenAI function name spec) is unchanged and still requires alias mapping for any module_id > 64. Schemas updated:binding.schema.json,module-schema.schema.json,module-meta.schema.jsondeclaremodule_id.maxLength: 192;acl-config.schema.jsoncallers/targetspattern strings raised to 192 to remain symmetric. Algorithm A01 (directory_to_canonical_id) Step 6 length threshold updated to 192. Conformance test T01-006 boundary updated to>192 chars. Forward-compatible relaxation: producers targeting mixed-version ecosystems should keep IDs ≤ 128 until all consumers are upgraded. - PROTOCOL_SPEC §5.6 yaml block now declares
required_attributes:(input_schema,output_schema,description) explicitly. The pseudocode block already marked these as "Required definitions" but the yaml block beneath only listedrequired_methodsandoptional_attributes, so a reader of the yaml alone could not see the attribute requirement. Closes a sync audit contradiction. - PROTOCOL_SPEC §4.4.1 — Annotations Extension Field (
extra) Wire Format — New normative section defining the canonical on-the-wire shape ofModuleAnnotations.extra. Producers MUST serialize as a nested{"extra": {...}}object and MUST NOT flatten extension keys to the annotations root. Consumers MUST accept the nested form; legacy top-level overflow keys MAY be tolerated for one MINOR cycle. When both forms appear in the same input, the nested value wins. extrafield inAnnotationsschema —schemas/module-meta.schema.jsonnow declaresextraas an object withadditionalProperties: true. The outerAnnotationsobject retainsadditionalProperties: false, so unknown root-level keys are no longer silently accepted at the schema layer.conformance/fixtures/annotations_extra_round_trip.json— 8 cross-language test cases locking the wire format: canonical nested round-trip, empty extra, namespaced keys, Unicode and nested object values, legacy flattened deserialization tolerance, nested-wins precedence, forbidden-root-keys negative case, and dotted-keys-are-not-paths.MIGRATION-v0.18.md— Consolidated migration guide covering all four breaking changes shipped in this release (annotations wire format, apcore-rust Config restructure, apcore-python event alias removal, misc cleanup).- 8 new feature specification docs. The following features were implemented in both
apcore-pythonandapcore-typescriptSDKs but had no corresponding feature spec in the protocol repo:docs/features/error-system.md— Structured error hierarchy (30+ error types), error codes, AI guidance fields (retryable,ai_guidance,user_fixable,suggestion),ErrorCodeRegistryfor custom module error codes.docs/features/extension-system.md—ExtensionManagerwith 6 built-in extension points (discoverer,middleware,acl,span_exporter,module_validator,approval_handler), plugin wiring viaapply().docs/features/call-chain-guard.md— Algorithm A20: call depth limiting, circular call detection, frequency throttling with configurable thresholds.docs/features/cancellation.md—CancelTokencooperative cancellation, executor timeout integration with 5-second grace period.docs/features/async-tasks.md—AsyncTaskManagerfor background module execution with concurrency semaphore, task lifecycle tracking, and cleanup.docs/features/streaming.md— Three-phase streaming pipeline (setup → chunk emission → post-validation), deep merge with depth cap.docs/features/identity-system.md—Identitydata structure with well-known types (user,service,ai,system,anonymous),ContextFactoryprotocol for web framework integration.docs/features/apcore-client.md—APCoreunified client feature spec covering initialization modes, auto-registration behavior, and method summary.
mkdocs.ymlnavigation updated. Feature Specifications section expanded from 11 to 19 entries (alphabetically sorted).README.mdDocumentation Index updated. Added all 8 new feature docs to the Feature Specifications table.
- PROTOCOL_SPEC §10.8 — Span attribute
"caller"corrected to"caller_id". The span naming convention section listed"caller"as a SHOULD attribute, inconsistent with thecaller_idfield name used everywhere else in the specification (§2.1, §6, §7, Context object). All SDKs already usedcaller_idin their span implementations. - PROTOCOL_SPEC §9.3 — RFC 2119 keyword compliance. Five constraint validation statements in the
A12_validate_configpseudocode used lowercasemustinstead of uppercaseMUST. Corrected to match RFC 2119 normative intent. mkdocs.ymlnavigation — 4 orphaned docs now reachable. Addedapi/client-api.md,features/config-bus.md,features/event-system.md, andfeatures/system-modules.mdto the site navigation. These pages existed and were referenced in README.md but were not in the MkDocs nav tree.- JSON Schema property descriptions. Added missing
descriptionfields to 4 properties inmodule-schema.schema.json(ErrorSchemaObject.type,.properties,.required, and thedescriptionproperty definition) and 3 surface-override objects inbinding.schema.json(cli,mcp,a2a). All properties now comply with the "every property must have a description" rule. docs/features/streaming.md— Step count corrected from "Steps 1–6" to "Steps 1–7". The text listed 7 pipeline phases (Context Creation through Input Validation) but labelled them "Steps 1–6". PROTOCOL_SPEC §6574 explicitly states "Steps 1–7 identical to call()" forstream().docs/api/executor-api.md—validate()step count corrected. Previously said "Steps 1-7"; now says "Steps 1-6, plus optional module-level preflight" to match PROTOCOL_SPEC §12.8.docs/spec/conformance.mdT11-001 — Deprecatedexecute_async()replaced withcall_async(). Theexecute_async()method was removed in v0.10; conformance table still referenced the old name.docs/guides/creating-modules.md— Rust examples added. Two tabbed code sections (module definition and module usage) only had Python and TypeScript examples. Added Rust tabs with complete, importable examples usingapcore::{Module, Context, Registry, Executor}.- Cross-language
extraserialization divergence — Audit revealed thatapcore-rust≤ 0.17.1 used#[serde(flatten)]and emitted extension keys at the annotations root, whileapcore-pythonandapcore-typescriptemitted nestedextraobjects. A binding round-tripped through Rust would silently lose theextrapayload (the nested object collapsed intoextra["extra"]). All three SDKs are now aligned on the nested form per §4.4.1. - Python/TypeScript precedence inversion — Both SDKs previously merged top-level overflow over explicit nested
extra({**explicit, **overflow}), making nested values losable. Per §4.4.1 rule 7, nested now wins. Behavior change is observable only when the same key appears in both forms in the same input — a pathological case that no conformant producer emits. apcore-rust Configno longer silently ignored spec-conformant YAML. The struct previously declared executor and observability fields at the root ofConfiginstead of nested underexecutorandobservabilitynamespaces, contradicting PROTOCOL_SPEC §9.1 and the Python/TypeScript SDKs. Loading a YAML file that used the canonical nested form would cause typed fields to remain at default values while the user data ended up in an unusedsettingsHashMap entry. v0.18.0 restructuresapcore-rust Configto a nested form, drops the legacy short field names, and rejects v0.17.x-style YAML with a hard error pointing atMIGRATION-v0.18.md. Seeapcore-rust/CHANGELOG.mdfor the full type-by-type rename table.docs/spec/design-context-annotations-acl.mdno longer contradicts shipped spec. The historical design document still claimed "ModuleAnnotations is frozen with 11 fields" and recommended#[serde(flatten)]for the Rust extra field. A superseded banner now points readers at PROTOCOL_SPEC §4.4.1 for current normative behavior; the original text is preserved for historical context.mkdocs.ymlHome nav now points atREADME.md(which exists) instead ofindex.md(which never did), eliminating a noisy mkdocs build warning.- PROTOCOL_SPEC §2.1 — Algorithm A01 step 6 and the directory_to_id YAML format block still said
max_length: 128after the §2.7 bump to 192. Both updated to 192. Internal contradiction with the §2.7 EBNF constraint and the version history changelog entry resolved. - PROTOCOL_SPEC §3868 cross-reference cleanup —
Self-Evolution: ... runtime reconfiguration (see §6.6, §10)repointed to(see §9.11 Hot-Reload, §10 Observability). §6.6 is "System Module Permissions" — unrelated to runtime reconfiguration. Stale reference from a §6.x renumbering. docs/spec/design-execution-pipeline.md:1248— Phase 4 implementation table replaced obsoleteVALIDATE_ONLYpreset with the canonicalMINIMALpreset, matching the canonical 5-preset enumeration at line 680 (standard, internal, testing, performance, minimal). VALIDATE_ONLY was replaced bydry_runper §4.docs/spec/design-execution-pipeline.md:1192—validate()return type table updated to show all three SDKs unified onPreflightResult(Rust column previously showedValidationResultwith a "long-term plan to unify" note; the unification was completed in this release).docs/features/approval-system.md— Wrong pipeline step numbers. Input Validation was cited as "Step 6" in three places; it is actually Step 7. Middleware Before Chain is Step 6. Corrected all references.docs/features/event-system.md— Missing canonical event name prefix.error_threshold_exceededandlatency_threshold_exceededwere listed without theapcore.prefix. Per PROTOCOL_SPEC §9.16, canonical names areapcore.error.threshold_exceededandapcore.latency.threshold_exceeded; the unprefixed forms are legacy aliases. Event Types table corrected.docs/features/config-bus.md— Wrong PROTOCOL_SPEC section references. "Typed Bind (§9.8)" corrected to §9.9.3 (§9.8 is Environment Variable Override). "Hot Reload (§9.9)" corrected to §9.11 (§9.9 is Namespace-Aware Access API).docs/features/streaming.md— TypeScript example fixed. Replaced incorrectclient.module()withasync function*(FunctionModule doesn't support streaming) with correctModuleinterface implementation using astream()method. Fixed deep merge table: arrays are replaced, not concatenated.docs/features/error-system.md— Reserved prefix list corrected. Added missing prefixes (DEPENDENCY_,CALL_,MIDDLEWARE_,VERSION_,ERROR_CODE_), removed incorrect ones (RELOAD_,EXECUTION_,ERROR_FORMATTER_). Added missing error classes (MiddlewareChainError,ErrorCodeCollisionError,DependencyNotFoundError).docs/features/call-chain-guard.md— Algorithm description corrected. Circular detection now accurately describes the prior-chain extraction + last-occurrence check, matching both SDK implementations.docs/features/cancellation.md— TypeScript Context example fixed.CancelTokenis passed vianew Context()constructor, notContext.create()which doesn't accept it.docs/features/async-tasks.md— Type corrections.TaskInfo.resulttype corrected fromdict[str, Any]toAny.get_result()return type corrected fromdicttoAny.docs/concepts.md— RemovedModuleinheritance. Allclass XModule(Module):examples changed toclass XModule:to match the spec requirement "Modules MUST NOT inherit from an ABC."docs/architecture.md— Fixed 3 broken links.../api/*.mdpaths corrected to./api/*.md(architecture.md is in docs/, not a subdirectory).docs/guides/middleware.md— Removed non-existentAsyncMiddlewareclass. Replaced with standardMiddlewaresubclass with async method overrides, which both SDKs support.docs/api/executor-api.md— Sync/async clarity.call()docstring updated from "Synchronously call module" to "Call module (synchronous in Python, async in TypeScript/Rust)."docs/api/client-api.md— Sync/async clarity. Section "4.1 Synchronous Call" renamed to "4.1 Basic Call" with note explaining Python sync vs TypeScript/Rust async.docs/spec/type-mapping.md— Go/Java SDK scope clarified. Added note that Go and Java type mappings are for future implementers; only Python, TypeScript, and Rust have official SDKs.docs/guides/— Cross-language notes added.acl-configuration.md,adapter-development.md, andtesting-modules.mdnow have a note at the top explaining how Python examples apply to TypeScript and Rust.- PROTOCOL_SPEC.md RFC 2119 compliance — 5 instances of lowercase bold
**must**/**should**in normative contexts (§1.6, §4.2, §5.6, §11.6) corrected to uppercase**MUST**/**SHOULD**. - PROTOCOL_SPEC.md §11.6 extension point semantics — Clarified ambiguous mixed requirement: "Implementations SHOULD support the following extension points. Each supported extension point MUST define a clear interface contract."
docs/spec/algorithms.mdRFC 2119 compliance — 5 lowercasemustin A12 config validation constraints (lines 895–899) corrected to uppercaseMUST.schemas/sys-control-update-config.schema.json— Added missingtypedeclarations toold_valueandnew_valueproperties.schemas/defaults.schema.json— Added explicitadditionalProperties: falseto root and all 9 nested objects.schemas/sys-health-summary.schema.json— Added explicitadditionalProperties: falseto root and all 3 nested objects.- CHANGELOG.md v0.16.0 date — Corrected from 2026-04-05 to 2026-04-03 (matching git history).
docs/features/config-bus.md— Converted all code examples to cross-language tabbed format (=== "Python"/=== "TypeScript"/=== "Rust"). Added missing Rust tab in Introspection section.- Orphaned docs removed from
docs/features/— Moved 4 code-forge planning docs (acl-conditions-redesign.md,annotations-redesign.md,context-redesign.md,overview.md) out of the published docs tree; these already exist inplanning/. docs/spec/design-execution-pipeline.md— Mergeddesign-pipeline-v2.mdcontent, removed stale legacy alias column.- Event naming standardization — Canonical
apcore.*prefix enforced across event system docs and identity rule schemas. - Multi-language documentation — Added cross-language examples and tabbed sections to feature docs, API references, and getting-started guide.
- Legacy event aliases.
apcore-pythonno longer emitsmodule_health_changedorconfig_changedalongside the canonicalapcore.module.toggled,apcore.health.recovered,apcore.config.updated, andapcore.module.reloadedevents. The dual-emission deadline was originally v0.16.0 but was missed; this release completes the cleanup. See migration guide §3.
- Legacy flat
Configfield names.Config.max_call_depth,Config.default_timeout_ms,Config.global_timeout_ms,Config.max_module_repeat,Config.enable_tracing,Config.enable_metricsare gone. Use the nested namespaces (Config.executor.*,Config.observability.*). The string-key API also drops the legacy bare-name aliases —config.get("max_call_depth")now returnsNone; useconfig.get("executor.max_call_depth"). See migration guide §2.
minimalstrategy preset — 4-step pipeline (context_creation→module_lookup→execute→return_result) for pre-validated internal hot paths. Documented in all three SDKs and spec.- Core vs Optional steps overview — New §4.0 in
design-execution-pipeline.mdwith ASCII diagram showing which 4 steps are mandatory and which 7 are removable. - Middleware vs Custom Step selection guide — Decision matrix in
design-execution-pipeline.md§4.2 and practical guide with cross-language examples indocs/guides/middleware.md§11. requires/providesstep dependency metadata — Optional advisory fields onBaseStep(Python),Stepinterface (TypeScript), andSteptrait (Rust).ExecutionStrategywarns at construction and insertion time if a step'srequiresare not satisfied by preceding steps'provides.- Execute step replacement warning —
!!! warningadmonition indesign-execution-pipeline.mdexplaining risks of replacing theexecutestep. - Strategy summary table — All preset strategies documented with step counts, removed steps, and use cases.
- PROTOCOL_SPEC.md pipeline order — Steps 6/7 swapped to match all SDK implementations (
middleware_before→input_validation). Step 2 renamed from "Safety Checks" to "Call Chain Guard". - design-execution-pipeline.md alignment — Step inventory table, code examples, JSON pipeline example, and strategy references updated for correct stage order, naming, and
validate_onlyremoval. validate_onlystrategy removed from spec — Never implemented in any SDK;validate()method withdry_run=Trueprovides the same behavior more cleanly.
- 4 new BaseStep fields —
match_modules(glob patterns for selective step execution),ignore_errors(fault-tolerant continuation on step failure),pure(no side effects, safe for dry-run/validate mode),timeout_ms(per-step timeout in milliseconds). Implemented across all three SDKs (Python, TypeScript, Rust). PipelineContext.dry_run— Whentrue, PipelineEngine skips steps withpure=false, enablingvalidate()to run user-defined pure steps automatically.PipelineContext.version_hint— Passed through to module_lookup for version negotiation.PipelineContext.executed_middlewares— Tracks which middleware ran for on_error recovery chain.StepTrace.skip_reason— Records why a step was skipped:"no_match","dry_run", or"error_ignored".- YAML pipeline configuration (Python) —
pipelinesection inapcore.yamlwithremove,configure, andstepsdirectives. Step resolution viatype(registry) andhandler(import path).
safety_check→call_chain_guard— Renamed across all SDKs to accurately describe the step's purpose (call chain depth/cycle/repeat checks, not transport-level rate limiting).- TypeScript
builtin.prefix removed — All built-in step names in the TypeScript SDK dropped thebuiltin.prefix for cross-SDK consistency (e.g.,builtin.context_creation→context_creation).
- Steps 6 and 7 swapped —
middleware_beforenow executes beforeinput_validation(was the reverse). Middleware transforms are now validated by the subsequent input_validation step, aligning with the Kubernetes Mutating → Validating admission order.
- Middleware transforms now validated — Previously, middleware modifications to inputs were either never re-validated (production code) or silently discarded (pipeline abstraction). The step order swap ensures transformed inputs pass schema validation.
env_styleparameter — Three modes for environment variable key conversion:auto(default, matches againstdefaultstree),nested(single_→.),flat(no conversion). Resolves flat snake_case config key conflicts.max_depthparameter — Limits nesting depth for env var key conversion (default: 5). Prevents excessively deep nesting from long env var names.env_prefixauto-derivation — Whenenv_prefixis not provided, auto-derived from namespace name vianame.upper().replace("-", "_").env_mapparameter — Explicit mapping of bare (unprefixed) env var names to config keys within a namespace (e.g.,{"REDIS_URL": "cache_url"}).Config.env_map()class method — Global bare env var → top-level config key mapping (e.g.,{"PORT": "port"}).CONFIG_ENV_MAP_CONFLICTerror — Raised when the same env var is claimed by multiple env_map registrations.
ContextKey<T>typed accessor — Generic type-safe wrapper forcontext.dataaccess withget(),set(),delete(),exists(),scoped()methods. Available in Python, TypeScript, and Rust.- Built-in context key constants —
TRACING_SPANS,TRACING_SAMPLED,METRICS_STARTS,LOGGING_START,REDACTED_OUTPUT,RETRY_COUNT_BASEexported for middleware authors. _context_versionserialization — Context serialization now includes_context_version: 1for forward compatibility. Deserialization warns on unknown versions but proceeds.- Context
serialize()/deserialize()methods — Explicit serialization API with data key filtering (underscore-prefixed keys excluded).
extrafield onModuleAnnotations— Free-form extension dictionary for ecosystem packages and user metadata (e.g.,extra={"mcp.category": "tools"}).pagination_styletype relaxed — Changed fromLiteral["cursor", "offset", "page"]to openstring, allowing custom pagination strategies.DEFAULT_ANNOTATIONSconstant — Exported frozen default annotations instance.from_dict()classmethod (Python) — Deserializes annotations with unknown keys captured inextra.createAnnotations()factory (TypeScript) — Convenience factory accepting partial overrides.- Canonical snake_case wire format (TypeScript) —
annotationsToJSON()/annotationsFromJSON()for cross-language serialization.
ACLConditionHandlerprotocol — Extensible condition evaluation interface. Python: sync + async protocols. TypeScript:boolean | Promise<boolean>. Rust:#[async_trait].ACL.register_condition()class method — Register custom condition handlers (e.g.,ip_range,time_window).$orand$notcompound operators — Built-in compound condition handlers for OR and NOT logic in ACL rules.async_check()method — Async ACL check alongside existing synccheck(), supporting async condition handlers.- Fail-closed for unknown conditions — Unknown condition keys now log a warning and return False (deny), instead of being silently ignored.
Stepprotocol / interface / trait — Pluggable pipeline step withname,description,removable,replaceable, and asyncexecute().ExecutionStrategyclass — Ordered list of steps withinsert_after(),insert_before(),remove(),replace()modification API.PipelineEngine— Executes strategy steps with index-based loop, skip_to support, trace accumulation, and abort handling.PipelineTrace/StepTrace— Complete execution trace for AI introspection and learning.- 11 built-in steps —
BuiltinContextCreation,BuiltinSafetyCheck,BuiltinModuleLookup,BuiltinACLCheck,BuiltinApprovalGate,BuiltinInputValidation,BuiltinMiddlewareBefore,BuiltinExecute,BuiltinOutputValidation,BuiltinMiddlewareAfter,BuiltinReturnResult. - Preset strategies —
build_standard_strategy()(11 steps),build_internal_strategy()(skip ACL/approval),build_testing_strategy()(minimal),build_performance_strategy()(skip middleware). Executor.strategyparameter — Optional, backward-compatible. When omitted, uses standard 11-step pipeline.call_with_trace()/call_async_with_trace()— Returns(result, PipelineTrace)tuple for observability.register_strategy()/list_strategies()/describe_pipeline()— Strategy introspection API.
- Data key naming convention — Internal middleware keys migrated from legacy names (
_metrics_starts,_usage_starts,_obs_logging_starts) to_apcore.mw.*convention. All middleware now uses typedContextKeyconstants.
- Rust
ApprovalRequestspec alignment — Added requiredcontextfield (Option<Context<Value>>) and changedannotationstype fromHashMaptoModuleAnnotationsper spec §7.3.1. - Rust
DependencyInfofield rename — Renamednametomodule_idfor cross-SDK consistency with Python/TypeScript. - Rust config env fallback — Fixed namespace-mode
APCORE_*env var fallback to resolve to top-level paths instead of incorrectly prependingapcore.prefix. - Rust
config_envconformance test — Added missing conformance test (was 9/10, now 10/10 fixtures). - Rust Context field alignment — Removed non-spec fields (
created_at,parent_trace_id,trace_context). Changedglobal_deadlinefromOption<Instant>toOption<f64>(epoch seconds). - Rust Identity immutability — Fields made private with pub getters. Serde compatibility via
IdentityRawpattern. - TypeScript
globalDeadlinefield — AddedglobalDeadline: number | nullto Context (was missing). - Rust system.control module — Extracted into dedicated
control.rsfile (was inline inmod.rs). - TypeScript
removeRulecomparison — Fixed to use element-wise array comparison instead ofJSON.stringify. - Rust empty callers matching — Empty callers list now matches none (aligned with Python/TypeScript).
- API documentation audit (13 fixes) — Corrected Executor constructor (missing
strategyparam),cache_key_fieldstype (tuple not list),ModuleAnnotations.extrafield, Reserved Words,extensions_dirdefault,ModuleExampledefaults, Contextserialize()/deserialize(),_global_deadline,preflight()/describe()methods,PreflightCheckResult.warnings,$or/$notACL examples. - Cross-language tabbed examples — Added TypeScript tab to system-modules.md, converted client-api.md to 3-language tabs, fixed bare code blocks in 4 API docs.
- 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. - Built-in namespace env prefixes:
APCORE__OBSERVABILITY→APCORE_OBSERVABILITY,APCORE__SYS→APCORE_SYS.
- Config Bus Architecture (§9.4) — apcore.Config upgraded from internal configuration tool to ecosystem-level Config Bus. Any package (apcore ecosystem or third-party) can register a namespace with optional JSON Schema validation, environment variable prefix, and defaults. Design principles: bus not center, zero-cost adoption, gradual integration, cross-language consistency, strict/flexible coexistence
- 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 explicitreload()to apply. Nounregister_namespacein this version - 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)._configreserved namespace for meta-configuration (strict,allow_unknown) - 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 - Per-Namespace Env Override (§9.8) — Each namespace declares its own
env_prefix. Longest-prefix-match dispatch algorithm resolves ambiguity.APCORE_MCPdouble-underscore convention for apcore sub-packages to avoid collision withAPCORE_prefix. Compatibility note for apflow's simpler env convention - 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 - Validation Algorithm A12-NS (§9.10) — Extended A12 for namespace mode: validates
apcorenamespace with original algorithm, validates registered namespaces against their JSON Schema, handles unknown namespaces per strict/allow_unknown settings - 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 - Cross-Language Implementation Requirements (§9.12) — MUST/SHOULD API surface table, language-idiomatic naming matrix, thread safety requirements, parameter passing style note
- Ecosystem Integration Patterns (§9.13) — Convention table for all apcore packages (namespace, env prefix, schema file). Third-party defensive integration pattern. Framework auto-registration examples (Django AppConfig.ready(), FastAPI module-level, NestJS)
- Config Discovery (§9.14) — Optional (
MAY) automatic config file discovery with search order:$APCORE_CONFIG_FILE→./project.yaml→./apcore.yaml→ user-level config - New error codes —
CONFIG_NAMESPACE_DUPLICATE,CONFIG_NAMESPACE_RESERVED,CONFIG_ENV_PREFIX_CONFLICT,CONFIG_MOUNT_ERROR,CONFIG_BIND_ERROR
Three mechanisms addressing ecosystem consistency across apcore, apcore-mcp, apcore-cli, apcore-a2a and third-party packages:
-
Error Formatter Registry (§8.8) — Shared
ErrorFormatterprotocol 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 -
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:observability(APCORE_OBSERVABILITY) — Extractsapcore.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 defaultssys_modules(APCORE_SYS) — Promotesapcore.sys_modules.*flat keys into a dedicated namespace.register_sys_modules()prefersconfig.namespace("sys_modules")in namespace mode withconfig.get("sys_modules.*")legacy fallback
-
Event Type Naming and Collision Fix (§9.16) — Resolves two confirmed collisions in apcore-python's emitted event types:
"module_health_changed"was used for two distinct events (toggle on/off vs. error rate recovery); replaced by canonical namesapcore.module.toggledandapcore.health.recovered"config_changed"was used for two distinct events (key update vs. module reload); replaced byapcore.config.updatedandapcore.module.reloaded- Establishes dot-namespaced naming convention:
apcore.*reserved for core,apcore-mcp.*/apcore-a2a.*/apcore-cli.*for adapters - All four legacy short-form names remain emitted as aliases during transition
docs/features/event-system.md— Fixed severity levels fromwarning/criticaltowarn/fatal, aligning with PROTOCOL_SPEC §10.2docs/features/core-executor.md— Fixed Identity description: removed incorrectpermissionsfield, corrected toid,type,roles,attrsper PROTOCOL_SPEC §5.7docs/features/core-executor.md— Added missingApprovalPendingErrorto Approval Gate description per PROTOCOL_SPEC §7.4docs/features/middleware-system.md— Addedretry.pyto Key Files table (was described in Components but missing from file list)docs/features/observability.md— Added concrete metric names (apcore_module_calls_total,apcore_module_errors_total,apcore_module_duration_seconds) to convenience method documentation
- Fixed duplicate
### 10.5section numbering — renumbered Sensitive Data Redaction to §10.6, Sampling Strategy to §10.7, Span Naming Convention to §10.8 - Fixed
### 8.1.1misnumbered under §9.1 — corrected to §9.1.1 - Fixed
### 10.8.xmisnumbered under §11.8 — corrected to §11.8.1–§11.8.4 - Fixed middleware priority model contradiction between §11.2 (explicit 0-1000) and §12 (registration order) — §12 now aligns with §11.2
- Fixed
on_errorexamples in README.md and concepts.md — changedraise errorto correct return-based contract (return None/return dict) - Updated "Last Updated" date to 2026-03-24
- Added Approval System (§7) and Event System to "Core Protocol Includes" list in SCOPE.md
- Added AI-Perceivable brand definition block to README header
- Added "Perceived → Understood → Executed" progression table to "Why AI-Perceivable?" section
§5.13 Display Overlay (specified in v0.13.0) is now implemented across the official adapter stack:
| Package | Version | What was implemented |
|---|---|---|
apcore-toolkit |
0.4.0 | DisplayResolver — §5.13 resolve priority chain, MCP alias sanitization/64-char limit, CLI alias validation, suggested_alias fallback, binding_path file/directory loading |
apcore-cli |
0.3.0 | CLI command routing from metadata["display"]["cli"]["alias"]; descriptor cache; JSON output reads display overlay |
apcore-mcp |
0.11.0 | MCP tool name and description from metadata["display"]["mcp"]; guidance appended to tool description |
apcore-a2a |
0.3.0 | A2A skill id/description/tags from metadata["display"]["a2a"]; removed dead _build_extensions() |
fastapi-apcore |
0.4.0 | binding_path parameter on create_cli() / create_mcp_server(); DeprecationWarning for simplify_ids=True |
- §5.14 Convention Module Discovery — new optional protocol capability for zero-decorator module registration via
commands/directory convention. Supports cross-language function discovery with schema inference from type annotations.
- Rebrand: aipartnerup → aiperceivable
- Caching annotations (§4.4) —
cacheable,cache_ttl,cache_key_fieldsannotation fields for AI-aware caching decisions - Pagination annotations (§4.4) —
paginated,pagination_style(cursor/offset/page) for paginated result handling - AI Metadata Conventions (§4.6) — 13 standardized
x-metadata keys across 4 categories: Intent, Planning, Performance/Cost, Trust/Verification sunset_date(§5.2) — ISO 8601 date field for module deprecation lifecycleon_suspend()/on_resume()(§5.6, §12.7.3) — Optional lifecycle hooks for state preservation during hot-reload- Hot Reload with State Migration (§12.7.3) — New section with algorithm, constraints, and Python example
- Ecosystem documentation — Added apcore-mcp, apcore-a2a, apcore-cli, and apcore-testing to README and SCOPE
module-meta.schema.json— Addedstreaming,cacheable,cache_ttl,cache_key_fields,paginated,pagination_style,sunset_datedefinitions
- Rebranded from "universal module development framework" to "AI-Perceivable module standard" with three-tier messaging (slogan/subtitle/full definition)
- SCOPE.md — Expanded boundary decisions from 17 to 26 rows; updated requirements wording from "The framework" to "Implementations"
- Section renumbering — §12.7.3–12.7.8 renumbered after hot-reload insertion
- Lifecycle table (§12.7.1) — Reordered to show
on_resumeafteron_load, with note clarifying old/new instance distinction
- Cross-references — Fixed 10+ stale §11.7.x and §12.7.x references across architecture, registry-api, algorithms, and context-object docs
- Metadata key consistency — Aligned
x-max-latency-msdescription between README and PROTOCOL_SPEC
- Error catalog expanded (§8.2) — Added
MODULE_DISABLED,EXECUTION_CANCELLED,RELOAD_FAILEDerror codes with retryability classification and error hierarchy entries - UsageCollector formalized (§10.4) — Added usage tracking specification for
UsageCollectorandUsageMiddlewarebackingsystem.usage.*modules
- Shared conformance fixtures (
conformance/fixtures/) — 7 JSON fixture files for cross-language testing:pattern_matching,specificity,normalize_id,call_chain,error_codes,version_negotiation,acl_evaluation
- Context.child() naming (§12.7.2) — Standardized
derive()→child()to match SDK implementations - Forward-declared errors resolved —
GENERAL_NOT_IMPLEMENTEDandDEPENDENCY_NOT_FOUNDnow fully implemented in both SDKs
- Cross-reference links — Added links between API docs and feature docs (executor-api.md, context-object.md)
- Conformance known deviations — Updated status of error code implementations
- CHANGELOG count corrections — System modules 10→9, APCore client methods 19→17
- Phantom entry removed — TypeScript
batchProcessingannotation (never implemented)
- APCore Client API (
docs/api/client-api.md) — Full API reference for the unifiedAPCoreclient covering 17 public methods:call,call_async,stream,validate,module,register,discover,list_modules,describe,use,use_before,use_after,remove,on,off,disable,enable, plusevents/registry/executorproperties and globalapcore.*entry points - Event System (
docs/features/event-system.md) —EventEmitter,ApCoreEvent,EventSubscriberprotocol,WebhookSubscriber(retry strategy),A2ASubscriber(auth modes), subscriber type factory registry, event types table, and YAML configuration reference - System Modules (
docs/features/system-modules.md) — Complete reference for 9 built-insystem.*modules with input/output schemas:system.health.summary,system.health.module,system.manifest.module,system.manifest.full,system.usage.summary,system.usage.module,system.control.update_config,system.control.reload_module,system.control.toggle_feature; plusregister_sys_modules()setup guide and YAML configuration
- Observability features —
ErrorHistory,UsageCollector,PlatformNotifyMiddlewaredocumented - Middleware guide — Built-in
RetryMiddleware+RetryConfigreference added - Schema system —
SchemaStrategyandExportProfileenums documented
docs/getting-started.md— Added §8 Global Entry Points (16apcore.*module-level functions) and §9 System Modules quick start with health/usage/manifest examplesdocs/api/executor-api.md—validate()return type updated fromValidationResulttoPreflightResultwith 6-check breakdown andrequires_approvalflag;call()/call_async()/stream()signatures updated withversion_hintparameter; addedModuleDisabledError,ModuleTimeoutError,ReloadFailedError,FeatureNotImplementedError,DependencyNotFoundErrorto error types; timeout section rewritten for dual-timeout model with cooperative cancellation (CancelToken+ 5s grace period)docs/api/registry-api.md— Addeddisable()/enable()module toggle,safe_unregister()with cooperative drain (Algorithm A21),acquire()context manager,is_draining(),describe()for AI/LLM tool discovery,negotiate_version()(Algorithm A14)docs/features/observability.md— Added three subsystems:ErrorHistory(ring buffer with deduplication,ErrorEntrydataclass),UsageCollector(hourly bucketed storage, trend computation,UsageMiddleware),PlatformNotifyMiddleware(threshold-based alerting with hysteresis)docs/guides/middleware.md— Replaced hand-writtenRetryMiddlewareexample with built-inRetryMiddleware+RetryConfigreference (exponential/fixed backoff, jitter, retryable-only); added §5.5–5.7 cross-references forErrorHistoryMiddleware,UsageMiddleware,PlatformNotifyMiddlewaredocs/features/schema-system.md— AddedSchemaStrategyenum (yaml_first,native_first,yaml_only) andExportProfileenum (mcp,openai,anthropic,generic)docs/features/core-executor.md— Added dual-timeout model (global deadline + per-module), cooperative cancellation withCancelToken, deep merge for streaming (depth cap 32), error propagation viapropagate_error()(Algorithm A11),PreflightResultvalidationdocs/README.md— Addedclient-api.md,event-system.md,system-modules.mdto directory tree, API reference table, feature specifications table, and concept index
- IDConverter implementation note (§12.2) — SDKs MAY implement as utility function instead of class
- MiddlewareManager split-method pattern (§12.2) — SDKs MAY use
execute_before/execute_after/execute_on_errorinstead of unifiedrun_chain() - Module.stream() optional method (§5.6) — Documented
stream()as optional method in Module interface for streaming support
DependencyNotFoundError— New error class forDEPENDENCY_NOT_FOUNDcode (previously forward-declared)FeatureNotImplementedError(Python) /NotImplementedError(TypeScript) — New error class forGENERAL_NOT_IMPLEMENTEDcode (previously forward-declared)
- Executor pipeline docs — All references updated from "10-step" to "11-step" pipeline across README.md, docs/features/core-executor.md, docs/api/executor-api.md
- Context
loggerproperty — Upgraded from SHOULD to MUST in docs/api/context-object.md (both SDKs already provide it) - docs/api/module-interface.md — Added optional
stream()method documentation - **docs/getting-started.md
** — Rewritten to recommendAPCore` unified client as primary approach
- Executor.validate() preflight (§12.2) —
[SHOULD]non-destructive preflight check through Steps 1–6 without invoking module code or middleware; newPreflightResult/PreflightCheckResulttypes with duck-typeValidationResultcompatibility - §12.8 Executor.validate() Cross-Language Implementation Guide — error handling mapping, type mapping for Python/TypeScript/Go/Rust/Java/C/C++, schema library requirements, naming conventions
- Preflight Tests added to §12.4 Consistency Test Suite (7 test cases)
- Context optional extension fields —
cancel_token,services,redacted_inputswith serialization rules (§5.7) - New error codes —
CONFIG_NOT_FOUND,CONFIG_INVALID,SCHEMA_CIRCULAR_REF,BINDING_FILE_INVALID,MIDDLEWARE_CHAIN_ERROR,VERSION_INCOMPATIBLE,ERROR_CODE_COLLISION,CIRCULAR_DEPENDENCY,DEPENDENCY_NOT_FOUND(§8) - New error classes —
BindingFileInvalidError,MiddlewareChainError,VersionIncompatibleError,ErrorCodeCollisionErroradded to error hierarchy - AI error guidance fields —
retryable,ai_guidance,user_fixable,suggestionsfor improved LLM agent error handling - AI intent metadata keys (§4.6) —
x-when-to-use,x-when-not-to-use,x-common-mistakes,x-workflow-hintsconventions for LLM agents - TypeScript and C/C++ added to §12.6 Language-Specific Implementation Notes
PROTOCOL_SPEC.md— bumped to v1.4.0-draft- Executor pipeline renumbered from 10 steps (with Step 4.5) to clean 11 steps — Approval Gate is now Step 5, subsequent steps shifted +1
- §7.4, §7.9, streaming protocol references updated to match new 11-step numbering
- Executor.validate() preflight added to §12.3 cross-language requirements table
- Section cross-references fixed — §11.7→§12.7, §10.3→§11.3, §9.7→§10.7, §7→§8 (error code references)
- Retryability table updated with new error codes;
MIDDLEWARE_CHAIN_ERRORremoved from forward-declared list docs/api/context-object.md— added optional extension fields documentationdocs/api/executor-api.md— added AI error guidance fields and new error typesdocs/concepts.md— expanded AI collaboration and cognitive interface conceptsSCOPE.md— updated to reflect new features
- AI Collaboration Lifecycle documentation: Integrated
description,metadata,requires_approval, andai_guidanceinto a unified narrative (Discovery, Strategy, Governance, Recovery). - New "Cognitive Interface" concept in
README.mdanddocs/concepts.md. - Intent-oriented design tips in
docs/guides/creating-modules.md. - Comprehensive multi-language "Getting Started" guide covering both Python and TypeScript side-by-side
- Multi-language support (side-by-side examples) in "Creating Modules" guide
- Unified documentation links across all implementation READMEs (
apcore-python,apcore-typescript) - TypeScript examples for Registry, Executor, and Module definition in core documentation
README.md— Updated Quick Start with multi-language tabs and links to the new Getting Started guide
- Approval System (§7) — new section in
PROTOCOL_SPEC.mddefining theApprovalHandlerprotocol,ApprovalRequest/ApprovalResultdata types, Executor Step 4.5 integration, error types (APPROVAL_DENIED,APPROVAL_TIMEOUT,APPROVAL_PENDING), built-in handlers, protocol bridge handlers, phased implementation (Phase A sync, Phase B async), and conformance levels
docs/features/approval-system.md— full specification of the Approval System feature
PROTOCOL_SPEC.md— bumped to v1.3.0-draft; added "Recommended AI Intent Metadata Keys" (§4.6) outliningx-when-to-use,x-when-not-to-use,x-common-mistakes, andx-workflow-hintsconventions for LLM agents; updatedrequires_approvalannotation description to reference runtime enforcement; added approval error codes and error hierarchy; renumbered §7–§13 → §8–§14docs/api/executor-api.md— addedapproval_handlerconstructor parameter,ApprovalDeniedError/ApprovalTimeoutErrorto error types, Step 4.5 to execution flow and state machinedocs/api/module-interface.md— updatedrequires_approvalannotation description to reference Approval System and runtime enforcementdocs/features/core-executor.md— added Step 4.5 (Approval Gate) to the execution pipelinedocs/README.md— added Approval System to feature specifications table, directory tree, and concept index
- Streaming execution protocol — new section in
PROTOCOL_SPEC.mddefining streaming execution model for long-running or real-time module outputs - Module ID constraints & naming conventions — formal rules for module identifiers added to the protocol spec
- SDK export requirements — specified required exports for conformant SDK implementations
- Module interface improvements — refined module interface contracts with streaming semantics
docs/features/acl-system.md— full specification of the Access Control List systemdocs/features/core-executor.md— detailed documentation of the execution pipelinedocs/features/decorator-bindings.md— guide on the@moduledecorator and binding mechanicsdocs/features/middleware-system.md— composable middleware pipeline specificationdocs/features/observability.md— tracing, metrics, and structured logging documentationdocs/features/registry-system.md— module registry and discovery system documentationdocs/features/schema-system.md— schema-driven module input/output validation documentation
- GitHub Actions workflow (
.github/workflows/deploy-docs.yml) for automated documentation deployment - MkDocs configuration (
mkdocs.yml) for the documentation site
- README — enhanced with unified SDK explanation, TypeScript SDK implementation examples, and updated architecture overview
- SCOPE.md — updated to reflect current project scope and feature set
docs/concepts.md— rewritten with unified SDK explanation for multi-language contextdocs/architecture.md— updated to align with protocol spec changesdocs/api/— updatedcontext-object.md,executor-api.md,module-interface.md, andregistry-api.mdwith version-aligned contentdocs/spec/conformance.md— revised conformance requirements to match 0.2.0 protocol specdocs/guides/adapter-development.md— updated adapter development guidemkdocs.yml— navigation structure updated to include new feature pages
ROADMAP.md— removed; roadmap references updated across documentationdocs/guides/creating-modules-translated.md— removed translated guide from navigation
- Level 2 Conformance (Phase 1) — Extension system, Async Task Management, and W3C Trace Context support added to protocol requirements
- Extension System (§12.2) — Unified extension point framework for pluggable components (discoverers, middleware, ACL, exporters)
- Async Task Management (§12.7.3) — Standardized lifecycle for background tasks (Pending, Running, Completed, Failed, Cancelled)
- Trace Context (§12.7.4) — W3C Trace Context (traceparent) support for distributed tracing propagation
- Async Middleware Protocol — Requirements for non-blocking middleware dispatch
- Streaming Support — Formalized
ModuleAnnotations.streamingandExecutor.stream()behavior in protocol specification - Shallow Merge for Streaming — Algorithm for accumulating streaming chunks for output validation and post-processing
- ErrorCodes Catalog — Standardized error code constants (replaces hardcoded strings)
- ContextFactory Protocol — Interface for creating Context from platform-specific requests
- Registry Constants — Standardized module ID patterns and event types
- Comprehensive Schema System — Formalized schema loading, validation, and multi-profile export (MCP, OpenAI, Anthropic) requirements
- Module ID Validation — Strengthened pattern to
^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$(lowercase, digits, underscores, dots; no hyphens) - Registry Event Constants — Standardized event names for module registration lifecycle
Initial Release
- Schema-driven modules - Define modules with Pydantic input/output schemas and automatic validation
- @module decorator - Zero-boilerplate decorator to turn functions into schema-aware modules
- Executor - 10-step execution pipeline with comprehensive safety and security checks
- Registry - Module registration and discovery system with metadata support
- Access Control (ACL) - Pattern-based, first-match-wins rule system with wildcard support
- Call depth limits - Prevent infinite recursion and stack overflow
- Circular call detection - Detect and prevent circular module calls
- Frequency throttling - Rate limit module execution
- Timeout support - Configure execution timeouts per module
- Composable pipeline - Before/after hooks for request/response processing
- Error recovery - Graceful error handling and recovery in middleware chain
- LoggingMiddleware - Structured logging for all module calls
- TracingMiddleware - Distributed tracing with span support for observability
- YAML bindings - Register modules declaratively without modifying source code
- Configuration system - Centralized configuration management
- Environment support - Environment-based configuration override
- Tracing - Span-based distributed tracing integration
- Metrics - Built-in metrics collection for execution monitoring
- Context logging - Structured logging with execution context propagation
- Sync/Async modules - Seamless support for both synchronous and asynchronous execution
- Async executor - Non-blocking execution for async-first applications
- Type safety - Full type annotations across the framework
- Comprehensive tests - 90%+ test coverage with unit and integration tests
- Documentation - Quick start guide, examples, and API documentation
- Examples - Sample modules demonstrating decorator-based and class-based patterns