Skip to content

Instance & ManifestInstance

Instance is the universal wrapper for every manifest instance — doc.kind, doc.name, doc.spec, doc.metadata, doc.typed, doc.origin. ManifestInstance is the blessed query surface: the one documented read API (all, one, root, default_agent, find_agent, build_prompt, resolve) that the guides and examples teach.

Instance

dna.kernel.instance.Instance

Instance(
    api_version: str,
    kind: str,
    name: str,
    metadata: dict[str, Any] | None = None,
    spec: dict[str, Any] | None = None,
    raw: dict[str, Any] | None = None,
    typed: Any | None = None,
    origin: str = "local",
)

Bases: Generic[SpecT]

Universal wrapper. Works for parsed BaseKind AND raw dict.

doc.spec and doc.metadata always return SpecDict at runtime — a dict subclass with attribute access. When typed via Instance[MySpec], the type checker treats doc.spec as MySpec, enabling autocomplete + typo detection at edit time without a runtime cast.

The typed model is still available via doc.typed.

metadata cached property

metadata: SpecDict

Always returns SpecDict — typed metadata when available, raw dict otherwise.

spec cached property

spec: SpecDict

Always returns SpecDict — typed spec when available, raw dict otherwise.

status property

status: dict[str, Any] | None

The DERIVED status block, or None when there is nothing to report.

Never authored and never stored (the write path strips it): the kernel computes it at read time from the Kind's current state. Today it says exactly one thing — that this instance's Kind was REVOKED, so the instance is no longer valid (i-085). See :mod:dna.kernel.validity.

is_valid property

is_valid: bool

Whether the kernel has anything AGAINST this instance.

True is "nothing to report", not "verified" — absence of a status is the ordinary case for every instance of every Kind. It goes False when the Kind has been revoked, and back to True the moment it is approved again, because validity follows the Kind's CURRENT state and is never a stamp on the instance.

from_raw classmethod

from_raw(
    raw: dict[str, Any], typed: Any | None = None
) -> Instance[Any]

Create an Instance from a raw dict.

Returns Instance[Any] because the spec type is determined at the call site by the consumer's annotation; the factory itself cannot infer it. Consumers using typed access: doc: Instance[PageIndexSpec] = Instance.from_raw(raw) get the right type via the annotation.

ManifestInstance

dna.kernel.manifest.ManifestInstance

ManifestInstance(
    scope: str,
    instances: list[Instance],
    kinds: dict[tuple[str, str], KindPort],
    source: SourcePort | None = None,
    resolve_errors: list[str] | None = None,
    kernel: Any = None,
    profiles: list | None = None,
    lazy: bool = False,
)

Facade over a loaded manifest scope.

Marco B (s-lazy-manifest-instance-class, 2026-05-14): supports a LAZY mode where the constructor only receives bootstrap docs (Genome + KindDefinition + LayerPolicy + UA index ~30 docs) instead of the full scope (often 1500+). In lazy mode:

  • one(kind, name) → delegates to kernel.get_instance (L2 cached, ~5ms).
  • all(kind) → delegates to kernel.query (~10ms indexed).
  • instances → triggers _ensure_loaded() which materializes the full scope. Emits a DeprecationWarning when called in lazy mode — callers should use one/all or kernel.query directly.

Bootstrap kinds (always eager) keep mi.root, mi.kind_for, namespace getters (mi.composition, mi.nav) functional cheaply. Heavy operations like mi.summary() / mi.composition.validate() that iterate the full scope still force the materialization, but that's the explicit ask.

To enable lazy mode: pass lazy=True to the constructor (the Kernel does this when DNA_LAZY_MI=1). When lazy=False (default), ManifestInstance behaves exactly as before — full back-compat for tests, agent paths, and external callers.

instances property writable

instances: list[Instance]

Materialized list of all docs. In lazy mode, accessing this triggers a full load (the only way to honor list semantics). Prefer one(kind, name) or all(kind) which stay lazy.

Emits a DeprecationWarning in lazy mode the first time the full set is materialized.

prompt property

prompt: PromptBuilder

Namespace: mi.prompt.build().

composition property

composition: CompositionEngine

Namespace: mi.composition.validate().

nav property

nav: Navigator

Namespace: mi.nav.describe() / summary() / inventory().

lock property

lock: LockManager

Namespace: mi.lock.generate().

root cached property

root: Instance | None

The manifest's root instance (the Genome), or None when the scope has no doc whose KindPort is marked is_root.

composition_result cached property

composition_result: CompositionResult

Validate cross-kind references. Returns resolved + missing + warnings.

profile_for

profile_for(doc: Any)

