Skip to content

ADR 0015: Knowledge Pipeline — COS SPOT to Serve via BFFs

Status

Proposed

Date

2026-08-12

Context

OVES needs a universal Single Point of Truth (SPOT) for all knowledge — products, articles, training materials, bot responses, media, and more. Knowledge must be general, unstructured, ad-hoc, and headless. It does not serve frontends directly.

The pipeline pattern is consistent regardless of knowledge type:

Source (COS SPOT) ──tag event──► Curation (SCF) ──► Produce (BFF mutation) ──► Serve (CDN)

Source: COS stores raw knowledge — markdown, PDFs, images, data files. COS native object tags provide metadata and routing. No folders, no custom schemas.

Curation: SCF (Serverless Cloud Function) triggered by COS events. Reads tags to determine which BFF to call. Reads content to build the mutation payload. Transforms unstructured knowledge into BFF-compatible structured input.

Produce: BFF mutation APIs create or update structured objects in the target system. BFFs are the sole write path — never touch Odoo or other backends directly.

Serve: The target system serves frontends via CDN, API, or social media — unchanged from current state.

Why This Pattern

Benefit How
COS owns data Universal, headless, no lock-in to any backend
No schema duplication BFF schema is consumer concern, not SPOT concern
Multi-dimensional relevance Tags: type=product, bff=product, parent=pet-3, region=cn
Single propagation mechanism One SCF handles all knowledge types via tag-driven routing
Extensible New knowledge types add tags + BFF mutations, no new pipelines

Rejected Alternatives

Approach Why rejected
Odoo as SPOT + COS as cache Defeats the goal — COS should own the data
Bidirectional sync Odoo demoted to read-only projection; no conflicts
YAML card files Redundant — COS native tags provide the same capability
SMH (Smart Media Hosting) Media-hosting oriented; revisit when comparing to Cloudinary
Folder-based categorization Forces rigid hierarchy; tags provide multi-dimensional relevance

COS Layout

Flat namespace under knowledge/ prefix. No folders. Files related by name prefix.

knowledge/
  pet-3.md                    # product knowledge body
  pet-3-spec-sheet.pdf        # product asset
  pet-3-hero.jpg              # product asset
  battery-48v.md              # product knowledge body
  battery-48v-datasheet.pdf   # product asset
  oves-e-mobility.md          # article knowledge body
  case-whitepaper.md          # article knowledge body
  onboarding-guide.md         # training knowledge body
  product-expert-bot.md       # bot knowledge body

COS Object Tags (max 10 per object)

Routing Tags (drive the propagation)

Tag Key Values Purpose
type product, article, training, bot, media Knowledge category
bff product, article, lexiang, edgeone, cloudinary Which BFF to call
updated ISO date 2026-08-12 Change tracking for propagation trigger
parent pet-3 Links asset to parent knowledge object
asset-type datasheet, image, manual What kind of asset

Metadata Tags (consumed by BFF mutations)

Tag Key Maps to
name BFF name field
slug BFF slug field
product-class BFF productClass field
is-sellable BFF isSellable field
author BFF author field

Tag Limit Mitigation (max 10)

  • Tags hold routing + identity metadata only
  • Rich content goes in the object body (markdown, JSON, PDF)
  • Complex metadata goes in a metadata.json sibling file with parent tag

Propagation — SCF Implementation

COS PutObject event on knowledge/ prefix
    │
    ▼ Read object tags
    ├── bff=product → product-bff mutation
    ├── bff=article → article-bff mutation
    ├── bff=lexiang → Lexiang MCP tool
    ├── bff=edgeone → EdgeOne CDN push
    └── bff=cloudinary → Cloudinary API
    │
    ▼ Read object content
    ├── Markdown → extract structured fields → BFF mutation payload
    ├── PDF → CI extract text → BFF mutation payload
    └── Image → CI OCR / thumbnail → CDN URL → BFF mutation payload
    │
    ▼ Call BFF mutation
    └── Odoo updated (or other backend)

Knowledge Type Instances

Products (first instance)

SPOT: knowledge/{product-name}.md + tagged assets
BFF: product-bff (createProduct, updateProduct mutations)
Odoo: ov.product_profile + related models (read-only projection)
Serve: decks.omnivoltaic.cn, websites via BFF query

Tags: type=product, bff=product, product-class=VEHICLE, is-sellable=true, name=PET-3

Articles (second instance)

SPOT: knowledge/{article-slug}.md
BFF: article-bff (createArticle, updateArticle mutations)
Odoo: blog.post collections (read-only projection)
Serve: Websites via BFF query, Lexiang AI Q&A

Tags: type=article, bff=article, author=Admin, tags=e-mobility,electric-vehicles

Future Instances

Knowledge Type BFF Target System
Training materials EdgeOne CDN training.omnivoltaic.cn
Bot responses Lexiang MCP AI 问答 via Lexiang knowledge set
Media assets Cloudinary API Social media, marketing channels
Memos EdgeOne CDN memo.omnivoltaic.cn
Presentations EdgeOne CDN decks.omnivoltaic.cn

Odoo Demotion

Odoo Product and Article custom models become read-only projections. SCF is the exclusive writer. No human Odoo UI editing for these models. Odoo remains the persistence layer behind BFFs — we don't know or care how BFFs persist internally.

Implementation Phases

Phase Deliverable
1 COS knowledge/ prefix, tag conventions, SCF skeleton
2 Product instance: SCF → product-bff mutation pipeline
3 Article instance: SCF → article-bff mutation pipeline
4 Migrate existing Odoo Products/Articles → COS knowledge/
5 Training + Media instances

Consequences

Benefits

  • COS owns all knowledge — universal, headless, multi-dimensional via tags
  • One propagation pipeline serves all knowledge types
  • BFFs remain the interface — frontends don't change
  • Odoo demoted but functional — no breaking changes to serving layer
  • Tag-driven routing eliminates schema duplication
  • SMH evaluation deferred — revisit for Cloudinary comparison

Trade-Offs

  • COS tag search requires list + filter (no native query index)
  • 10-tag limit per object — sufficient for routing, not for rich metadata
  • BFF mutations must exist (may need to be built in dirac-fed)
  • Two-step propagation adds latency (COS event → SCF → BFF)