Work with typed registries¶
Use this guide when you already understand the basic std.registry model and need to choose a subject, migrate an existing catalogue, publish it through a facade, inspect dependency metadata, or diagnose a rejected entry.
Choose the subject kind¶
Declare every subject kind the registry accepts:
pub static commands: Registry[CommandId, CommandSpec] = Registry.define(
subjects=[SubjectKind.Function, SubjectKind.Method],
)
Use the narrowest set that matches the domain. A function-only catalogue should not admit package entries merely because another registry might need them.
| Subject | Source form | Use it for |
|---|---|---|
Function |
@describe(...) on a function |
Commands, transforms, handlers, adapters |
Method |
@describe(...) on a concrete method |
Operations whose identity includes an owning type |
CompilationUnit |
A static registry.entry(...) with RegistrySubject.current_unit() |
Facts owned by one source module |
Package |
A static registry.entry(...) with RegistrySubject.package() |
Facts owned by the package as a whole |
Traits are not method subjects in the initial surface because a trait declaration does not identify one concrete runtime method. Named modules are also outside the initial surface; module tests: remains a test grouping rather than a general module declaration.
Describe a method¶
The registry remains a module static. Apply @describe to a concrete class, model, enum, or newtype method accepted by the compiler:
from std.registry import Registry, SubjectKind, describe
@derive(Clone, Eq)
pub type FormatterId = newtype str
@derive(Descriptor)
pub model FormatterSpec:
pub media_type: str
pub static formatters: Registry[FormatterId, FormatterSpec] = Registry.define(
subjects=[SubjectKind.Method],
)
class JsonFormatter:
@describe(formatters, FormatterId("json"), FormatterSpec(media_type="application/json"))
pub def render(self, value: str) -> str:
return value
The subject identity includes the owning type and method. Importing or reexporting the type does not manufacture a second registry entry.
Describe a compilation unit or package¶
Do not create a fake function just to attach metadata. Construct an explicit static entry:
from std.registry import Registry, RegistryEntry, RegistrySubject, SubjectKind
@derive(Clone, Eq)
pub type CapabilityId = newtype str
@derive(Descriptor)
pub model CapabilitySpec:
pub summary: str
pub static capabilities: Registry[CapabilityId, CapabilitySpec] = Registry.define(
subjects=[SubjectKind.CompilationUnit, SubjectKind.Package],
)
pub static module_capability: RegistryEntry[CapabilityId, CapabilitySpec] = capabilities.entry(
key=CapabilityId("text.normalization"),
subject=RegistrySubject.current_unit(),
descriptor=CapabilitySpec(summary="Text normalization supplied by this module"),
)
pub static package_capability: RegistryEntry[CapabilityId, CapabilitySpec] = capabilities.entry(
key=CapabilityId("catalogue"),
subject=RegistrySubject.package(),
descriptor=CapabilitySpec(summary="Package-wide function catalogue"),
)
The compiler replaces the subject placeholder with the canonical checked module or package identity during lowering. Runtime code receives the same resolved identity; it does not discover package ownership dynamically.
Reexport a registry and its subjects¶
Keep the registry and described declarations source-owned, then expose them through an ordinary public facade:
pub from crate.text import functions, normalize
The checked entry retains its original registry and subject identity. Inspection and codegraph output add the facade paths under reexport_paths; they do not duplicate the entry or transfer ownership to the facade.
Inspect a local or dependency registry¶
Inspect a local module-level identity:
incan inspect registry text::functions --project . --format json
The selector may also use dots: text.functions. If two packages publish the same module-level identity, qualify the selector with the package:
incan inspect registry analytics-kit::text::functions --project . --format json
Dependency inspection reads the checked registry projection embedded in the dependency's .incnlib; it does not load dependency source or run dependency initialization. Only public registries and public entries are visible to consumers. Local source inspection may include private registries owned by the selected package.
Migrate a custom runtime registry¶
Use this sequence when a library currently registers functions through its own decorator or mutable queue:
- Define a domain key type and a descriptor model. Put domain fields in that model instead of adding registry-specific keyword arguments.
- Add
@derive(Descriptor)to the descriptor model and replace dynamic descriptor expressions with structural values. - Define one
Registry[K, T]as the canonical declaration authority. - Replace the custom registration decorator with
@describe(registry, key, descriptor). - Replace runtime enumeration with
loaded_entries()where process-local behavior is intended. - Replace source scanning or documentation extraction with
incan inspect registry. - Remove the old mutable or queued registry. Keeping it as a fallback would create two authorities that can disagree.
- Test direct imports, facade reexports, package consumers, compiled test batches, inspection JSON, and generated Rust.
Dynamic-only registration may remain custom when entries genuinely depend on runtime values. Document that such a catalogue has no complete static projection rather than presenting it as equivalent to std.registry.
Diagnose rejected entries¶
| Failure | Check |
|---|---|
| Registry argument is rejected | It must resolve to the declared Registry[K, T], not another value with a similar name. |
| Key or descriptor type mismatch | The key must be K and the descriptor must be T; implicit string-shaped metadata is not accepted. |
| Descriptor is not structural | Add @derive(Descriptor) and remove mutable containers, functions, Rust handles, open generics, or recursive descriptor graphs. |
| Subject kind is rejected | Add the required SubjectKind to Registry.define(...) or choose the correct registry. |
| Duplicate key is rejected | Give each entry a unique typed key within that registry. Reexports do not require a new entry. |
| Dependency registry is missing | Rebuild the dependency with an SDK that publishes RFC 113 metadata; older .incnlib artifacts do not contain the projection. |
| Selector is ambiguous | Use package::module::registry instead of the shorter module-local identity. |
For the complete type and JSON contracts, see the std.registry reference. For the design boundary between loaded and checked views, see Checked catalogues and loaded registries.