Find the CompositionProfile for a doc's kind (via alias).

all

all(kind: str) -> list[Instance]

Return all docs of kind — DEPRECATED, will be removed in 1.0.

s-blessed-query-surface: the blessed query surface is mi.instances (in-memory, filter by d.kind) plus kernel.query(scope, kind) for indexed / record-plane reads. This method survives as a warning shim until 1.0.

all_async async

all_async(
    kind: str, *, tenant: str | None = None
) -> list[Instance]

Async-native variant of all() — bridge for callers migrating to await kernel.query(scope, kind).

f-mi-class-extinction (Story s-mi-async-bridge, 2026-05-14): new API that callers should target during the MI sweep. Returns the same list[Instance] shape as sync all() but uses await kernel.query end-to-end — no thread, no asyncio.run, no loop-mismatch with asyncpg pools.

Bootstrap kinds (Genome, KindDefinition, LayerPolicy) are served from the in-memory self._instances (no query). Lazy-cached kinds (already materialized by a previous call) are served from self._lazy_kind_cache.

Tenant note: does not auto-apply tenant overlay from this MI's resolved layer context. Callers that need tenant filtering should pass tenant via kernel.query(..., tenant=...) directly. The MI overlay-merge is applied at doc content level and is observable via this method only when the underlying layer storage already filters by tenant (Postgres adapter does).

all_where

all_where(predicate) -> list[Instance]

Return all instances whose registered KindPort satisfies a predicate. Forces full materialization in lazy mode (cross-kind walk requires the whole scope).

one_async async

one_async(
    kind: str, name: str, *, tenant: str | None = None
) -> Instance | None

Async-native variant of one() — bridge for callers migrating to await kernel.get_instance(scope, kind, name).

f-mi-class-extinction (Story s-mi-async-bridge, 2026-05-14): new API that callers should target during the MI sweep. Same return shape as sync one(). Bootstrap kinds + lazy-cached kinds short-circuit; otherwise delegates to await kernel.get_instance (L2 cached).

one

one(kind: str, name: str) -> Instance | None

Lookup single doc by (kind, name) — DEPRECATED, will be removed in 1.0.

s-blessed-query-surface: the blessed query surface is mi.instances (in-memory, search by d.kind/d.name) plus kernel.get_instance(scope, kind, name) for indexed / record-plane reads. This method survives as a warning shim until 1.0.

read_spec

read_spec(
    kind: str, name: str, field: str, *, default: Any = None
) -> Any

Read a single field from instance.spec.

read_metadata

read_metadata(
    kind: str, name: str, field: str, *, default: Any = None
) -> Any

Same contract as read_spec but reads from instance.metadata.

read_spec_list

read_spec_list(kind: str, name: str, field: str) -> list

Read a list-typed spec field, returning [] when missing or None.

default_agent

default_agent() -> Instance | None

The agent Instance the root Genome names as its default (spec.default_agent via the root KindPort), or None when there is no root or no such agent.

list_kinds

list_kinds() -> list[str]

Sorted list of the distinct Kind names present in this manifest's loaded instances.

render_doc

render_doc(kind: str, name: str) -> list[PreviewBlock]

Polymorphic per-kind preview.

consumers_of

consumers_of(kind: str, name: str) -> list[dict[str, str]]

Walk the manifest and return every doc that references this one.

is_root_doc

is_root_doc(doc: Any) -> bool

Public alias for _is_root_doc — part of the blessed public surface.

kind_for

kind_for(kind: str) -> Any | None

Return the KindPort registered for kind (by kind name), or None.

kind_for_alias

kind_for_alias(alias: str) -> Any | None

Return the KindPort whose alias matches, or None.

iter_doc_deps

iter_doc_deps(doc: Any) -> list[dict[str, Any]]

Iterate an instance's declared dep_filters dynamically.

get

get(kind: str | None = None) -> list[dict[str, Any]]

List instances as light {kind, name, apiVersion} dicts — all of them, or only those of kind. For full Instances use instances / all() instead.

describe

describe(kind: str, name: str) -> str

Human-readable description of one instance (metadata, spec highlights, relationships) — delegates to the Navigator.

summary

summary() -> str

Plain-text overview of the manifest (scope + the instances loaded, grouped by kind) — delegates to the Navigator.

inventory

inventory() -> dict[str, Any]

Structured inventory of everything loaded in this manifest.

dependency_tree

dependency_tree() -> dict[str, Any]

Build a dependency tree for every instance that has dep_filters.

ref

ref(value: str) -> str

Resolve a file reference via source, or return value as-is.

