Positioning Layer — Marketing as Queryable Protocol Surface

Accepted

ontoref
Ontoref's thesis is that everything project-defining is queryable NCL:

Context

Ontoref's thesis is that everything project-defining is queryable NCL: axioms, tensions, practices, ADRs, reflection modes, manifest capabilities, config surface, backlog, Q&A, search bookmarks, even the API catalogue (via the #[onto_api] proc-macro + inventory). The protocol pushes this discipline aggressively into every layer it touches.

One surface escapes the discipline: the *outward-facing* artefacts of the project — the things that explain to a potential adopter, sponsor, or contributor *why they should care*. Today these live as:

- card.ncl — a tagline + features array attached to ProjectCard. Identity-shaped, audience-flat. There is no model of *whom* the project speaks to, *what claim* is being made to each audience, or *which architectural decisions back the claim*.

- assets/web/ — index.html, personal.html, provisioning.html, architecture-diagram.html. These are the public face of ontoref. They are **hand-edited markdown-and-HTML**, disconnected from the NCL substrate. Every other artefact in ontoref derives from a typed source (ADRs from forms via Jinja, API pages from inventory catalogue, describe outputs from ontology/manifest/qa). Public messaging does not.

- domains/{personal, framework, provisioning}/ — these are *implicit* audience segments, modelled as technical extension verticals (per ADR-012) rather than as go-to-market audiences. The mapping "platform engineer ↔ provisioning domain" exists in the maintainer's head, nowhere in NCL.

The gap is real and visible. The user observation that opened this ADR: when ontoref reasons about *what to build next* (Wish/Idea backlog, ADR drafting, tension synthesis) it has rich machinery; when ontoref reasons about *who cares and why* it has card.ncl's tagline string. Marketing claims drift from architectural decisions; messaging copy ages independently of the substrate that justifies it; the protocol's own "everything is NCL" axiom is silently violated at exactly the surface where adopters meet the project.

Two prior sessions have orbited this gap without closing it:

- The PRD-vs-ADR question (this session, prior turn) — initially framed as "should backlog Wish items carry a PRD block?". That sketch (extend Item with prd field) addresses *internal* product intent. It does not address the outward-facing positioning surface.

- ADR-031's on+re duality — established that ontology (substance, "what IS") and reflection (act, "what DOES") are constitutive, not optional. Positioning artefacts live primarily on the reflection axis (claims are acts of communication toward an audience) but are *anchored* to substance via evidence_adrs (each claim cites the ADRs that materially back it). Without this anchoring, claims drift into pure narrative and become un-auditable.

The positioning layer formalises the outward-facing surface as queryable NCL — completing the protocol's coverage of the project's externalised self — while keeping prose-as-prose via a narrative_path that points to markdown bodies. The skeleton is verifiable (audience present, evidence ADRs cited when Live, launch criteria executable); the flesh stays human.

Decision

A new layer `positioning` is added to the protocol surface, with three artefact kinds — **Audience**, **ValueProp**, **Campaign** — typed by schemas installed to the user data dir, and instantiated per-project under `.ontoref/positioning/`.

SCHEMAS (installed to data dir, same install path as project-card.ncl)

ontology/schemas/positioning.ncl PositioningStatus | [| 'Draft, 'Validated, 'Live, 'Retired |] LaunchCheck | tagged record (Grep | NuCmd | ApiCall | FileExists) Audience | { id, name, description, primary_pain, context, channels[], evidence[], linked_nodes[] } ValueProp | { id, claim, audience_ids[], problem_solved, alternatives[], unique_angle, evidence_adrs[], proof_points[], narrative_path?, launch_criteria[], status, retired_at?, superseded_by? } Campaign | { id, title, audience_ids[], value_prop_ids[], goal, channels[], launch_criteria[], narrative_path?, status, starts_at?, ends_at? }

ontology/defaults/positioning.ncl make_audience, make_value_prop, make_campaign — apply cross-field contracts: - non_empty_audience_ids (value-prop must target someone) - live_requires_evidence_adrs (Live claims must cite ≥1 ADR) - retired_has_date (Retired requires retired_at) - superseded_implies_retired (cannot supersede while Live) - each_launch_check_well_formed (per-tag field requirements)

CONSUMER LAYOUT (per-project, opt-in)

