237 lines
13 KiB
Text
237 lines
13 KiB
Text
|
|
# Positioning schema — marketing as queryable protocol surface.
|
|||
|
|
#
|
|||
|
|
# Three artefact kinds:
|
|||
|
|
# - Audience : a named segment with its primary pain
|
|||
|
|
# - ValueProp : a claim aimed at one or more audiences, backed by ADRs
|
|||
|
|
# - Campaign : a GTM moment bundling value-props for specific channels
|
|||
|
|
#
|
|||
|
|
# Each artefact lives in its own .ncl file under `.ontoref/positioning/`.
|
|||
|
|
# Narrative copy (the prose that sells) is held in markdown files referenced
|
|||
|
|
# via `narrative_path` — NCL holds the verifiable skeleton, markdown holds
|
|||
|
|
# the messaging body. The two split lets the protocol validate claims
|
|||
|
|
# (audience present, evidence_adrs non-empty when Live, launch_criteria pass)
|
|||
|
|
# without trying to be a copywriter.
|
|||
|
|
|
|||
|
|
let status_type = [| 'Draft, 'Validated, 'Live, 'Retired |] in
|
|||
|
|
|
|||
|
|
# Audience validation state — an audience is a Hypothesis until a Proof
|
|||
|
|
# references it (coherence rule 2). Defaults to 'Hypothesis so existing
|
|||
|
|
# audience files remain valid without edits.
|
|||
|
|
let audience_status_type = [| 'Hypothesis, 'Validated |] in
|
|||
|
|
|
|||
|
|
# Launch readiness check — structurally identical to the ConstraintCheck
|
|||
|
|
# type in `.ontoref/adrs/adr-schema.ncl`. Duplicated intentionally as a
|
|||
|
|
# sketch trade-off: a future lift-out into `ontology/schemas/check.ncl`
|
|||
|
|
# (shared by ADR and positioning layers) is tracked in ADR-035's negative
|
|||
|
|
# consequences. Until then, validators in both layers MUST keep these
|
|||
|
|
# variants in sync.
|
|||
|
|
let check_tag_type = [|
|
|||
|
|
'Grep,
|
|||
|
|
'NuCmd,
|
|||
|
|
'ApiCall,
|
|||
|
|
'FileExists,
|
|||
|
|
|] in
|
|||
|
|
|
|||
|
|
let launch_check_type = {
|
|||
|
|
tag | check_tag_type,
|
|||
|
|
..
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# Audience — one file per segment.
|
|||
|
|
let audience_type = {
|
|||
|
|
id | String, # kebab-case, e.g. "platform-engineer"
|
|||
|
|
name | String, # human-readable display name
|
|||
|
|
description | String, # who they are (one paragraph)
|
|||
|
|
primary_pain | String, # the pain we name and claim to solve
|
|||
|
|
context | String | default = "", # constraints, daily tools, team shape
|
|||
|
|
channels | Array String | default = [], # where they consume content (blog, conf, hn, mastodon)
|
|||
|
|
evidence | Array String | default = [], # observed signals this audience exists & cares
|
|||
|
|
linked_nodes | Array String | default = [], # ontology node ids that this audience engages
|
|||
|
|
status | audience_status_type | default = 'Hypothesis, # Validated requires a Proof (rule 2)
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# Value proposition — one file per claim.
|
|||
|
|
let value_prop_type = {
|
|||
|
|
id | String, # e.g. "vp-001-structure-that-remembers"
|
|||
|
|
claim | String, # the nuclear one-liner
|
|||
|
|
audience_ids | Array String, # references audience_type.id (>=1 required, contract enforced)
|
|||
|
|
differentiator_ids | Array String | default = [], # QUÉ×QUIÉN seam: differentiators this claim binds (>=1 when Live)
|
|||
|
|
problem_solved | String, # the problem this claim addresses
|
|||
|
|
alternatives | Array String | default = [], # competition / status-quo options the user might consider
|
|||
|
|
unique_angle | String, # why no one else solves it this way
|
|||
|
|
evidence_adrs | Array String | default = [], # ADR ids that materially back this claim (required when status='Live)
|
|||
|
|
proof_points | Array String | default = [], # benchmarks, demos, public metrics
|
|||
|
|
narrative_path | String | optional, # path to markdown body (relative to project root)
|
|||
|
|
launch_criteria | Array launch_check_type | default = [],
|
|||
|
|
status | status_type,
|
|||
|
|
retired_at | String | optional, # ISO date (required when status='Retired)
|
|||
|
|
superseded_by | String | optional, # value-prop id that replaces this one
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# Campaign — a GTM moment bundling value-props for specific channels.
|
|||
|
|
let campaign_type = {
|
|||
|
|
id | String, # e.g. "launch-mcp-surface"
|
|||
|
|
title | String,
|
|||
|
|
audience_ids | Array String, # who this campaign targets
|
|||
|
|
value_prop_ids | Array String, # value-props in scope
|
|||
|
|
mechanism_ids | Array String | default = [], # difusión mechanisms this campaign coordinates
|
|||
|
|
goal | String, # what we're trying to achieve
|
|||
|
|
channels | Array String | default = [], # where this campaign runs
|
|||
|
|
launch_criteria | Array launch_check_type | default = [],
|
|||
|
|
narrative_path | String | optional,
|
|||
|
|
status | status_type,
|
|||
|
|
starts_at | String | optional, # ISO date
|
|||
|
|
ends_at | String | optional, # ISO date
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# ── QUÉ axis ────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
# Differentiator — one file per area of differential value. Admissible only
|
|||
|
|
# when anchored: it must name the competitor categories that do NOT do it
|
|||
|
|
# (`vs`) and trace to ontology nodes + Accepted ADRs. Cross-field invariants
|
|||
|
|
# live in `make_differentiator` (defaults).
|
|||
|
|
let differentiator_type = {
|
|||
|
|
id | String, # e.g. "diff-witness-seam"
|
|||
|
|
area | String, # short area label
|
|||
|
|
value_claim | String, # one-line difference in VALUE, not a feature
|
|||
|
|
vs | Array String, # competitor categories that do not do this
|
|||
|
|
competitor_ids | Array String | default = [], # optional refs to competitor_type.id
|
|||
|
|
linked_nodes | Array String | default = [], # ontology node ids (>=1, contract enforced)
|
|||
|
|
evidence_adrs | Array String | default = [], # ADR ids (>=1 Accepted, contract enforced)
|
|||
|
|
status | status_type,
|
|||
|
|
retired_at | String | optional,
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# Competitor — one file per competitor or category on the QUÉ axis. Supports
|
|||
|
|
# the differentiators by recording what the competitor does and how ontoref
|
|||
|
|
# differs.
|
|||
|
|
let competitor_type = {
|
|||
|
|
id | String,
|
|||
|
|
name | String,
|
|||
|
|
category | String, # "KG consultancy", "developer portal", ...
|
|||
|
|
what_they_do | String,
|
|||
|
|
differs_by | Array String | default = [], # how ontoref differs (prose or differentiator ids)
|
|||
|
|
url | String | optional,
|
|||
|
|
linked_nodes | Array String | default = [],
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# ── CÓMO axis ───────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
# DifusionMechanism — one file per vehicle of difusión. Difusión is the
|
|||
|
|
# category; a Campaign is one time-boxed coordination within it. A mechanism is
|
|||
|
|
# persistent (Web, DocsSite, McpServer, Repo) or an event (Content, Talk,
|
|||
|
|
# Event). Admissible only with >=1 channel and >=1 thing it difunde — enforced
|
|||
|
|
# by make_difusion_mechanism.
|
|||
|
|
let mechanism_kind_type = [| 'Web, 'DocsSite, 'McpServer, 'Repo, 'Content, 'Talk, 'Event |] in
|
|||
|
|
let difusion_mechanism_type = {
|
|||
|
|
id | String,
|
|||
|
|
kind | mechanism_kind_type,
|
|||
|
|
name | String,
|
|||
|
|
description | String | default = "",
|
|||
|
|
persistent | Bool, # persistent surface vs one-off / event
|
|||
|
|
channels | Array String | default = [], # where it runs (>=1, contract enforced)
|
|||
|
|
value_prop_ids | Array String | default = [],
|
|||
|
|
differentiator_ids | Array String | default = [],
|
|||
|
|
launch_criteria | Array launch_check_type | default = [],
|
|||
|
|
status | status_type,
|
|||
|
|
retired_at | String | optional,
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# ── VALIDACIÓN — the seam ────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
# Proof — the costura (seam) of the positioning layer. A Proof is the
|
|||
|
|
# convergence of the three axes (a differentiator, an audience, a difusión
|
|||
|
|
# mechanism) signed by a real outcome — "where everything converges really".
|
|||
|
|
# It references the other three; nothing references it. `converges` must carry
|
|||
|
|
# >=1 id on each axis (enforced by make_proof).
|
|||
|
|
let proof_converges_type = {
|
|||
|
|
differentiator_ids | Array String,
|
|||
|
|
audience_ids | Array String,
|
|||
|
|
mechanism_ids | Array String,
|
|||
|
|
} in
|
|||
|
|
let proof_kind_type = [| 'Dogfood, 'PersonalInstance, 'ReferenceAdopter, 'Benchmark |] in
|
|||
|
|
let proof_type = {
|
|||
|
|
id | String,
|
|||
|
|
title | String,
|
|||
|
|
kind | proof_kind_type,
|
|||
|
|
converges | proof_converges_type,
|
|||
|
|
outcome | String, # the real, measurable result
|
|||
|
|
evidence | Array String | default = [], # links / paths / metric strings
|
|||
|
|
evidence_adrs | Array String | default = [],
|
|||
|
|
launch_criteria | Array launch_check_type | default = [],
|
|||
|
|
status | status_type,
|
|||
|
|
retired_at | String | optional,
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
# ── VIABILIDAD — the second seam ──────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
# ViabilityPath — the sustainability costura, parallel to Proof. Where Proof
|
|||
|
|
# answers "is the claim real?" (validation seam over the three axes), a
|
|||
|
|
# ViabilityPath answers "does the claim sustain its maker?" (sustainability seam
|
|||
|
|
# over the same axes). It is NOT a fourth orthogonal axis — compensation is not
|
|||
|
|
# a facet of a claim but a convergence outcome: an audience (PARA QUIÉN) reached
|
|||
|
|
# by a difusión mechanism (CÓMO) monetizing a value-prop (QUÉ×QUIÉN) returns a
|
|||
|
|
# sustaining flow to the maintainer. Self-similar to witness-as-axis-seam and to
|
|||
|
|
# Proof: it references the three axes via `converges`; nothing references it.
|
|||
|
|
#
|
|||
|
|
# `directness` maps to the two poles of the core tension openness-vs-sustainability:
|
|||
|
|
# 'Indirect — the Yin/open pole: visibility, funnel, reputation (the open and
|
|||
|
|
# personal surface widens the funnel whose paid tail sustains).
|
|||
|
|
# 'Direct — the Yang/governance pole: metered payment for the witness/audit
|
|||
|
|
# surface corporate audiences pay for.
|
|||
|
|
#
|
|||
|
|
# A ViabilityPath is 'Draft (a hypothesis about how positioning could sustain the
|
|||
|
|
# project) until a real, measured compensation outcome promotes it — exactly as an
|
|||
|
|
# Audience is 'Hypothesis until a Proof validates it. The gate is one-directional
|
|||
|
|
# (realized status ⇒ realized_outcome present), never a Hard biconditional on the
|
|||
|
|
# Spiral question (ondaod forbidden pattern).
|
|||
|
|
let directness_type = [| 'Direct, 'Indirect |] in
|
|||
|
|
let flow_kind_type = [|
|
|||
|
|
'HostedService, # daemon / witness / audit surface run for a fee
|
|||
|
|
'Support, # paid support, SLA, priority response
|
|||
|
|
'Consulting, # integration / adoption / advisory services
|
|||
|
|
'DualLicense, # commercial license of an otherwise-open surface
|
|||
|
|
'Sponsorship, # grants, sponsors, patronage of the open protocol
|
|||
|
|
'Product, # a consumer artefact (e.g. a native shell) adopted or paid for
|
|||
|
|
'Funnel, # indirect: the open/personal surface feeds a paid tail
|
|||
|
|
'Reputation, # indirect: talks/demos/content convert to opportunities
|
|||
|
|
|] in
|
|||
|
|
let viability_converges_type = {
|
|||
|
|
audience_ids | Array String, # PARA QUIÉN the flow draws from (>=1 enforced)
|
|||
|
|
mechanism_ids | Array String, # CÓMO they are reached — difusión (>=1 enforced)
|
|||
|
|
value_prop_ids | Array String, # QUÉ×QUIÉN claim monetized (>=1 enforced)
|
|||
|
|
} in
|
|||
|
|
let viability_path_type = {
|
|||
|
|
id | String, # e.g. "via-001-governance-audit-tail"
|
|||
|
|
title | String,
|
|||
|
|
directness | directness_type, # maps to openness-vs-sustainability poles
|
|||
|
|
flow_kind | flow_kind_type,
|
|||
|
|
converges | viability_converges_type, # the triple-axis convergence this flow rests on
|
|||
|
|
hypothesis | String, # the sustaining mechanism stated as a claim
|
|||
|
|
realized_outcome | String | optional, # the REAL measured compensation (required when status='Validated|'Live)
|
|||
|
|
linked_nodes | Array String | default = [], # ontology node ids (>=1 enforced — must engage the viability tension)
|
|||
|
|
evidence_adrs | Array String | default = [], # ADR ids backing the path (required when status='Live)
|
|||
|
|
launch_criteria | Array launch_check_type | default = [],
|
|||
|
|
status | status_type, # Draft until a real outcome → Validated/Live
|
|||
|
|
retired_at | String | optional,
|
|||
|
|
} in
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
PositioningStatus = status_type,
|
|||
|
|
AudienceStatus = audience_status_type,
|
|||
|
|
MechanismKind = mechanism_kind_type,
|
|||
|
|
ProofKind = proof_kind_type,
|
|||
|
|
Directness = directness_type,
|
|||
|
|
FlowKind = flow_kind_type,
|
|||
|
|
LaunchCheckTag = check_tag_type,
|
|||
|
|
LaunchCheck = launch_check_type,
|
|||
|
|
Audience = audience_type,
|
|||
|
|
ValueProp = value_prop_type,
|
|||
|
|
Campaign = campaign_type,
|
|||
|
|
Differentiator = differentiator_type,
|
|||
|
|
Competitor = competitor_type,
|
|||
|
|
DifusionMechanism = difusion_mechanism_type,
|
|||
|
|
Proof = proof_type,
|
|||
|
|
ViabilityPath = viability_path_type,
|
|||
|
|
}
|