Sync entry-point. Use this from CLI / tests / sync workers whose top-level entry is asyncio.run. Async callers MUST use 🇵🇾meth:ref_async — running this from inside an event loop with a postgres source orphans the asyncpg pool.

ref_async async

ref_async(value: str) -> str

Async variant of 🇵🇾meth:ref.

Use from inside the harness event loop (lifespan, request handlers, async middleware). Awaits source.resolve_ref directly on the caller's loop, which is the same loop that owns the asyncpg pool — no cross-loop dispatch needed.

build_prompt

build_prompt(
    agent: str | None = None,
    context: dict[str, Any] | None = None,
    enabled_skills: list[str] | None = None,
    enabled_guardrails: list[str] | None = None,
    enabled_slots: dict[str, list[str]] | None = None,
) -> str

Build system prompt via template cascade.

Sync — uses _run_sync_helper to await source ref() coroutines. Callers inside an async event loop must use build_prompt_async to avoid the "called from inside a running loop" guard.

build_prompt_async async

build_prompt_async(
    agent: str | None = None,
    context: dict[str, Any] | None = None,
    enabled_skills: list[str] | None = None,
    enabled_guardrails: list[str] | None = None,
    enabled_slots: dict[str, list[str]] | None = None,
) -> str

Async variant of 🇵🇾meth:build_prompt. Use from inside an async caller (test, middleware, etc.) so the ref_async path keeps the source pool's loop binding intact.

explain_prompt

explain_prompt(
    agent: str | None = None,
    *,
    context: dict[str, Any] | None = None,
    enabled_skills: list[str] | None = None,
    enabled_guardrails: list[str] | None = None,
    enabled_slots: dict[str, list[str]] | None = None,
    tenant: str | None = None
) -> "PromptExplanation"

Compose agent AND return per-section provenance.

The prompt field is byte-identical to 🇵🇾meth:build_prompt; sections attributes each composed section (instruction, soul, skills, guardrails) to its source artifact, hash, version, and layer/overlay origin. Sync — see 🇵🇾meth:build_prompt.

explain_prompt_async async

explain_prompt_async(
    agent: str | None = None,
    *,
    context: dict[str, Any] | None = None,
    enabled_skills: list[str] | None = None,
    enabled_guardrails: list[str] | None = None,
    enabled_slots: dict[str, list[str]] | None = None,
    tenant: str | None = None
) -> "PromptExplanation"

Async variant of 🇵🇾meth:explain_prompt.

find_agent

find_agent(name: str) -> Instance | None

Find the best prompt-target instance matching name.

Considers prompt_target_priority when multiple kinds match. Public API — use this instead of _find_agent().

resolve

resolve(
    layers: dict[str, str] | None = None,
) -> ManifestInstance

Apply layer overlays (sync). Delegates to Kernel.resolve_layers().

Memoizes per-layers so repeated mi.resolve({"tenant": X}) calls from request handlers reuse the same merged instance instead of re-reading every doc from disk. Cache lives on the base mi; MIHolder.reload() swaps the base instance, which discards this cache transparently.

From inside an event loop, prefer await mi.resolve_async(layers) — the sync path falls back to a ThreadPool/asyncio.run dance that orphans pool-based source adapters.

resolve_async async

resolve_async(
    layers: dict[str, str] | None = None,
) -> ManifestInstance

Async variant of resolve. Use from inside an event loop (request handlers, lifespan, EventBus consumer) so the source pool stays tied to the caller's loop.

apply_hooks

apply_hooks() -> None

Ask every loaded extension to activate the instances it owns.

Historically this method WAS the activation: it read Hook.spec field by field (target/type/action/fields/body, exec()-ing the body, branching on the middleware/event and inject_fields/log/script enums), then read SafetyPolicy.spec (scope/action/rules) and built a dna.safety.scanner.ScannerPipeline. Two Kinds' schemas, parsed in the kernel, while the extensions that register those Kinds did nothing but kernel.kind(...) — the dependency pointing the wrong way.

i-112 (board dna) inverted it, and MOVED rather than declared: a trait would have swapped two strings and left every field read exactly where it was, which is the shape i-109 refused because it makes the boundary LOOK fixed. The behaviour now lives in HookExtension and SafetyPolicyExtension via :class:~dna.kernel.protocols.ManifestActivator; what is left here is the entry point, and it names no Kind.

The NAME stays apply_hooks: it is published API (the golden port surface, docs/concepts/builtin-kinds.md, and HookKind.docs itself all say a Hook is registered "when ManifestInstance.apply_hooks() is called"). A rename here would have been the cleanup that breaks every caller for a nicer word.

generate_lock

generate_lock() -> 'Lockfile'

Generate lockfile with SHA256 per instance.