my-project/ └── .ontoref/ └── positioning/ ← new layer root ├── audiences/ │ ├── audience-platform-engineer.ncl │ ├── audience-ai-agent-runtime.ncl │ └── audience-architecture-team.ncl ├── value-props/ │ ├── vp-001-structure-that-remembers.ncl │ ├── vp-002-mcp-agent-discoverability.ncl │ └── vp-003-decision-replay.ncl ├── campaigns/ │ └── campaign-NNNN-slug.ncl └── narratives/ ├── vp-001-self-knowledge.md ← prose for the claim └── audience-platform-engineer.md

NCL holds the verifiable skeleton (audience, evidence_adrs, launch_criteria, status). Markdown narratives hold the messaging body. Generated HTML in `assets/web/` derives from the pair via the same Jinja pattern used for ADRs.

CARD.NCL INTEGRATION

ProjectCard schema gains one optional field: primary_value_prop_id | String | optional

When present, it MUST resolve to a file at .ontoref/positioning/value-props/<id>.ncl

card.tagline is preserved (zero migration cost). When primary_value_prop_id is set, downstream renderers (portfolio publication, web assets) MAY prefer the canonical value-prop's claim over the static tagline.

CHECK TYPE REUSE — ACKNOWLEDGED DUPLICATION

positioning.LaunchCheck is structurally identical to the ConstraintCheck type in `.ontoref/adrs/adr-schema.ncl` (tags: 'Grep, 'NuCmd, 'ApiCall, 'FileExists; per-tag well-formedness). The duplication is intentional in this ADR: lifting ConstraintCheck into a shared `ontology/schemas/check.ncl` requires editing the ADR schema's import graph (changing what every existing ADR resolves against), which is out of scope for introducing a new optional layer.

Lift-out is recorded as a soft constraint in this ADR (shared-check-lift-out-pending) and deferred to a follow-up migration. Until then, validators in both layers reference the same tags by name and the test suite checks shape-equivalence on every CI run.

ASSETS/WEB/ DERIVATION (out of scope but enabled)

This ADR does not specify the asset generator. It establishes the queryable source from which assets/web/index.html, personal.html, etc. CAN be generated. A follow-up ADR (or migration) replaces hand-edited HTML with Jinja-templated output reading the positioning tree + linked narratives, the same way ADRs are generated from forms today. Until that follow-up lands, assets/web/ remains hand-edited and the positioning layer informs it via reference, not derivation.

OPT-IN PER PROJECT

