The bond — a typed project↔domain relation on a governance/dependency axis, declared by a carrier, never derived from repo_kind
Accepted
Context
A project relates to another project, and to a domain, in ways the protocol could not say. It could say a project IMPLEMENTS a domain — one flat marker directory, `.{domain}.ontoref/`, whose mere presence flipped a boolean in `domain.nu`. And it could say a node points at another node — a `link` (the `ln`/Link schema), a generic reference with no semantics beyond "see also". Between the two there was nothing: no way to state that a project is GOVERNED by a domain (an acyclic hierarchy) as opposed to DEPENDS ON a peer (a graph that permits cycles), no way to type the KIND of a dependency, no way to declare membership in MORE THAN ONE domain, and no way to bond to a framework WITHOUT surrendering to its project management.
Three facts made the gap concrete, all measured 2026-07-17.
THE MEMBERSHIP THROTTLE IS AN ASYMMETRY. A domain already accepts many kinds: `code/domains/schema.ncl` types `repo_kinds | Array String`. But a project declares exactly one: `code/ontology/schemas/manifest.ncl` types `repo_kind | repo_kind_type`, a scalar. So multi-domain membership was capped not by a domain refusing members but by a project being allowed only one kind — and the cap was invisible because it lived on the OTHER side of the relation from where it bit.
THE MARKER WAS A DETECTION CONVENTION, NOT A FILE. `.{domain}.ontoref/`'s presence WAS the signal (`domain.nu :: project-domains`, pattern (c)). The three precedent projects carrying it — `DD7pasos/.librosys.ontoref/`, `website-htmx-rustelo/.rustelo.ontoref/`, `jpl/rustelo-website/.rustelo.ontoref/` — are heterogeneous (schemas+modes / an `app.ncl` / modes only), none of them a bond declaration. They predate the model. Relocating them is a convention change, not a move.
DDD ALREADY HAD THE DEPENDENCY VOCABULARY, AND ONLY THAT. Domain-Driven Design's Context-Map names exactly the kinds a dependency edge comes in — CustomerSupplier, Partnership, OpenHostService, Conformist, SharedKernel, AnticorruptionLayer. It is the right enum for one axis and the wrong frame for the whole: adopt it as the frame and the native ontoref vocabulary (nodes, edges, axes, planes, the Spiral) is displaced by someone else's, which is the anti-PAP move this project has refused everywhere else.
Decision
Introduce the BOND: a typed relation between projects and their domains, contracted in `code/ontology/schemas/bond.ncl`, orthogonal to whether ontoref manages the project.
A bond carries exactly one AXIS:
'Governance the project is an ontoref-node in a domain's DAG (acyclic). Resolved with 'Delegate / 'Override / 'Compose (ADR-018, ADR-045). Not typed with DDD. 'Dependency the project inhabits an ontoref-plane with peer nodes (cycles permitted). Typed with a DDD Context-Map `dep_kind`, optionally `mutual`. A `mutual` bond (Partnership) is the 2-cycle that CONSTITUTES a plane (plane-habitability).
A bond resolves against the DOMAIN ROOTS of both endpoints — their definitions, attributes, states and capabilities — never on the edge itself; `capabilities` on a bond are reference ids, not inline definitions.
A project's linkage occupies an FSM state — 'None / 'Linked / 'Node / 'Managed — with 'Onboard (climb), 'Update (retype same tier) and 'Offboard (descend) transitions. A project reaches 'Linked or 'Node WITHOUT 'Managed: a bond needs no `.ontoref/` management spine.
MEMBERSHIP IS DECLARED BY A CARRIER, NEVER DERIVED FROM `repo_kind`. A bonded project carries `.domains-ontoref/<domain>/bonds.ncl` conforming to `BondSet`; multi-domain membership is multiple subfolders. This unbinds membership from the scalar `repo_kind` WITHOUT touching `manifest.ncl` at all (carrier-only — the manifest grows no `bonds` field). Detection adds pattern (d) `bonded_dir` to `domain.nu :: project-domains`, recognized ALONGSIDE the now-deprecated flat `.{domain}.ontoref/` during the 0047 migration window, with dedup.
The relation is named `bond`, not `link`: `link` is taken (the generic `ln`/Link reference), and a bond is a constitutive, typed tie, not a see-also.
Constraints
- Hard The bond contract exists as a typed schema in the ontology layer.
- Hard The carrier layout is recognized by the domain detector, not merely documented.
- Hard Membership is carrier-declared; the manifest grows no bond/membership field (carrier-only).
- Soft A DDD dep_kind is meaningful only on a 'Dependency bond; a 'Governance bond carrying one is malformed.
Alternatives considered
- Adopt DDD's Context-Map as the overall frame. — rejected: It displaces the native ontoref vocabulary (nodes/edges/axes/planes/Spiral) with another discipline's, the exact anti-PAP refused elsewhere. DDD is kept as the dependency-edge enum only.
- Model everything as one graph (governance and dependency as the same edge set). — rejected: A single graph cannot hold that governance is acyclic while dependency permits cycles. The axis distinction — DAG vs plane — is precisely what a one-graph model erases, and it is the thing that decides whether a `mutual` tie is legal.
- Name the relation `link` and extend the existing `ln`/Link schema. — rejected: `link` is taken and means a generic node reference with no semantics. A bond is a constitutive, typed, mutual-capable tie; overloading `link` would blur the one word that already has a job.
- Derive multi-domain membership from a plural `repo_kinds` on the manifest. — rejected: Couples membership to project management and to the manifest's identity field. The carrier keeps bonding orthogonal to management (a non-managed project has no manifest) and uncapped, with zero manifest surface.
Anti-patterns
- Importing the Discipline, Not the Word — A borrowed vocabulary that fits one axis is adopted as the frame for the whole. DDD's Context-Map names dependency kinds perfectly; taken as the overall model it drags Ubiquitous Language, Bounded Contexts and the rest in with it, and the native ontoref frame (nodes, edges, axes, planes, the Spiral) is quietly displaced by another discipline's ontology in the one project whose entire premise is its own ontology.
- One Kind, One Domain, By Accident — Membership in a domain is inferred from the project's single `repo_kind`, so a project can belong to exactly one — not because anyone decided a project may bond to only one domain, but because the identity field happens to be scalar. The cap is real, invisible, and lives on the opposite side of the relation from where it is felt: the domain accepts many kinds, the project may declare one.
- Moving the Sign the System Reads By — A convention whose literal form is what the tooling detects is 'relocated' as if it were a file move. `.{domain}.ontoref/`'s NAME was the membership signal; renaming the directory to `.domains-ontoref/<id>/` without teaching the detector the new form silently strips membership from every project that adopts the new layout — the relocation reads as done, and detection reads as empty.