Releases: aiperceivable/apcore
Release 0.21.0 (2026-05-06)
Added
- §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.
Notes
- 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.
Release 0.20.0
[0.20.0] - 2026-05-05
Added
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).
Changed
- 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 heade...
Release 0.19.0 (2026-04-19)
Changed
- 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.
Added
- 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).
Fixed
- 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.
Deprecated
- 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.
Release 0.18.0 (2026-04-15)
Breaking changes in this release. See
MIGRATION-v0.18.mdfor the consolidated migration guide covering all four repositories.
Added
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 / 7 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.
Fixed
- 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 — Alg...
v0.17.1
[0.17.1] - 2026-04-06
Added
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.
Fixed
- 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.
v0.17.0
[0.17.0] - 2026-04-04
Added
Pipeline v2 — Declarative Step Metadata
- 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).
Changed
Pipeline Step Rename
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).
Pipeline Step Order
- 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.
Fixed
- 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.
Release 0.15.1
Changed
- 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.
Release 0.15.0
Added
Protocol Specification (v1.6.0-draft)
- 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
Cross-Package Consistency (§8.8, §9.15, §9.16)
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
v0.16.0
[0.16.0] - 2026-04-03
Added
Config Bus Enhancements
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.
Context Redesign
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).
Annotations Extension
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.
ACL Condition Handlers
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.
Execution Pipeline Strategy
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.
Changed
- 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.
Fixed
- 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.
Release 0.14.0 (2026-03-24)
Fixed
Specification Documents
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
Protocol Specification
- 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
Scope Document
- Added Approval System (§7) and Event System to "Core Protocol Includes" list in SCOPE.md
README
- Added AI-Perceivable brand definition block to README header
- Added "Perceived → Understood → Executed" progression table to "Why AI-Perceivable?" section
Ecosystem
§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.