ADR-004: Variety entities — embedded in commodity profile by default
Status: accepted (2026-05-15) Date decided: 2026-05-15
Context
Engineering Spec §3.1’s entity_type enum allows both commodity AND variety
as valid Taproot entity types. The avocado.md exemplar uses typed wikilinks
[[variety:hass|Hass]] implying varieties are addressable as their own
entities. But the GDF v2.1.1 source profile data (in knowledge-forge/data/ exports/<commodity>_adapted.json) embeds variety information INSIDE the
commodity profile’s s04_varieties.variety_summaries[] array.
Two structural patterns are possible:
| Pattern | Storage | Wikilink resolution | Maintenance |
|---|---|---|---|
| A. Variety as own entity_profile | Separate variety_profile_<id> doc per variety | [[variety:hass]] → standalone doc page | 339 commodities × ~5-50 varieties each = potentially 5,000-15,000 variety profile rows |
| B. Variety embedded in commodity | Variety data in s04_varieties.variety_summaries[] of commodity profile | [[variety:hass]] → anchor in commodity profile (commodity_profile_avocado#hass) | One doc per commodity covers its varieties |
| C. Hybrid (selective promotion) | Most varieties embedded; some promoted to own entity_profile | Embedded varieties resolve to anchors; promoted varieties resolve to standalone docs | Configurable per-variety basis |
Decision
Default to embedded (Pattern B). Promote a variety to its own
variety_profile_<id> entity_profile only when one of these conditions holds:
-
Substantial unique data — the variety has its own regional cultivar variants, GAP certification specifics, or storage/handling that diverges meaningfully from the commodity-level baseline (e.g., “Hass Gem” or “Ataulfo Mango” might warrant their own profiles)
-
Cross-commodity ambiguity — the variety name is shared across multiple commodities and disambiguation matters (rare but exists — “Lady” varieties span apples + grapes; “Honeycrisp” is unambiguous)
-
Tenant-specific certification or contract scope — when a customer contract explicitly references a variety by name (e.g., “all Honeycrisp shipments must include Tier-1 certification”) and the agent needs a single citation target
Rendering implications:
- The commodity_profile_renderer extracts
linked_entitiesfor each variety ins04_varieties.variety_summaries[]— that ARRAY is what makes the variety addressable for graph traversal even when no separate doc exists [[variety:hass]]wikilinks resolve via the renderer/parser:- First try: lookup
entity_doc_projectionforentity_domain=variety, entity_key=hass— if avariety_profile_hassdoc exists, link to it - Fallback: resolve to
commodity_profile_<parent_commodity>#<variety_id>anchor (the renderer’s variety section gets per-variety anchors)
- First try: lookup
Consequences
Positive
- Maintenance scale stays sane — 339 commodity profiles vs potentially 10,000+ if every variety got its own profile
- Data lives where it’s authored — knowledge-forge already structures varieties inside commodity profile JSONB; embedded matches the source
- Renderer simpler — one renderer per commodity covers all its varieties
- Wikilinks still typed —
[[variety:hass]]is the canonical form whether it resolves to anchor or standalone doc; authors don’t have to know - Cross-commodity variety queries — agent can still answer “which commodities have a variety named X?” via the linked_entities[] index across all commodity_profile docs (no separate variety table needed)
Negative
- Anchor links may break if a commodity profile is restructured — variety
anchors live INSIDE the commodity profile doc, so renaming a section in the
commodity renderer breaks any wikilink pointing at the old anchor.
Mitigation: variety anchor IDs derived from
slugifyForId(variety.name)— stable as long as variety names don’t change - No per-variety snapshot pattern — can’t snapshot just “Hass at 2026-W19” without snapshotting the entire avocado profile. Acceptable for v0.2; revisit if a use case emerges
Neutral
- The entity_type enum keeps
varietyas a valid value — just used selectively
Implementation
Done 2026-05-15:
- Schema (
packages/shared/src/schemas/contracts/doc.ts) accepts bothentity_type: commodityANDentity_type: variety - commodity_profile_renderer’s
extractLinkedEntities()populates alinked_entities[]entry per variety froms04_varieties.variety_summaries - commodity_profile_renderer’s
extractRelatedEntities()populatesrelated_entities[]withrelation: "variety_of"andweight: market_share_pct/100 - Rendered profiles show varieties as a table inside the commodity profile with a per-variety implicit anchor (heading slug)
Pending:
- Wikilink resolver in the doc parser needs the “first try entity_doc_projection, fallback to anchor” logic when T7 wires up the resolver
- Promotion criteria operationalized — initial list of variety promotions (if any) lands as a follow-up ADR or a config knowledge row
Related
- Engineering spec basis: Intranet Projection Engineering Spec v0.2 §3.1, §6.1
- Renderer that implements this:
fin-agentic/packages/mcp-server/src/ingestion/renderers/commodity-profile-renderer.ts - Avocado exemplar: avocado.md