The handbook
OverviewAgentsCultivatorsDataHub templates
Develop

Develop: DataHub templates

How to author a DataHub template — the opt-in domain model that gives a client's tenant a head start on top of the free-form primitives. The architecture decision is ADR-0002 (docs/adr/0002-template-architecture.md); the in-repo guide is docs/templates/authoring-guide.md; the worked example is packages/datahub/src/datahub/templates/examples/hr_skills/ (an example template — its HR domain is incidental; AcasoOS is domain-general).

The two-layer model

Every tenant always has the platform ontology (acaso_core.ttl): Person, Statement, SourceDocument and friends, addressable in the tenant's knowledge-graph namespace. A template layers a curated domain module on top — an OWL vocabulary (module.ttl), source pipelines, tools, and cultivators — that tenants opt into per tenant via tenant_template_state. Templates evolve independently on their own semver; tenants pin a version.

Layout

packages/datahub/src/datahub/templates/<family>/<name>/
├── __init__.py             # side-effect imports pipelines (and cultivators)
├── module.ttl              # OWL vocabulary (classes + properties)
├── pipelines/
│   ├── __init__.py         # side-effect imports each pipeline
│   └── from_slack.py       # one module per data source
├── cultivators/            # optional — Kinds shipped with the template
├── tools.py                # tools the template exposes to agents
└── README.md

<family> groups domains (sales, eng, hr, …); <name> is the template. Three sibling locations share this internal shape:

  • templates/<family>/<name>/productized catalog templates, reusable across clients. None ship in-tree yet; each lands via its own spec cycle.
  • templates/custom/<client_slug>/client-bespoke work that doesn't generalize, namespaced to one tenant.
  • templates/examples/<name>/reference implementations (examples/hr_skills, examples/perf_evidence). Copy them to start; activate them only for dev/demo tenants, never for clients.

A blank skeleton to copy lives at packages/datahub/src/datahub/templates/_template_skeleton/.

Steps

  1. Spec first. Templates ship via the spec process (specs/): which classes, object properties, pipelines, and tools; the datatype properties per class; whether any new SQL is needed (rare — templates normally live entirely in the KG).
  2. Define the vocabulary in module.ttl as an OWL module (classes, object/datatype properties, bilingual labels, acaso:embeddable where search should see the class). People-like classes subclass acaso:Person — the erasure flow depends on it.
  3. Author pipelines under pipelines/. A pipeline consumes ingest events and writes graph structure. Every write goes through datahub.kg.individuals (upsert_individual / link) — deterministic IRIs keep re-ingest idempotent, and embeddable classes enqueue their vectors automatically. End each module with register_pipeline(PipelineDescriptor(...)).
  4. Author tools in tools.py — tenant-scoped query functions over the template's entities. Agents consume them today by wrapping them in @tool functions (the knowledge = ["<family>/<name>"] auto-wiring is registry metadata in v0, not active tool injection).
  5. Ship cultivators under cultivators/ if the template should write memory autonomously — see Develop: Cultivators.
  6. Activate per tenant through the admin API:
bash
curl -sS -X POST https://<host>/v1/admin/tenants/<slug>/templates \
  -H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
  -d '{"template_key": "<family>/<name>", "version": "1.0", "config": {}}'
  1. Test end-to-end under packages/datahub/tests/integration/ against the docker-compose Postgres (AGE + pgvector): event in, nodes/edges out, both stores consistent, RLS isolation asserted.

Migrations

If the template genuinely needs new tables, the alembic migration ships in the same PR. tenant_id columns plus ENABLE/FORCE ROW LEVEL SECURITY and a tenancy policy are mandatory; CI's tenancy lint blocks tables without them. Migrations are append-only and must survive make migration-check (upgrade → downgrade → upgrade).

Versioning

Templates follow semver, pinned per tenant in tenant_template_state.version:

  • Backward-compatible additions (new entity types, new optional fields) — same MINOR.
  • Entity-shape changes (rename, drop, type change) — version bump plus a migration script for existing tenant data.

Ship checklist

  • Spec exists; layout matches the skeleton; entity/edge constants exported.
  • All graph writes via pipelines.nodes; no direct AGE writes; no raw cross-tenant queries.
  • Integration tests green against compose Postgres; all make gates green.
  • Migration (if any) carries RLS and passes make migration-check.
  • Tenant activation recorded for each client that opts in.
PreviousDevelopCultivatorsNext ReferenceEnvironment variables