{
  "uid": "cogitave.learn.project-products-into-core.how-content-projects-into-core",
  "kind": "moduleUnit",
  "href": "/modules/project-products-into-core/how-content-projects-into-core/",
  "title": "How content projects into the canonical model",
  "summary": "",
  "type": null,
  "products": [],
  "roles": [],
  "levels": [],
  "subjects": [],
  "headings": [
    "The node it becomes",
    "Two identifiers, two jobs",
    "The edges that link it in",
    "The pipeline: ACQUIRE to PUBLISH",
    "Served identically to humans and agents"
  ],
  "source": "Projecting content into a canonical model is not a metaphor. It names a\nspecific pipeline, a specific identity scheme, and a specific set of edges. This\nunit walks all three, using Cogitave's own Core as the worked example, from its\n[Core architecture spec](../../../../core/docs/architecture.md).\n\n## The node it becomes\n\nContent lands as one of the model's typed nodes. In Cogitave's Core, for\ninstance, that is a `Doc` (a Diataxis-typed docs page), an `Article`, a\n`Standard`, an `ADR`, or - for this very platform - a `Unit`, `Module`, or\n`LearningPath`. Each type has its own JSON Schema, but all share one base schema\nand one identity rule, so a standard and a learning unit are queried the exact\nsame way.\n\n## Two identifiers, two jobs\n\nEvery node carries two orthogonal identifiers. The **`uid`** is identity: an\nimmutable, dotted, globally unique name in one flat namespace across the whole\nestate (Cogitave's own read `cogitave.<area>.<name>`, e.g.\n`cogitave.core.architecture`), so a URL can change under it without breaking a\nsingle link. The **`contentHash`** is version: a content-addressed digest of the\ncanonicalized payload, the same Merkle-DAG idea Git and IPFS use - a changed\nleaf changes only the hashes on the path back to the root, and two byte-identical\nnodes are stored once. (Vector embeddings are deliberately excluded from\n`contentHash`, so swapping an embedding model never looks like a content edit.)\n\n## The edges that link it in\n\nA node alone is inert; a **closed set of edge types** is what makes it part of\nthe graph. Cogitave's Core fixes **eleven** of them, and content-authoring hits\nfour most: `xref` (a citation, `<xref:uid>` or `@uid`, traversed both ways),\n`partOf` (composition - a `Unit` is `partOf` a `Module`, which is `partOf` a\n`LearningPath`; this module's own five units are wired this way), `appliesTo` (a\n`Standard` governs a `Repo`), and `teachesSkill` (a `Unit` teaches a `Skill`).\nThe vocabulary is closed on purpose: a fixed set of labels keeps traversal and\nfaceting stable as the estate grows.\n\n## The pipeline: ACQUIRE to PUBLISH\n\nA source blob becomes a node through a fixed pipeline; Cogitave's Core runs\n**ACQUIRE → PARSE → ENRICH → INDEX → EMIT → PUBLISH**. Each acquired blob is\nhashed; an unchanged blob short-circuits, and\na changed one recomputes its node's `contentHash` and marks only the\n*transitively affected* nodes dirty via the `xref`/`include` edge DAG - the same\non-demand, memoized recompute discipline as `salsa` and Adapton. Schema\nvalidation and broken-xref/link checks are a **blocking** gate: if it doesn't\nvalidate, it doesn't publish. On a green merge, three materializations rebuild\nas pure functions of the graph - a UID-keyed catalog, a graph store for\ntraversal, and a hybrid search index - swapped in atomically behind a new\ncontent root.\n\n## Served identically to humans and agents\n\nOnce published, every node is an MCP **resource** - in Cogitave's Core, addressed\nas `cogitave://{type}/{id}` (for example `cogitave://doc/cogitave.core.architecture`).\n`docs_fetch` returns the node's `uid`, `type`, `title`, rendered content, and its\n`contentHash` and `lastModified` - the same [MCP interface](../../../../core/docs/mcp-interface.md)\na human's search box calls underneath. `describe_schema` lets an agent read the\nclosed vocabulary before it queries, and `resources/subscribe` plus\n`notifications/resources/updated` mean a client learns about a changed node\ninstead of polling for it.\n\n> [!TIP]\n> \"How does a doc become a queryable node\" now has a precise answer: it is\n> hashed, typed, given a stable UID, linked in through the closed edge set, and\n> served through `docs_fetch`/`resources/read` - the same path whether the\n> caller is a person's browser or an agent's MCP client.\n",
  "partOf": "cogitave.learn.project-products-into-core",
  "durationInMinutes": 7,
  "quiz": null
}