Extensions Contract
Everything a domain extension must (and may) implement for the platform to load it correctly
For each REGISTER, TABLE, or PROGRAM_REGISTER in g2p_register_definitions:
Live ORM
G2PRegister{Mnemonic}
History ORM
G2PRegisterHistory{Mnemonic}
Intake ORM
G2PIntakeForm{Mnemonic}
Live schema
G2PRegisterSchema{Mnemonic}
History schema
G2PRegisterHistorySchema{Mnemonic}
Intake schema
G2PIntakeFormSchema{Mnemonic}
Domain service
G2PRegisterDomainService{Mnemonic}
Export all classes from package __init__.py files. CORE_TABLE registers (e.g. Score) use core ORM - seed metadata only; optional G2PScoreComputeService{Type}.
Domain service methods
validate_domain_attributes
Yes
Business rules before intake save / change-request create
construct_record_name
Yes
Display name for live register rows and change requests
construct_search_text
Yes
Full-text search string
construct_intake_record_name
Recommended override
Display name for intake-form rows; default appends application_reference to construct_record_name
pre_approve
Optional
Before approval commits (session available)
post_approve
Optional
After approval commits (session available)
post_ingest
Optional
After intake → register insert (session available)
validate_intake_parent_link
Optional
Enforce rules on resolved intake parent links
construct_record_name, construct_intake_record_name, and construct_search_text are synchronous operations. They cannot query related registers.
Package-level requirements
G2PRegisterDomainFactory
Standard boilerplate resolves G2PRegisterDomainService{Mnemonic} via importlib
app.py Initializer
CoreInitializer() → factory → optional eager domain services
migrate_database()
Must call create_migrate() on every domain ORM class
pyproject.toml
name and [tool.hatch.version] point at your own package. No source map — see below. A readme = "README.md" line means the file must exist, or the build fails
Celery workers do not run migrate_database(), API containers must start first.
The module alias — two halves that look contradictory
Your package installs under its own import name (
openg2p_registry_<domain>_extension). Do not add a[tool.hatch.build.targets.wheel.sources]map ontoopeng2p_registry_extensions. That was the pre-1.0 mechanism; it prevents your extension from coexisting with the platform's reference extension in one image.Code the platform resolves must nevertheless import the alias. The container entrypoint (
openg2p_registry_staff_api/main.pyand its siblings) installs the module named byREGISTRY_EXTENSION_MODULEintosys.modulesasopeng2p_registry_extensionsbefore any platform import runs. So the factories do:Changing that to your own package name works in your image and breaks the moment the reference extension is the one selected. Copy the factories unchanged — the only thing you adjust is which services exist.
Rule of thumb: package name → yours. Import target inside the factories → openg2p_registry_extensions.
Optional components
G2PIdGeneratorService
register_domain/id_generator/
Lazy via factory; keys match Helm idTypes (lowercase)
G2PScoreComputeService{Type}
score_compute/services/
Factory; score_type in metadata → class name
Enrichers
ingestion_pipeline/enricher_services/
Exact class name in semantic pattern config
Jinja templates
templates/ingest, templates/outgest
Upload to MinIO; and attach to related references and g2p_registry_documents
Last updated
Was this helpful?