positioning is a tier-orthogonal optional layer (consistent with ADR-029's tier-coexistence design). A project at tier-0 with no positioning/ directory operates exactly as before. Adoption is declared by creating `.ontoref/positioning/` and ≥1 audience + ≥1 value-prop; the daemon's describe surface exposes positioning as a category iff the directory is non-empty. No migration is required for existing consumer projects; the layer is purely additive.

CLI / DESCRIBE SURFACE

ontoref describe positioning [--audience <id>] [--status Live] → lists value-props (filtered) with their audience, evidence_adrs, and narrative_path

ontoref positioning validate → runs launch_criteria checks across all value-props and campaigns; reports pass/fail per artefact

ontoref positioning audit → cross-references evidence_adrs against the ADRs directory; flags claims whose cited ADRs are 'Deprecated, 'Superseded, or missing entirely (drift detection between marketing and architecture)

Constraints

  • Hard The positioning schema (ontology/schemas/positioning.ncl) and its defaults (ontology/defaults/positioning.ncl) MUST be installed to the user data dir as part of every ontoref install, alongside project-card.ncl and the other layer schemas. Without these files, consumer projects cannot resolve `import "positioning"` and the layer is structurally unavailable.
  • Hard The positioning defaults file (ontology/defaults/positioning.ncl) MUST be installed alongside the schema. Cross-field contracts (live_requires_evidence_adrs, retired_has_date, etc.) are applied via the make_* helpers in this file — without it, value-prop files importing `defaults/positioning.ncl` fail to parse and the structural invariants the layer promises are unenforceable.
  • Hard Audience records MUST live in their own files under `.ontoref/positioning/audiences/<id>.ncl`. Value-prop files MUST reference audiences by id (audience_ids: Array String), NOT inline the audience record. The `primary_pain` field is the canonical signature of an audience record — its presence inside a value-prop file is the violation.
  • Hard When `card.ncl` declares `primary_value_prop_id = "vp-XXX-..."`, the referenced value-prop file MUST exist at `.ontoref/positioning/value-props/vp-XXX-....ncl`. Dangling references between identity (card) and positioning (value-props) silently break downstream renderers (portfolio, web pages, MCP surfaces).
  • Hard Every value-prop with status='Live MUST declare a non-empty evidence_adrs array. This is enforced at nickel-export time by the live_requires_evidence_adrs contract in `ontology/defaults/positioning.ncl`. The Hard runtime check confirms the contract has not been bypassed (e.g. by editing exported JSON directly).
  • Hard When a value-prop or campaign declares `narrative_path = "..."`, the path MUST resolve to an existing file. Markdown narratives outside the NCL ecosystem are referenced, not validated for content, but their existence is the minimum enforceable invariant.
  • Soft The LaunchCheck type in `ontology/schemas/positioning.ncl` is structurally identical to ConstraintCheck in `.ontoref/adrs/adr-schema.ncl`. Both MUST be lifted into a shared `ontology/schemas/check.ncl` in a follow-up migration. Until that migration lands, the duplicated definitions MUST stay synchronised: any new tag added to one MUST be added to the other in the same commit.

Alternatives considered

  • Extend card.ncl monolithically with positioning fields (audiences, value_props, campaigns inline)rejected: Mixes identity (what the project is — stable across years) with positioning (claims aimed at specific audiences — evolves per campaign). The schema becomes ambiguous: is ProjectCard.tagline the project's identity statement or its current value-prop claim? Inline arrays also lose the per-artefact reviewability that separate files give (each value-prop change is its own git diff, its own narrative file, its own launch_criteria). Card-as-monolith fails as soon as a project has more than one audience to address.
  • Address only the internal PRD gap — extend backlog Item with a prd block (route A from the prior session turn)rejected: Solves a different problem (internal product intent for the engineering team) and leaves the outward-facing surface — the one that adopters actually see — untouched. The prior session sketch is complementary, not substitutive: an engineering PRD captured in backlog and a marketing value-prop captured in positioning serve disjoint audiences (team vs. world). Choosing only the PRD route leaves assets/web/ disconnected from the protocol forever.
  • Keep positioning as pure markdown under .ontoref/positioning/*.md — no NCL skeletonrejected: Loses every property that justifies adding the layer: no evidence_adrs anchor (claims drift from architecture invisibly), no launch_criteria (no executable readiness check), no audience targeting (claims float free of who they address), no MCP queryability (markdown is opaque to structured tooling), no audit surface (drift between marketing and architecture is undetectable). Markdown-only positioning is what every project already has via README and blog posts — adding it to the protocol contributes nothing structural.
  • Per-domain positioning — let each domains/{personal,framework,provisioning}/ carry its own positioning filesrejected: Couples positioning to the technical extension mechanism (domains) when in fact value-props frequently cross domains (a single claim like 'structure that remembers' targets multiple audiences across multiple domains). Per-domain positioning would either duplicate value-props across domain directories (drift risk) or require a cross-domain index that re-introduces the centralised positioning/ directory by another name. The centralised layer with audience_ids as the cross-cutting key is simpler and supports the cross-domain claim case natively.
  • Defer the entire question — write hand-edited HTML now and revisit positioning later when adoption volume justifies itrejected: The protocol's coherence with its own axiom (everything is NCL) is the value proposition — deferring its application to the outward-facing surface is precisely the pattern (substance-vs-act collapse) that ADR-031 was opened to prevent. Also: the longer the surface stays hand-edited, the more drift accumulates between claims and architecture, and the higher the cost of eventually adopting positioning. The opt-in design means deferral by individual consumers is already supported — what is rejected is the protocol itself deferring the schema.

Anti-patterns

  • Live Value-Prop Without Evidence ADRs — A value-prop is set to status='Live but evidence_adrs is empty — marketing is published without architectural backing. The live_requires_evidence_adrs contract catches this at nickel-export, but the anti-pattern is the maintainer reasoning that 'we can fill in evidence later'.
  • Audience Record Inlined in Value-Prop — A value-prop file declares the audience inline (name, primary_pain, channels) instead of referencing it by id. Multiple value-props targeting the same audience drift independently.
  • Marketing-Architecture Drift — A value-prop's evidence_adrs cites an ADR that has been superseded or deprecated — the claim is still Live, but its backing is no longer the active architectural decision. The marketing surface and the architecture diverge silently.

Related ADRs

ADR-001 · ADR-009 · ADR-012 · ADR-015 · ADR-024 · ADR-027 · ADR-029 · ADR-030 · ADR-031 · ADR-032

Was this useful? Rate it
Got something to add? Tell me what you think, what you'd suggest, or whether we should keep exploring this topic.
· reads

We use cookies to help this site function, understand service usage, and support marketing efforts. Cookie Policy for more info.