Entity extensions add schemas. Developer registries add physical behavior. Both plug in without changing the workspace API.
1. Two Extension Layers
HeavenBase separates extension work into two layers that share the hb.ext import path but serve different authors:
| Layer | Audience | What it adds | Primary API |
|---|
| Entity extension | Application and package authors | Optional Entity classes and MetaSchema rows | ExtensionSpec, register_extension, ws.enable_extension |
| Developer extension | Backend and compiler authors | Backends, handlers, strategies, logical types, ops | hb.ext.register_backend_builder, register_strategy, register_handler, … |
Entity extensions publish structure through normal workspace registration. Developer extensions publish behavior through process-global registries that the workspace planner and handler seeding consume at runtime.
2. Built-In System and Prompt Extensions
HeavenBase ships one required built-in entity extension: system. Every HeavenBase(...) workspace enables it automatically, and default configuration also loads prompt.
The system implementation lives under src/heavenbase/extensions/system/; Prompt and Translation live under src/heavenbase/extensions/prompt/. Import hb.Prompt, hb.Capsule, and hb.Toolkit from import heavenbase as hb.
2.1. Built-In Entities
| Entity id | Class | Purpose |
|---|
sys-catalog | Catalog | Concrete object discovery |
sys-metaschema | MetaSchema | Structure, capabilities, extensions |
sys-prompt | Prompt | Callable prompt rows from the prompt extension |
sys-translation | Translation | Prompt-bound translations from the prompt extension |
sys-capsule | Capsule | Executable Capsule manifests |
sys-toolkit | Toolkit | Toolkit manifests |
ConfigLayer (sys-config-layer) exists in the same package but is not part of system_entities() and is not auto-enabled.
2.2. What Changed in the Architecture Optimization Pass
Capsule and Toolkit remain under the required system extension; Prompt and Translation live in the default-loaded prompt extension with the same root package API.
Lazy helpers such as ensure_prompt_entities(ws) call ws.enable_extension("prompt"); Capsule registry setup calls ws.enable_extension("system") when it needs system rows.
3. Entity Extension API
Define and register entity extensions before workspaces load them:
import heavenbase as hb
from heavenbase.ext import Extension, ExtensionSpec, register_extension
class AuditLog(hb.Entity):
identifier = "audit-log"
event = hb.field(hb.ShortText)
register_extension(
Extension(
ExtensionSpec(
identifier="audit",
name="Audit Log",
desc="Optional audit rows.",
entities=(AuditLog,),
tags=("audit",),
)
)
)
ExtensionSpec fields:
| Field | Role |
|---|
identifier | Stable extension id (same naming rules as workspace ids) |
name, desc, version | Human-readable metadata published to MetaSchema |
entities | Entity classes registered when the extension is enabled |
required | When True, the extension cannot be skipped (only system uses this today) |
tags, meta | Extra metadata for discovery and tooling |
Enable an extension in one workspace:
ws = hb.HeavenBase("demo", preset="debug")
spec = ws.enable_extension("audit")
print(spec.identifier)
print(ws.extensions())
Enable custom extensions on every new workspace through config:
# heavenbase.extensions.default: ["audit", "my-package"]
Entity extensions do not bypass storage planning. Enabled entities still go through ws.register(...) and normal routing.
4. Developer Extension Registries
Developer extensions register physical behavior in process-global registries exported from hb.ext:
| Registry | Register function | Used by |
|---|
| Backends | register_backend_builder, register_backend_class | Config loading, storage placement, handler seeding |
| Handlers | register_handler, HandlerRegistry.register | Query compilation |
| Strategies | register_strategy | Storage placement and handler lookup |
| Logical types | register_logical_type | Handler seeding and storage profiles |
| Operations | register_op | JSON query parsing and handler seeding |
| Storage profiles | register_storage_profile | Default field placement |
Handlers compile one logical operation into one QueryFragment. They do not execute IO; backends execute fragments.
import heavenbase as hb
def compile_ends_with(field_schema, value, ctx):
def payload(row):
return str(row.get(field_schema.name, "")).endswith(str(value))
return hb.ext.QueryFragment(ctx.backend, "ends_with", field_schema.name, payload)
hb.ext.register_handler(
"demo",
hb.ShortText,
"ends_with",
"inmem",
"suffix-index",
compile_ends_with,
)
Provider-native handler plugins live in heavenbase.handlers.plugins and register through register_handler_plugin. Built-in seeding consumes registries and plugins instead of hard-coded provider lists.
5. Discover Capabilities
Users and extension UIs should discover choices through the public capability index instead of reading registry internals:
import heavenbase as hb
ws = hb.HeavenBase("demo", preset="debug")
hb.capabilities.logical_types()
hb.capabilities.strategies(hb.Vector)
hb.capabilities.backends(hb.Array, hb.SideTable, op="array_contains")
hb.capabilities.ops(hb.ShortText, hb.InlineColumn, backend="sqlite")
# Same methods on ws.capabilities(), filtered to configured backends
ws.capabilities.backends(hb.Vector, hb.VectorIndex, op="near")
Each option exposes to_dict() for building pickers or diagnostics, and workspace construction mirrors common registry metadata into sys-metaschema.
6. Validation Checklist
After adding an extension surface:
- Register the component in the correct role package under
src/heavenbase/.
- Export it from the package
__init__.py and top-level heavenbase when it is built in.
- Add tests in
tests/test_extensions.py, tests/test_backends.py, or a focused test module.
- Run
rtk bash scripts/test.bash from the HeavenBase repository root.
See demos/developer/03_write_an_extension.py for a minimal custom extension demo.
Further Exploration