docs(positioning): add "el-sujeto-no-se-alquila" narrative (es/en)

This commit is contained in:
Jesús Pérez 2026-07-19 18:36:16 +01:00
parent 27a8fb23a2
commit e53474a236
25 changed files with 537 additions and 48 deletions

View file

@ -14,7 +14,7 @@ css_class: "category-adr"
---
<h2>Context</h2>
<p>Consumer projects have project-type-specific CLI needs that ontoref's generic command set cannot satisfy. A PersonalOntology project has CFP pipelines, career schemas, and opportunity tracking that no other repo_kind needs. A DevWorkspace project has workspace cards, cluster state dimensions, and production-readiness gates. These capabilities lived as local scripts (scripts/jpl.nu) — invisible to ontoref's help system, absent from describe capabilities, and not portable to other PersonalOntology projects. The problem had two layers: (1) how to conditionally load domain-specific Nu commands without breaking the existing dispatcher, and (2) how to make domain commands discoverable (help, describe capabilities) and aliasable (ore prov, personal state). Nu's module system (`use`, `source`, `overlay use`) is compile-time: paths must be known at parse time and `overlay use` creates an isolated namespace that cannot extend `def main` in the existing dispatcher. Runtime loading of arbitrary Nu modules is architecturally impossible in the current Nu model.</p>
<p>Consumer projects have project-type-specific CLI needs that ontoref's generic command set cannot satisfy. A PersonalOntology project has CFP pipelines, career schemas, and opportunity tracking that no other repo_kind needs. A DevWorkspace project has workspace cards, cluster state dimensions, and production-readiness gates. These capabilities lived as local scripts (scripts/jpl.nu) — invisible to ontoref's help system, absent from describe capabilities, and not portable to other PersonalOntology projects. The problem had two layers: (1) how to conditionally load domain-specific Nu commands without breaking the existing dispatcher, and (2) how to make domain commands discoverable (help, describe capabilities) and aliasable (ontoref prov, personal state). Nu's module system (`use`, `source`, `overlay use`) is compile-time: paths must be known at parse time and `overlay use` creates an isolated namespace that cannot extend `def main` in the existing dispatcher. Runtime loading of arbitrary Nu modules is architecturally impossible in the current Nu model.</p>
<h2>Decision</h2>
@ -26,7 +26,7 @@ css_class: "category-adr"
<h2>Alternatives considered</h2>
<ul><li><strong>Nu overlay use for runtime domain loading</strong><em>rejected:</em> overlay use creates an isolated namespace — commands defined in an overlayed module cannot be called by name in the parent scope. It is also parse-time in module context. Confirmed broken: nu -c 'use FILE *; command args' causes infinite recursion when called from def main.</li><li><strong>Add domain commands directly to reflection/bin/ontoref.nu</strong><em>rejected:</em> Would require hardcoding every domain's commands in the main dispatcher, or using dynamic path strings in `use` which Nu forbids. Also violates the no-enforcement axiom — the main dispatcher should not know about PersonalOntology specifics.</li><li><strong>Project-local scripts/ (scripts/jpl.nu approach)</strong><em>rejected:</em> Invisible to ore help and ore describe capabilities. Not portable across PersonalOntology projects. Namespace requires prefix (jpl cfp vs cfp). Dispatch requires knowing the script path.</li></ul>
<ul><li><strong>Nu overlay use for runtime domain loading</strong><em>rejected:</em> overlay use creates an isolated namespace — commands defined in an overlayed module cannot be called by name in the parent scope. It is also parse-time in module context. Confirmed broken: nu -c 'use FILE *; command args' causes infinite recursion when called from def main.</li><li><strong>Add domain commands directly to reflection/bin/ontoref.nu</strong><em>rejected:</em> Would require hardcoding every domain's commands in the main dispatcher, or using dynamic path strings in `use` which Nu forbids. Also violates the no-enforcement axiom — the main dispatcher should not know about PersonalOntology specifics.</li><li><strong>Project-local scripts/ (scripts/jpl.nu approach)</strong><em>rejected:</em> Invisible to ontoref help and ontoref describe capabilities. Not portable across PersonalOntology projects. Namespace requires prefix (jpl cfp vs cfp). Dispatch requires knowing the script path.</li></ul>
<h2>Related ADRs</h2>

View file

@ -22,7 +22,7 @@ css_class: "category-adr"
<h2>Constraints</h2>
<ul><li><strong>Hard</strong> The credentials required to pull the src-vault OCI artifact from ZOT must be stored outside the vault itself — each src-vault has its own access credentials, held in the actor's local .kage, not inside any vault it protects</li><li><strong>Hard</strong> RegistryEntry must not use credential_env — only credential_sops or credential_oidc are valid credential reference fields</li><li><strong>Hard</strong> Every *.sops.yaml credential file must include at minimum the admin and the bound_actor recipients for its access class</li><li><strong>Hard</strong> Any domain or mode that pushes or pulls from a registry must declare uses_registry referencing the RegistryEntry id in manifest.ncl</li><li><strong>Soft</strong> All sops operations on registry credential files must go through ore secrets — direct sops invocations bypass lock state and audit log</li><li><strong>Hard</strong> Every oras invocation must use DOCKER_CONFIG pointing to a tmpdir containing only the credential for the target registry endpoint — no ambient ~/.docker/config.json</li><li><strong>Hard</strong> vault_key must never be written to disk in plaintext — it is decrypted from access.sops.yaml into RESTIC_PASSWORD or KOPIA_PASSWORD for the duration of the operation only</li><li><strong>Soft</strong> All vault snapshot operations must use vault-backend.nu — direct restic or kopia invocations are not permitted in recipes or modules</li><li><strong>Hard</strong> Every push of src-vault/&lt;project&gt;:latest to ZOT must produce a cosign signature; every pull must verify the signature before the artifact is trusted</li></ul>
<ul><li><strong>Hard</strong> The credentials required to pull the src-vault OCI artifact from ZOT must be stored outside the vault itself — each src-vault has its own access credentials, held in the actor's local .kage, not inside any vault it protects</li><li><strong>Hard</strong> RegistryEntry must not use credential_env — only credential_sops or credential_oidc are valid credential reference fields</li><li><strong>Hard</strong> Every *.sops.yaml credential file must include at minimum the admin and the bound_actor recipients for its access class</li><li><strong>Hard</strong> Any domain or mode that pushes or pulls from a registry must declare uses_registry referencing the RegistryEntry id in manifest.ncl</li><li><strong>Soft</strong> All sops operations on registry credential files must go through ontoref secrets — direct sops invocations bypass lock state and audit log</li><li><strong>Hard</strong> Every oras invocation must use DOCKER_CONFIG pointing to a tmpdir containing only the credential for the target registry endpoint — no ambient ~/.docker/config.json</li><li><strong>Hard</strong> vault_key must never be written to disk in plaintext — it is decrypted from access.sops.yaml into RESTIC_PASSWORD or KOPIA_PASSWORD for the duration of the operation only</li><li><strong>Soft</strong> All vault snapshot operations must use vault-backend.nu — direct restic or kopia invocations are not permitted in recipes or modules</li><li><strong>Hard</strong> Every push of src-vault/&lt;project&gt;:latest to ZOT must produce a cosign signature; every pull must verify the signature before the artifact is trusted</li></ul>
<h2>Alternatives considered</h2>

View file

@ -18,7 +18,7 @@ css_class: "category-adr"
<h2>Decision</h2>
<p>Formalize the three-level hierarchy with four mechanisms: (1) Level identity declaration — each ontoref instance declares level.index (1=base, 2=domain, 3=instance), level.name, and level.parent (name of the level above; absent at level 1) in manifest.ncl. This makes level identity explicit and queryable. (2) Per-mode resolution strategy — each reflection mode declares a strategy field using one of four values: 'Override (implementation is complete at this level, traversal stops), 'Delegate (no implementation here, traverse to parent), 'Merge (accumulate fields bottom-up across all levels; lower level wins on conflicts), 'Compose (declare explicit partial inheritance via an extends field naming specific steps or fields to inherit from the parent). Strategy is required at level 2+; absent at level 1 (base is always Override by definition). Implicit absence at level 2+ is treated as 'Delegate with a Soft validation warning. (3) FSM-bound strategy transitions — when a mode's strategy is expected to change as the project matures, declare a state dimension in state.ncl whose current_state tracks the strategy value. The transition from 'Delegate to 'Override (or any other pair) is then an observable FSM event: tracked in state.ncl, visible in ore describe state, and triggerable by the same transition conditions as any other dimension. Strategy changes are architectural events, not silent refactors. (4) Observable traversal via ore mode resolve — the command 'ore mode resolve &lt;id&gt;' reports which level answered the operation, the strategy applied, the source file, and the reason (declared strategy or FSM state). No mode resolution is ever silent.</p>
<p>Formalize the three-level hierarchy with four mechanisms: (1) Level identity declaration — each ontoref instance declares level.index (1=base, 2=domain, 3=instance), level.name, and level.parent (name of the level above; absent at level 1) in manifest.ncl. This makes level identity explicit and queryable. (2) Per-mode resolution strategy — each reflection mode declares a strategy field using one of four values: 'Override (implementation is complete at this level, traversal stops), 'Delegate (no implementation here, traverse to parent), 'Merge (accumulate fields bottom-up across all levels; lower level wins on conflicts), 'Compose (declare explicit partial inheritance via an extends field naming specific steps or fields to inherit from the parent). Strategy is required at level 2+; absent at level 1 (base is always Override by definition). Implicit absence at level 2+ is treated as 'Delegate with a Soft validation warning. (3) FSM-bound strategy transitions — when a mode's strategy is expected to change as the project matures, declare a state dimension in state.ncl whose current_state tracks the strategy value. The transition from 'Delegate to 'Override (or any other pair) is then an observable FSM event: tracked in state.ncl, visible in ontoref describe state, and triggerable by the same transition conditions as any other dimension. Strategy changes are architectural events, not silent refactors. (4) Observable traversal via ontoref mode resolve — the command 'ontoref mode resolve &lt;id&gt;' reports which level answered the operation, the strategy applied, the source file, and the reason (declared strategy or FSM state). No mode resolution is ever silent.</p>
<h2>Constraints</h2>

View file

@ -77,7 +77,7 @@ implementation is tracked as backlog item bl-014.</p>
<h2>Alternatives considered</h2>
<ul><li><strong>Leave the gap as implicit Rust source TODOs</strong><em>rejected:</em> Implicit TODOs are not queryable. ADR-031 on+re duality requires protocol gaps to live on the ontology axis. Cost of this ADR is small; cost of every consumer rediscovering the gap grows linearly with the consumer count.</li><li><strong>Implement substrate effects directly in ADR-033 §FULL EFFECTS instead of splitting</strong><em>rejected:</em> ADR-033 §FULL EFFECTS was a self-contained PR-sized scope: tier_transition module + tests + CLI surface + watcher + holder + handler. Including substrate would have ballooned the scope across multiple substrate crates and pushed the ship date indefinitely. Splitting lets ADR-033 ship its value (working ore tier set + guard + tests) immediately.</li><li><strong>Persist the oplog via stratumiops stratum-db</strong><em>rejected:</em> An earlier draft of this ADR assumed stratum-db for oplog persistence. The implemented `ontoref-oplog` is redb-backed and self-contained in the workspace, enforcing signature + parent-existence + monotonic-HLC checks on every append. Using stratum-db would reintroduce an external path dependency for tier-2, contradicting ADR-001's minimal-adoption posture and ADR-029's offline-first default. redb keeps the witness chain dependency-free.</li></ul>
<ul><li><strong>Leave the gap as implicit Rust source TODOs</strong><em>rejected:</em> Implicit TODOs are not queryable. ADR-031 on+re duality requires protocol gaps to live on the ontology axis. Cost of this ADR is small; cost of every consumer rediscovering the gap grows linearly with the consumer count.</li><li><strong>Implement substrate effects directly in ADR-033 §FULL EFFECTS instead of splitting</strong><em>rejected:</em> ADR-033 §FULL EFFECTS was a self-contained PR-sized scope: tier_transition module + tests + CLI surface + watcher + holder + handler. Including substrate would have ballooned the scope across multiple substrate crates and pushed the ship date indefinitely. Splitting lets ADR-033 ship its value (working ontoref tier set + guard + tests) immediately.</li><li><strong>Persist the oplog via stratumiops stratum-db</strong><em>rejected:</em> An earlier draft of this ADR assumed stratum-db for oplog persistence. The implemented `ontoref-oplog` is redb-backed and self-contained in the workspace, enforcing signature + parent-existence + monotonic-HLC checks on every append. Using stratum-db would reintroduce an external path dependency for tier-2, contradicting ADR-001's minimal-adoption posture and ADR-029's offline-first default. redb keeps the witness chain dependency-free.</li></ul>
<h2>Related ADRs</h2>

View file

@ -77,14 +77,14 @@ reflection schema `reflection/schemas/interaction.ncl` (migration 0028).</p>
rather than redeclaring parallel vocabularies that would drift. The
runtime validator parses enum members FROM the schema files at runtime, so
the allowlist can never diverge from the contract.</p>
<p>5. Two emission paths, both supported. `ore interaction record` validates a
<p>5. Two emission paths, both supported. `ontoref interaction record` validates a
record structurally before appending (the forced path; malformed records
are rejected). Direct JSONL append by the agent is also permitted (the
free path), audited post-hoc by `ore interaction validate` (pre-commit /
free path), audited post-hoc by `ontoref interaction validate` (pre-commit /
CI gate). The `--no-validate` flag bridges the two on the same command.</p>
<p>6. Decisions materialise the adr? criteria as data. `Decision` carries
`alternatives_rejected`, `reversible`, `adr_candidate`, and
`tensions_engaged` (core.ncl node ids). `ore interaction decisions
`tensions_engaged` (core.ncl node ids). `ontoref interaction decisions
--adr-candidates` surfaces ADR candidates across sessions in one query,
feeding the ADR lifecycle from observed decisions rather than recollection.</p>
<p>The layer is opt-in per ADR-029: a project that never emits interaction JSONL

View file

@ -55,7 +55,7 @@ schema/catalog additions to consumers.</p>
build kind, the build/distribution catalog entries, and the generator that
emits .woodpecker/release.yml + install/install.sh + justfiles/release.just
all live in the NCL workflow model. Distribution is therefore self-describing
and queryable via `ore workflow`, like every other build/CI artifact.</p>
and queryable via `ontoref workflow`, like every other build/CI artifact.</p>
<p>2. Build-once; the image is assembled FROM the binary, never recompiled. The
musl binary is cross-compiled once per arch (`cross`). Both the install
bundle AND the runnable image consume that same binary. The image is

View file

@ -17,7 +17,7 @@ css_class: "category-adr"
<p>Projects accumulate many just recipes (and loose scripts). ontoref already
INVENTORIES recipes — describe.nu::scan-just-recipes runs `just --list` and
categorizes each recipe by a name-prefix heuristic (categorize-recipe), and
`ore describe tools` lists them. But the inventory is shallow: it knows a
`ontoref describe tools` lists them. But the inventory is shallow: it knows a
recipe's name, its trailing `#` comment, and a guessed bucket. It cannot answer
&quot;which is the canonical way to run all checks&quot;, &quot;show me the deploy tasks&quot;, or
&quot;what does build-standalone do and why&quot; — because the recipe carries no declared
@ -26,16 +26,16 @@ INTENT and no real categorization, only a prefix guess.</p>
<p>- Recipes (justfiles) are the MECHANISM layer — the concrete command. Single
source of truth for HOW.
- Modes (reflection/modes/*.ncl) are the PROCEDURE layer — multi-step gated
DAGs with actors/steps/depends_on, executed by `ore run`.</p>
DAGs with actors/steps/depends_on, executed by `ontoref run`.</p>
<p>What is missing is not a third store (&quot;devtask&quot; is a target-side abstraction,
not ontoref vocabulary). What is missing is for a recipe to SELF-DESCRIBE — to
declare its intent and categorization in place — exactly as Rust handlers do
with #[onto_api(method, path, description, tags)]: the annotation sits next to
the code, a proc-macro parses it, and inventory aggregates a zero-cost catalog
that `ore describe api` / GET /api/catalog / MCP all read. The same shape is
that `ontoref describe api` / GET /api/catalog / MCP all read. The same shape is
wanted for recipes: an annotation next to the recipe, parsed by ontoref, that
makes justfiles queryable.</p>
<p>The qa routing fix in the same session (ore qa show/list/search/add) added the
<p>The qa routing fix in the same session (ontoref qa show/list/search/add) added the
seeding primitive — a tag-categorized, nickel-validated write path. The question
this ADR settles is the GRAMMAR and the BOUNDARIES of seeding/enriching the tool
surface: how an annotation is written, how it is mutated, and how a project that
@ -67,14 +67,14 @@ grammar, the recommended mode taxonomy, and the new surface to consumers.</p>
<p>2. The recipe is the single source of truth. Annotations live IN the justfile,
above the recipe — never in a parallel devtasks store. ontoref parses them
(scan-just-recipes gains an @onto parser) and DERIVES a catalog, exactly as
#[onto_api] derives artifacts/api-catalog-*.ncl. `ore tool export` writes the
derived catalog; `ore describe tools --mode &lt;m&gt; --label &lt;l&gt;` reads it.</p>
<p>3. Mutation via a transversal verb. `ore + &lt;noun&gt; &lt;args&gt;` upserts and
`ore - &lt;noun&gt; &lt;args&gt;` removes, across stores:</p>
<p>ore + tool ci-full --mode ci --labels ci,gate --intent &quot;all checks&quot;
ore - tool ci-full # removes the @onto line only
ore + qa &quot;&lt;q&gt;&quot; &quot;&lt;a&gt;&quot; --tags devtool,ci # the qa add path
ore - qa &lt;id&gt;</p>
#[onto_api] derives artifacts/api-catalog-*.ncl. `ontoref tool export` writes the
derived catalog; `ontoref describe tools --mode &lt;m&gt; --label &lt;l&gt;` reads it.</p>
<p>3. Mutation via a transversal verb. `ontoref + &lt;noun&gt; &lt;args&gt;` upserts and
`ontoref - &lt;noun&gt; &lt;args&gt;` removes, across stores:</p>
<p>ontoref + tool ci-full --mode ci --labels ci,gate --intent &quot;all checks&quot;
ontoref - tool ci-full # removes the @onto line only
ontoref + qa &quot;&lt;q&gt;&quot; &quot;&lt;a&gt;&quot; --tags devtool,ci # the qa add path
ontoref - qa &lt;id&gt;</p>
<p>`+ tool` / `- tool` edit the `@onto` comment ABOVE the named recipe in
place, validated against `just --list`; they NEVER touch the recipe body.
`+`/`-` are first-level dispatcher tokens taking `&lt;noun&gt; &lt;args&gt;`, so new
@ -84,7 +84,7 @@ grammar, the recommended mode taxonomy, and the new surface to consumers.</p>
behavior). Annotation raises a recipe from guessed to declared; it is never
required to run a recipe. This is the `formalization-vs-adoption` balance
made literal — &quot;optional layers, not mandatory gates&quot;.</p>
<p>5. Non-destructive compliance/migration. A `compliant` mode (and `ore tool
<p>5. Non-destructive compliance/migration. A `compliant` mode (and `ontoref tool
migrate`) scans a project's existing justfiles, proposes `@onto` annotations
(seeding `mode` from categorize-recipe + name, leaving `intent` for the
author), and — opt-in — reorganizes recipes into justfiles/&lt;mode&gt;.just. It
@ -92,7 +92,7 @@ grammar, the recommended mode taxonomy, and the new surface to consumers.</p>
deleted or renamed, reorg is opt-in and reversible, and the project's own
recipes are first-class (the mechanism annotates them, it does not supplant
them with ontoref-blessed ones).</p>
<p>6. Drift-checkable round-trip. `ore tool audit` reports orphan annotations
<p>6. Drift-checkable round-trip. `ontoref tool audit` reports orphan annotations
(an `@onto` whose recipe is gone from `just --list`) and, at compliance
level, un-annotated recipes. Mirrors docs-drift: the annotation and the
recipe must stay in sync because they are co-located.</p>
@ -102,7 +102,7 @@ annotated recipes are complementary, not competing.</p>
<h2>Constraints</h2>
<ul><li><strong>Hard</strong> A recipe in a justfile is the single source of truth for its command. @onto metadata MUST live in a comment co-located with the recipe; there MUST NOT be a parallel store of recipe command strings. Any tool catalog is DERIVED from the annotations and MUST be regenerated, not hand-edited.</li><li><strong>Hard</strong> The annotation MUST be a single comment line `# @onto key=val ...` immediately above the recipe. Recognized keys: `mode` (one token), `labels` (comma-separated), `intent` (double-quoted free text). Unknown keys MUST be ignored, not error. The recipe name and its plain `#` doc comment MUST be left intact.</li><li><strong>Hard</strong> Annotation/entry mutation MUST go through `ore + &lt;noun&gt; &lt;args&gt;` (upsert) and `ore - &lt;noun&gt; &lt;args&gt;` (remove). For noun `tool`, the verb edits/removes ONLY the @onto comment above the named recipe, validated against `just --list`, and MUST NOT modify the recipe body. New stores MUST be added as nouns, not as new top-level verbs.</li><li><strong>Hard</strong> An un-annotated recipe MUST remain valid, runnable, and inventoried by name+category. Annotation MUST NOT be a precondition for running or listing a recipe. Compliance is reported by audit, never enforced as a gate.</li><li><strong>Hard</strong> The compliance/migration mechanism MUST NOT delete or rename any project-owned recipe. It annotates in place and may propose reorganization into justfiles/&lt;mode&gt;.just only as an opt-in, semantics-preserving, reversible step. A project's own recipes are first-class and MUST survive migration unchanged in behavior.</li><li><strong>Soft</strong> `ore tool audit` MUST report orphan annotations (an @onto whose recipe is absent from `just --list`) and, at compliance level, un-annotated recipes. Every @onto MUST reference an existing recipe.</li><li><strong>Soft</strong> The recommended `mode` taxonomy (tools/dev/build/test/ci/deploy/docs/dist/secrets/…) is advisory guidance, not an enforced enum; `labels` are free-form. Validation MUST NOT reject an unrecognized mode or label.</li></ul>
<ul><li><strong>Hard</strong> A recipe in a justfile is the single source of truth for its command. @onto metadata MUST live in a comment co-located with the recipe; there MUST NOT be a parallel store of recipe command strings. Any tool catalog is DERIVED from the annotations and MUST be regenerated, not hand-edited.</li><li><strong>Hard</strong> The annotation MUST be a single comment line `# @onto key=val ...` immediately above the recipe. Recognized keys: `mode` (one token), `labels` (comma-separated), `intent` (double-quoted free text). Unknown keys MUST be ignored, not error. The recipe name and its plain `#` doc comment MUST be left intact.</li><li><strong>Hard</strong> Annotation/entry mutation MUST go through `ontoref + &lt;noun&gt; &lt;args&gt;` (upsert) and `ontoref - &lt;noun&gt; &lt;args&gt;` (remove). For noun `tool`, the verb edits/removes ONLY the @onto comment above the named recipe, validated against `just --list`, and MUST NOT modify the recipe body. New stores MUST be added as nouns, not as new top-level verbs.</li><li><strong>Hard</strong> An un-annotated recipe MUST remain valid, runnable, and inventoried by name+category. Annotation MUST NOT be a precondition for running or listing a recipe. Compliance is reported by audit, never enforced as a gate.</li><li><strong>Hard</strong> The compliance/migration mechanism MUST NOT delete or rename any project-owned recipe. It annotates in place and may propose reorganization into justfiles/&lt;mode&gt;.just only as an opt-in, semantics-preserving, reversible step. A project's own recipes are first-class and MUST survive migration unchanged in behavior.</li><li><strong>Soft</strong> `ontoref tool audit` MUST report orphan annotations (an @onto whose recipe is absent from `just --list`) and, at compliance level, un-annotated recipes. Every @onto MUST reference an existing recipe.</li><li><strong>Soft</strong> The recommended `mode` taxonomy (tools/dev/build/test/ci/deploy/docs/dist/secrets/…) is advisory guidance, not an enforced enum; `labels` are free-form. Validation MUST NOT reject an unrecognized mode or label.</li></ul>
<h2>Alternatives considered</h2>

View file

@ -100,7 +100,7 @@ truth.</p>
with its own in one directory. The DECLARATION (card.ncl) plus the .ontoref/
content are the source of truth; the .claude/ symlink is a derived view.</p>
<p>5. Propagation rides the update/migration mechanism, not bespoke symlink edits.
`ore conventions sync` (and the `update_ontoref` mode) reconcile each declared
`ontoref conventions sync` (and the `update_ontoref` mode) reconcile each declared
binding against the shared origin: detect version drift (origin moved, e.g.
new Rust guideline), report it, and — opt-in — re-link or re-vendor to the
declared version. A shared change reaches every binding project through this
@ -114,26 +114,26 @@ truth.</p>
means writing it to the origin and re-syncing, so all binding projects can
adopt it.</p>
<p>7. Optional, audit-not-gate. A project may declare zero conventions and remain
valid. `ore conventions audit` reports: bindings whose origin is unreachable,
valid. `ontoref conventions audit` reports: bindings whose origin is unreachable,
version drift (declared != origin), and resolution-mode mismatches (declared
symlink but found vendored, or vice versa). It REPORTS; it does not block —
the formalization-vs-adoption resolution.</p>
<p>These are MODES/utilities, not new stores: per the user's framing, guidelines/CI/
config bindings are implementation-framework utilities (templates, examples,
sync). `ore conventions sync|audit|describe` and the `update_ontoref` extension
sync). `ontoref conventions sync|audit|describe` and the `update_ontoref` extension
are the operational surface; card.ncl carries the declaration.</p>
<h2>Constraints</h2>
<ul><li><strong>Hard</strong> A project's convention bindings (guidelines per language, CI reference, config conventions) MUST be declared in .ontoref/card.ncl as a `conventions` block naming source + version. Guideline CONTENT MUST NOT be inlined; the declaration names sources, content resolves from the shared origin.</li><li><strong>Hard</strong> Convention CONTENT MUST live authoritatively under .ontoref/conventions/&lt;lang&gt;/ (protocol-first). .claude/guidelines/&lt;lang&gt;/ MUST be a materialized view (a symlink into .ontoref/conventions/), never the authoritative location. The conventions MUST resolve with no .claude/ present. The card.ncl declaration plus the .ontoref/ content are the source of truth; per-file resolution mode (symlink to origin | vendored | local-own) is recorded, and a project's own guideline is a real file beside the shared ones.</li><li><strong>Hard</strong> Convention propagation/sync MUST be implemented as an extension of the existing migration system (ADR-010) and the update_ontoref mode — e.g. `ore conventions sync` and an update_ontoref step — NOT as a separate symlink-specific propagation path. A shared-origin change reaching consumers MUST be expressible as / accompanied by a migration.</li><li><strong>Hard</strong> The shared origin MUST be authoritative for shared conventions. A project MUST NOT silently fork a bound convention by editing a vendored copy in place. Local refinements are promoted by writing to the origin and bumping the declared version; divergence MUST be reported by audit, not auto-merged.</li><li><strong>Soft</strong> Convention bindings MUST be optional: a project may declare none and remain valid. `ore conventions audit` MUST report unreachable origins, version drift, and resolution-mode mismatches, but MUST NOT block builds or commands.</li><li><strong>Soft</strong> The `ci` convention binding MUST reference a workflow layer id (ADR-038 model), not re-declare CI steps in the conventions surface. `describe conventions` cross-links to the workflow model rather than duplicating it.</li></ul>
<ul><li><strong>Hard</strong> A project's convention bindings (guidelines per language, CI reference, config conventions) MUST be declared in .ontoref/card.ncl as a `conventions` block naming source + version. Guideline CONTENT MUST NOT be inlined; the declaration names sources, content resolves from the shared origin.</li><li><strong>Hard</strong> Convention CONTENT MUST live authoritatively under .ontoref/conventions/&lt;lang&gt;/ (protocol-first). .claude/guidelines/&lt;lang&gt;/ MUST be a materialized view (a symlink into .ontoref/conventions/), never the authoritative location. The conventions MUST resolve with no .claude/ present. The card.ncl declaration plus the .ontoref/ content are the source of truth; per-file resolution mode (symlink to origin | vendored | local-own) is recorded, and a project's own guideline is a real file beside the shared ones.</li><li><strong>Hard</strong> Convention propagation/sync MUST be implemented as an extension of the existing migration system (ADR-010) and the update_ontoref mode — e.g. `ontoref conventions sync` and an update_ontoref step — NOT as a separate symlink-specific propagation path. A shared-origin change reaching consumers MUST be expressible as / accompanied by a migration.</li><li><strong>Hard</strong> The shared origin MUST be authoritative for shared conventions. A project MUST NOT silently fork a bound convention by editing a vendored copy in place. Local refinements are promoted by writing to the origin and bumping the declared version; divergence MUST be reported by audit, not auto-merged.</li><li><strong>Soft</strong> Convention bindings MUST be optional: a project may declare none and remain valid. `ontoref conventions audit` MUST report unreachable origins, version drift, and resolution-mode mismatches, but MUST NOT block builds or commands.</li><li><strong>Soft</strong> The `ci` convention binding MUST reference a workflow layer id (ADR-038 model), not re-declare CI steps in the conventions surface. `describe conventions` cross-links to the workflow model rather than duplicating it.</li></ul>
<h2>Alternatives considered</h2>
<ul><li><strong>Keep symlinks as the mechanism, add nothing</strong><em>rejected:</em> Symlinks are invisible to ontoref, unversioned, and break on clone/CI/container. They cannot answer 'what version' or 'is this in sync', and a shared bump requires manual per-project symlink work. The whole point is to make bindings declared, queryable, and synced.</li><li><strong>Declare conventions in a new .ontoref/catalog/conventions.ncl</strong><em>rejected:</em> Fragments 'what the project is bound to' away from the card.ncl beacon and duplicates the read surface that describe guides/config already provide. A catalog suits kind-extensible operation inventories (ADR-034), not the small, identity-level binding declaration. Chosen against, but the catalog remains the right home if bindings later grow rich enough to need their own kind system.</li><li><strong>A bespoke convention-sync tool independent of the migration system</strong><em>rejected:</em> Duplicates ADR-010's propagation mechanism and update_ontoref, and stays outside `ore migrate`/`describe`. Convention propagation is the same event shape as protocol propagation; it must reuse that one path.</li><li><strong>Vendor (copy) conventions only, drop symlinks entirely</strong><em>rejected:</em> Loses the transparent dev-loop where editing the origin is instantly reflected. Both modes have a place: symlink for local dev, vendored for clone/CI/container. The declaration records which mode applies; forcing one collapses a useful choice.</li><li><strong>Inline guideline content into card.ncl / the project</strong><em>rejected:</em> Bloats the beacon, defeats sharing, and guarantees drift from the origin. Bindings must name sources+versions and resolve content from the shared origin, never inline it.</li></ul>
<ul><li><strong>Keep symlinks as the mechanism, add nothing</strong><em>rejected:</em> Symlinks are invisible to ontoref, unversioned, and break on clone/CI/container. They cannot answer 'what version' or 'is this in sync', and a shared bump requires manual per-project symlink work. The whole point is to make bindings declared, queryable, and synced.</li><li><strong>Declare conventions in a new .ontoref/catalog/conventions.ncl</strong><em>rejected:</em> Fragments 'what the project is bound to' away from the card.ncl beacon and duplicates the read surface that describe guides/config already provide. A catalog suits kind-extensible operation inventories (ADR-034), not the small, identity-level binding declaration. Chosen against, but the catalog remains the right home if bindings later grow rich enough to need their own kind system.</li><li><strong>A bespoke convention-sync tool independent of the migration system</strong><em>rejected:</em> Duplicates ADR-010's propagation mechanism and update_ontoref, and stays outside `ontoref migrate`/`describe`. Convention propagation is the same event shape as protocol propagation; it must reuse that one path.</li><li><strong>Vendor (copy) conventions only, drop symlinks entirely</strong><em>rejected:</em> Loses the transparent dev-loop where editing the origin is instantly reflected. Both modes have a place: symlink for local dev, vendored for clone/CI/container. The declaration records which mode applies; forcing one collapses a useful choice.</li><li><strong>Inline guideline content into card.ncl / the project</strong><em>rejected:</em> Bloats the beacon, defeats sharing, and guarantees drift from the origin. Bindings must name sources+versions and resolve content from the shared origin, never inline it.</li></ul>
<h2>Anti-patterns</h2>
<ul><li><strong>Treating .claude Symlinks as the Authoritative Content</strong> — Convention content lives authoritatively under .claude/guidelines/* (harness-specific) with no card.ncl declaration and nothing under .ontoref/. The binding is invisible to ontoref, unversioned, harness-coupled, and vanishes on clone/CI/container or when there is no Claude Code.</li><li><strong>A Separate Sync Path Outside the Migration System</strong> — Convention propagation is built as its own tool/script independent of the migration system and update_ontoref, so it is invisible to `ore migrate` and duplicates the protocol's propagation mechanism.</li><li><strong>Editing a Vendored Convention In Place</strong> — A project edits its vendored copy of a shared guideline directly, forking the shared origin silently. Other binding projects never see the change and the origin fractures.</li><li><strong>Failing Builds on Convention Drift</strong>`ore conventions audit` (or a hook) blocks commands/builds when a binding is drifted or undeclared, turning an optional layer into a mandatory gate.</li><li><strong>Re-Declaring CI in the Conventions Surface</strong> — CI steps are copied into card.ncl's conventions block instead of referencing the workflow layer, creating a second drifting CI source.</li></ul>
<ul><li><strong>Treating .claude Symlinks as the Authoritative Content</strong> — Convention content lives authoritatively under .claude/guidelines/* (harness-specific) with no card.ncl declaration and nothing under .ontoref/. The binding is invisible to ontoref, unversioned, harness-coupled, and vanishes on clone/CI/container or when there is no Claude Code.</li><li><strong>A Separate Sync Path Outside the Migration System</strong> — Convention propagation is built as its own tool/script independent of the migration system and update_ontoref, so it is invisible to `ontoref migrate` and duplicates the protocol's propagation mechanism.</li><li><strong>Editing a Vendored Convention In Place</strong> — A project edits its vendored copy of a shared guideline directly, forking the shared origin silently. Other binding projects never see the change and the origin fractures.</li><li><strong>Failing Builds on Convention Drift</strong>`ontoref conventions audit` (or a hook) blocks commands/builds when a binding is drifted or undeclared, turning an optional layer into a mandatory gate.</li><li><strong>Re-Declaring CI in the Conventions Surface</strong> — CI steps are copied into card.ncl's conventions block instead of referencing the workflow layer, creating a second drifting CI source.</li></ul>
<h2>Related ADRs</h2>

View file

@ -54,7 +54,7 @@ first-class typed kind, bound by a Proof seam:</p>
<p>The crossings are explicit: `ValueProp` is the WHAT×WHOM seam (gains
`differentiator_ids`); `Campaign` and `Proof` bind HOW and VALIDATION.</p>
<p>The marketing surface is governed by the same mechanism it sells. Five coherence
rules run in `ore positioning audit` (local-first, daemon-independent):</p>
rules run in `ontoref positioning audit` (local-first, daemon-independent):</p>
<p>1. AnchorDrift — a differentiator/proof evidence_adr must be Accepted.
2. UnprovenValidatedAudience — an Audience marked 'Validated needs &gt;=1 Proof.
3. Live value-prop — must converge four elements (differentiator +
@ -69,7 +69,7 @@ Validated/Live claims must cite Accepted ADRs — mirroring the
<h2>Constraints</h2>
<ul><li><strong>Hard</strong> The positioning schema MUST keep WHAT (Differentiator), FOR WHOM (Audience) and HOW (DifusionMechanism) as separate typed kinds. Cross-axis binding MUST happen only through the seam kinds (ValueProp for what×whom; Campaign and Proof). Collapsing an axis back into another kind is forbidden.</li><li><strong>Hard</strong> Proof MUST reference the other three axes (converges.differentiator_ids, audience_ids, mechanism_ids each non-empty) and MUST NOT be referenced by any other kind. The make_proof contract (`_proof_converges_all`) enforces the triple convergence.</li><li><strong>Hard</strong> Every Differentiator MUST cite &gt;=1 ontology node and &gt;=1 evidence ADR, and name &gt;=1 competitor category it beats (`vs`). The make_differentiator contracts (`_diff_requires_anchor`, `_diff_requires_vs`) enforce this.</li><li><strong>Hard</strong> `ore positioning audit` MUST run the five coherence rules over the positioning layer and gate CI by value-prop status: hard ADR-drift on Validated/Live value-props and any coherence violation fail; Draft drift and coverage gaps are informational.</li><li><strong>Soft</strong> A value-prop with status 'Live MUST bind &gt;=1 differentiator (the WHAT axis); the `_live_requires_differentiator` contract enforces the local half, and the audit enforces the rest (a difundiendo mechanism and a proven differentiator).</li></ul>
<ul><li><strong>Hard</strong> The positioning schema MUST keep WHAT (Differentiator), FOR WHOM (Audience) and HOW (DifusionMechanism) as separate typed kinds. Cross-axis binding MUST happen only through the seam kinds (ValueProp for what×whom; Campaign and Proof). Collapsing an axis back into another kind is forbidden.</li><li><strong>Hard</strong> Proof MUST reference the other three axes (converges.differentiator_ids, audience_ids, mechanism_ids each non-empty) and MUST NOT be referenced by any other kind. The make_proof contract (`_proof_converges_all`) enforces the triple convergence.</li><li><strong>Hard</strong> Every Differentiator MUST cite &gt;=1 ontology node and &gt;=1 evidence ADR, and name &gt;=1 competitor category it beats (`vs`). The make_differentiator contracts (`_diff_requires_anchor`, `_diff_requires_vs`) enforce this.</li><li><strong>Hard</strong> `ontoref positioning audit` MUST run the five coherence rules over the positioning layer and gate CI by value-prop status: hard ADR-drift on Validated/Live value-props and any coherence violation fail; Draft drift and coverage gaps are informational.</li><li><strong>Soft</strong> A value-prop with status 'Live MUST bind &gt;=1 differentiator (the WHAT axis); the `_live_requires_differentiator` contract enforces the local half, and the audit enforces the rest (a difundiendo mechanism and a proven differentiator).</li></ul>
<h2>Alternatives considered</h2>

View file

@ -18,7 +18,7 @@ css_class: "category-adr"
<h2>Decision</h2>
<p>Generalize ADR-018's absolute triad into a relative parent chain and add a co-tenancy declaration, both opt-in and backward-compatible, via four mechanisms. (1) RELATIVE LEVEL CHAIN — level identity is the parent chain, not an absolute index. A manifest declares level.parent (a name reference, exactly as ADR-018) and an optional level.role label; level.index becomes a DERIVED depth (walk parents to a base) that is no longer capped at 3 and no longer authoritative. The names base/domain/instance are reinterpreted as ROLES in a sliding window of three relative to an observation point, not global coordinates. Existing index 1/2/3 declarations remain valid as the depth&lt;=3 case; deeper chains simply keep walking. The terminal node of one chain may itself be the parent-root of another (instance-as-host is a well-formed shape, not an error). (2) DOMAIN CO-TENANCY — a repo may declare manifest.ncl::hosts, an array of { chain (name reference to a foreign domain artifact), mount (where in this repo the hosted chain's Layer-3 wiring lives), digest (optional witness pin) }. The foreign chain is resolved by name through catalog discovery (ADR-030) and verified by witness (ADR-028), NEVER cloned. The host carries only Layer-3 wiring for the hosted chain (ADR-020), tagged layer-3-boundary. (3) MUTATION SOVEREIGNTY UNDER CO-TENANCY — a host's runtime MUST NOT mutate the authoritative state of any hosted chain. Hosting is placement plus wiring; each chain's authoritative state mutates only through its own declared operations with its own witness (ADR-024). Co-tenancy is co-residency of wiring, not transfer of state ownership. (4) OBSERVABLE CROSS-CHAIN RESOLUTION — ore mode resolve extends to report the full traversal PATH (every level visited, not a single hop) and the CHAIN SCOPE (which chain answered) when multiple chains are co-resident; ore describe state disambiguates FSM dimensions by chain scope. No cross-chain resolution is silent.</p>
<p>Generalize ADR-018's absolute triad into a relative parent chain and add a co-tenancy declaration, both opt-in and backward-compatible, via four mechanisms. (1) RELATIVE LEVEL CHAIN — level identity is the parent chain, not an absolute index. A manifest declares level.parent (a name reference, exactly as ADR-018) and an optional level.role label; level.index becomes a DERIVED depth (walk parents to a base) that is no longer capped at 3 and no longer authoritative. The names base/domain/instance are reinterpreted as ROLES in a sliding window of three relative to an observation point, not global coordinates. Existing index 1/2/3 declarations remain valid as the depth&lt;=3 case; deeper chains simply keep walking. The terminal node of one chain may itself be the parent-root of another (instance-as-host is a well-formed shape, not an error). (2) DOMAIN CO-TENANCY — a repo may declare manifest.ncl::hosts, an array of { chain (name reference to a foreign domain artifact), mount (where in this repo the hosted chain's Layer-3 wiring lives), digest (optional witness pin) }. The foreign chain is resolved by name through catalog discovery (ADR-030) and verified by witness (ADR-028), NEVER cloned. The host carries only Layer-3 wiring for the hosted chain (ADR-020), tagged layer-3-boundary. (3) MUTATION SOVEREIGNTY UNDER CO-TENANCY — a host's runtime MUST NOT mutate the authoritative state of any hosted chain. Hosting is placement plus wiring; each chain's authoritative state mutates only through its own declared operations with its own witness (ADR-024). Co-tenancy is co-residency of wiring, not transfer of state ownership. (4) OBSERVABLE CROSS-CHAIN RESOLUTION — ontoref mode resolve extends to report the full traversal PATH (every level visited, not a single hop) and the CHAIN SCOPE (which chain answered) when multiple chains are co-resident; ontoref describe state disambiguates FSM dimensions by chain scope. No cross-chain resolution is silent.</p>
<h2>Constraints</h2>

View file

@ -16,7 +16,7 @@ css_class: "category-adr"
<p>ADR-035 established positioning as a queryable protocol surface and ADR-043
modelled it as four elements (WHAT / FOR WHOM / HOW bound by the Proof seam),
governed by the five coherence rules of `ore positioning audit` — the first of
governed by the five coherence rules of `ontoref positioning audit` — the first of
which, AnchorDrift, requires that every `evidence_adr` cited by a Differentiator
or Proof resolves to an Accepted ADR. Both ADRs were authored when the
positioning surface had exactly one occupant: ontoref itself. In a single-project
@ -34,7 +34,7 @@ only in rustelo's ADR catalogue, not ontoref's.</p>
<p>Working the placement through surfaced the load-bearing fact: a rustelo
Differentiator authored inside ontoref's `.ontoref/positioning/` would FAIL
AnchorDrift, because ontoref does not own those ADRs. Authored inside rustelo's
own `.ontoref/positioning/`, the same subject passes — verified: `ore positioning
own `.ontoref/positioning/`, the same subject passes — verified: `ontoref positioning
audit` run from rustelo reports `adr-drift: none`. The audit already resolves
`evidence_adrs` against the AUTHORING project's ADR set; the framework simply
never named where that places a subject. This is the same authority-upstream /
@ -68,7 +68,7 @@ the rustelo publishing-contract.</p>
<h2>Constraints</h2>
<ul><li><strong>Hard</strong> A positioning subject that cites `evidence_adrs` MUST be authored in the `.ontoref/positioning/` of the project whose ADR catalogue Accepts those ADRs. `ore positioning audit`, run from that project, MUST report no AnchorDrift for the subject. Placing such a subject in a project that does not Accept its evidence ADRs is a drift finding, not a stylistic choice.</li><li><strong>Soft</strong> A project MUST reference a peer's positioning subject only by ROLE — a prose mention or an id in its OWN authored record — and MUST NOT re-author the peer's typed subject in its own tree. Cross-references between subjects are by id (competitor_ids, differentiator_ids), never by copied record.</li><li><strong>Soft</strong> The sovereignty rule MUST stay additive and opt-in: a project with no `positioning/` directory, or one that references no peer subject, MUST validate unchanged and pay zero adoption cost. The rule adds no kind, folder, or required field to ADR-043's surface.</li></ul>
<ul><li><strong>Hard</strong> A positioning subject that cites `evidence_adrs` MUST be authored in the `.ontoref/positioning/` of the project whose ADR catalogue Accepts those ADRs. `ontoref positioning audit`, run from that project, MUST report no AnchorDrift for the subject. Placing such a subject in a project that does not Accept its evidence ADRs is a drift finding, not a stylistic choice.</li><li><strong>Soft</strong> A project MUST reference a peer's positioning subject only by ROLE — a prose mention or an id in its OWN authored record — and MUST NOT re-author the peer's typed subject in its own tree. Cross-references between subjects are by id (competitor_ids, differentiator_ids), never by copied record.</li><li><strong>Soft</strong> The sovereignty rule MUST stay additive and opt-in: a project with no `positioning/` directory, or one that references no peer subject, MUST validate unchanged and pay zero adoption cost. The rule adds no kind, folder, or required field to ADR-043's surface.</li></ul>
<h2>Alternatives considered</h2>
@ -76,7 +76,7 @@ the rustelo publishing-contract.</p>
<h2>Anti-patterns</h2>
<ul><li><strong>Authoring a Peer's Positioning Subject in the Host's Tree</strong> — A contributor places project B's Differentiator/ValueProp inside project A's `.ontoref/positioning/` to 'own the story in one place'. Its evidence_adrs are Accepted in B, not A, so `ore positioning audit` from A reports AnchorDrift — the subject is homeless, not centralised.</li><li><strong>Duplicating a Subject Across Projects and Syncing by Hand</strong> — The same Differentiator is copied into both the sovereign project and a peer to keep each surface 'complete'. The copies drift, and the peer copy must either strip its anchor (unanchored, rejected) or cite un-Acceptable ADRs (AnchorDrift). Two records, no single sovereign truth — the exact drift AnchorDrift exists to prevent.</li><li><strong>Reaching Into a Peer's ADR Catalogue to Anchor a Local Subject</strong> — To author a peer's subject locally, a contributor adds a resolver so evidence_adrs can cite a peer project's Accepted ADRs. This dissolves ADR sovereignty — the host now anchors claims on decisions it did not make and cannot govern — the opposite of ADR-028's consume-by-reference-cannot-mutate model.</li></ul>
<ul><li><strong>Authoring a Peer's Positioning Subject in the Host's Tree</strong> — A contributor places project B's Differentiator/ValueProp inside project A's `.ontoref/positioning/` to 'own the story in one place'. Its evidence_adrs are Accepted in B, not A, so `ontoref positioning audit` from A reports AnchorDrift — the subject is homeless, not centralised.</li><li><strong>Duplicating a Subject Across Projects and Syncing by Hand</strong> — The same Differentiator is copied into both the sovereign project and a peer to keep each surface 'complete'. The copies drift, and the peer copy must either strip its anchor (unanchored, rejected) or cite un-Acceptable ADRs (AnchorDrift). Two records, no single sovereign truth — the exact drift AnchorDrift exists to prevent.</li><li><strong>Reaching Into a Peer's ADR Catalogue to Anchor a Local Subject</strong> — To author a peer's subject locally, a contributor adds a resolver so evidence_adrs can cite a peer project's Accepted ADRs. This dissolves ADR sovereignty — the host now anchors claims on decisions it did not make and cannot govern — the opposite of ADR-028's consume-by-reference-cannot-mutate model.</li></ul>
<h2>Related ADRs</h2>

View file

@ -23,7 +23,7 @@ UNWITNESSED. So a check can go green over a shrinking fraction of reality and it
perfectly valid. It does not fail. It does not warn. It looks where it always looked, finds nothing
wrong there, and signs.</p>
<p>AND THE COVERAGE EXPIRES PRECISELY WHEN YOU IMPROVE THE MECHANISM. That is the finding, and it is
not a metaphor. In one session, on one witness (`ore rustelo routes`), THREE TIMES:</p>
not a metaphor. In one session, on one witness (`ontoref rustelo routes`), THREE TIMES:</p>
<p>· the SITE gained ownership of its templates (adr-070's overlay). The witness read only the
framework's `crates/pages_htmx/templates/` and reported a site-owned page as broken.
· the CONSUMER gained ownership of its routes (`merge_site_routes`). The witness read only the

View file

@ -0,0 +1,101 @@
---
id: "adr-074"
title: "Context-provision is governed — ontoref is the single source of truth about the protocol; carriers invoke it live and hold no protocol content, pointers, or verifiers of their own"
slug: "074"
subtitle: "Accepted"
excerpt: "A session — a person or an agent — arrives needing to know how the protocol works: how a project"
author: "ontoref"
date: "2026-07-18"
published: true
featured: false
category: "accepted"
tags: ["adr-074", "adr", "accepted"]
css_class: "category-adr"
---
<h2>Context</h2>
<p>A session — a person or an agent — arrives needing to know how the protocol works: how a project
self-governs its relation to a domain, what vocabulary is mandatory, how bonds are declared and
retyped. The convenient move, and the one this session nearly took, is to answer where the session
already looks: write the how-to into the project's CLAUDE.md, propagate a bond-vocabulary block into
every consumer's CLAUDE.md, plant a topic-specific line into a SessionStart hook under `~/.claude/`,
add a verifier next to it. Four turns of the same reflex: put the truth where the reader is standing.</p>
<p>That reflex is drift by construction, and the ontology already names why. `dev-internal-overlay`
classes `.claude/` as dev-internal OPERATIONAL CONFIG — a carrier, not a home for governed content.
`ontoref-three-layer-model` states the rule generically: protocol truth is Layer 1 / protocol-qa,
and &quot;this entry exists so adopters discover the layering from protocol documentation, not by
re-deriving it across three projects&quot;; Layer 3 (per-caller, per-machine wiring) is OUTPUT of a
relationship, &quot;never in this project's repo&quot;, referenced by &quot;explicit pointers, never copy-paste&quot;.
ADR-028 already forbids the clone: the ontology is addressable independently and a consumer verifies
BY WITNESS, NOT BY COPY.</p>
<p>The sharp form of the requirement, as stated in-session: we do not want channels, pointers,
declarations, or verifiers SCATTERED across the universe and its constellations on one particular
machine's disk. ontoref is the one that governs, and is responsible for the truth about questions
like these AS SUCH. The failure is not that carriers exist — an agent does read CLAUDE.md first, and
that is legitimate reach. The failure is content, pointers, and verifiers ACCRETING in those carriers
as a second authority that no witness keeps in sync with the governed surface. Two copies, no witness,
guaranteed divergence the first time one is edited and the other is not.</p>
<p>There is already a correct shape in production and it was almost overlooked: the generic on+re
conduits (the SessionStart orient hook, the UserPromptSubmit routing that answers &quot;esto ya está
gobernado — `ontoref qa show &lt;id&gt;`&quot;). They carry NO protocol content. They ASK ontoref, at read-time,
which governed entry houses the current topic, and surface a live pointer INTO the governed surface.
The content stays in ontoref; the routing is decided by ontoref; the carrier is a thin, generic,
topic-agnostic conduit. That is the pattern this ADR ratifies and generalizes — and it is the pattern
whose alternative (topic-specific truth accreting per carrier, per machine) it forbids.</p>
<h2>Decision</h2>
<p>CONTEXT-PROVISION ABOUT THE PROTOCOL IS GOVERNED. ontoref's own surface — the ontology, reflection
(qa, modes, disciplines, substrate views), adrs, the CLI/daemon/MCP that serve them — is the SINGLE
SOURCE OF TRUTH and the SINGLE RESPONSIBLE AUTHORITY for how the protocol works. The answer to a
&quot;how does X work in the protocol&quot; question is a QUERY answered by ontoref, reached by asking ontoref
at one addressable locus, not a copy read from wherever the asker happened to be standing.</p>
<p>Three rules follow, and they are the contract:</p>
<p>1. TRUTH LIVES ONLY IN THE GOVERNED SURFACE. Protocol knowledge is authored once, in ontology or
reflection, where ontoref can type it, validate it, witness it and version it. A qa entry, a
discipline, a schema, an ADR — these are homes. A carrier is not.</p>
<p>2. CARRIERS INVOKE; THEY DO NOT REPLICATE. An external carrier (a project or global CLAUDE.md,
MEMORY.md, a SessionStart/UserPromptSubmit hook, an agent memory index) may hold at most a THIN,
GENERIC, TOPIC-AGNOSTIC invocation that resolves against ontoref at read-time — the shape the
orient/routing hooks already have. It holds NO protocol content, NO topic-specific pointer table,
NO per-topic declaration. Routing is ontoref's job (the hook asks ontoref which entry is
relevant), never a hand-maintained map in the carrier.</p>
<p>3. THE GOVERNOR VERIFIES; VERIFIERS ARE NOT SCATTERED. If a check is needed that a carrier has not
accreted duplicated truth, that check is ONE command inside ontoref auditing its own carriers —
the governor doing its job — never a validator planted per-machine, per-repo, next to each
carrier as its own little authority. Centralized-in-ontoref is the cure; scattered-across-disk
is the disease, and a scattered verifier is the disease wearing the cure's clothes.</p>
<p>Corollary for the bond work that surfaced this (adr-073): the bond how-to lives as ONE governed qa
entry (`bond-carrier-layout-howto` in `reflection/qa.ncl`) and as the `bonding` discipline/substrate
view that references it — NOT as a bond section propagated into consumer CLAUDE.md files, NOT as a
bond-specific hook line, NOT as a per-repo bond verifier. The existing bond vocabulary block in the
producer-level CLAUDE.md is legacy duplication under this ADR and is to be thinned to the generic
&quot;ask ontoref&quot; conduit, not extended.</p>
<p>SCOPE — TRUTH, NOT INSTRUMENTS. This ADR governs protocol TRUTH: definitions, directives,
vocabulary, how-the-protocol-works knowledge. OPERATIONAL INSTRUMENTS — fill-in-the-blanks forms,
materializable templates, prompt scaffolds, mode step-sequences (reflection/forms/,
reflection/templates/, reflection/modes/) — are NOT carriers of truth: filling one produces a
disposable work product, not a second authority, and materializing one is not replication. An
instrument crosses into this ADR's territory only when it INLINES governed content instead of
referencing it. That boundary is watched SOFTLY, from one place — the instrument registry
(reflection/instruments.ncl) read by `sync instruments check` — and softly on purpose: an
instrument may legitimately NUANCE or EXPLAIN governed content with no intent to stand over the
governance; a match is a finding for a human to judge, never an automatic verdict of drift.</p>
<p>This ADR is itself an instance of its own rule: it is authored once, here, in the governed adrs/
surface; every carrier that needs it points a session at `ontoref` to read it, and copies nothing.</p>
<h2>Constraints</h2>
<ul><li><strong>Hard</strong> The bond context-provision how-to lives in the governed qa surface, not in a carrier.</li><li><strong>Hard</strong> The governed provision channel exists as ontoref infrastructure — the substrate-view/discipline registry through which a session is provisioned by asking ontoref.</li><li><strong>Soft</strong> No protocol content, pointer table, or independent verifier lives in any external carrier (CLAUDE.md, MEMORY.md, hooks) as a second source of truth; carriers hold only a thin generic invocation.</li><li><strong>Soft</strong> Session instruments (forms, templates, prompt scaffolds) reference governed entries instead of inlining governed content; the instrument registry exists and the governor audits it from one place, softly.</li></ul>
<h2>Alternatives considered</h2>
<ul><li><strong>Propagate the content into every consumer's CLAUDE.md (and a hook line, and a memory entry) so a session has it inline without asking.</strong><em>rejected:</em> This is the reflex the ADR exists to stop. It duplicates governed truth into N per-machine carriers with no witness; the first edit that misses one copy is silent drift. dev-internal-overlay classes CLAUDE.md as a carrier, and three-layer-model forbids re-deriving protocol truth across projects.</li><li><strong>Keep content in the governed surface but maintain a per-carrier POINTER TABLE (topic -&gt; entry id) in each CLAUDE.md/hook.</strong><em>rejected:</em> A pointer table is governed content in disguise — it encodes what-houses-what and drifts as the surface changes. Routing belongs to ontoref (the orient/routing hooks already decide relevance live); a hand-maintained map fragments that authority across carriers.</li><li><strong>Enforce no-duplication with a verifier installed alongside each carrier, per repo and per machine.</strong><em>rejected:</em> That is the scattered-verifier the requirement names explicitly: each deputy carries its own model of the truth, per disk, competing with the governor. One audit inside ontoref keeps the authority singular; scattering it recreates the fragmentation it was meant to prevent.</li><li><strong>Abolish content-bearing carriers entirely — forbid CLAUDE.md/hooks from surfacing protocol knowledge at all.</strong><em>rejected:</em> Collapses formalization-vs-adoption toward formalization: carriers are how the protocol reaches a session before any command runs. The synthesis keeps the conduit and empties its payload, rather than removing the reach.</li></ul>
<h2>Anti-patterns</h2>
<ul><li><strong>Answering Where the Reader Stands</strong> — Governed protocol knowledge is copied into a carrier the reader already looks at — a project or global CLAUDE.md section, a MEMORY.md entry, a hook body — so a session finds it inline without asking. The copy has no witness binding it to the governed surface; the first edit to one side that the other misses is silent, undetectable divergence, and the carrier now confidently serves a stale truth.</li><li><strong>Deputies With Their Own Badge</strong> — To stop carriers drifting, a verifier is installed next to each carrier — per repo, per machine — each encoding its own model of what the truth should be. The remedy fragments the very authority it was meant to protect: now there are many little verifiers scattered across disks, each a candidate second source of truth, none of them the governor. Enforcement itself becomes the thing that drifts.</li><li><strong>Trusting the Photocopy</strong> — A session treats a carrier's inline copy as authoritative and never asks ontoref, so it cannot tell whether what it read is current. Drift becomes invisible not because it is absent but because nothing ever compares the copy to the source. The carrier was meant to point at the governor; instead it stands in for it.</li></ul>
<h2>Related ADRs</h2>
<p><a href="/adr/073">ADR-073</a> · <a href="/adr/028">ADR-028</a> · <a href="/adr/046">ADR-046</a> · <a href="/adr/062">ADR-062</a> · <a href="/adr/018">ADR-018</a></p>

View file

@ -0,0 +1,12 @@
{
id = "adr-074",
title = "Context-provision is governed — ontoref is the single source of truth about the protocol; carriers invoke it live and hold no protocol content, pointers, or verifiers of their own",
slug = "074",
excerpt = "",
tags = ["adr", "accepted"],
page_route = "/adr/074",
graph = {
implements = [],
related_to = ["adr-073", "adr-028", "adr-046", "adr-062", "adr-018"],
},
}

View file

@ -1,6 +1,6 @@
# AUTO-GENERATED — do not edit manually
# Scope: blog/en
# Updated: 2026-07-17T05:56:25Z
# Updated: 2026-07-19T04:50:00Z
# Regen: nu scripts/content/generate-ncl-index.nu
std.array.flatten [

View file

@ -1,6 +1,6 @@
# AUTO-GENERATED — do not edit manually
# Scope: blog/en/engineering
# Updated: 2026-07-17T05:56:25Z
# Updated: 2026-07-19T04:50:00Z
# Regen: nu scripts/content/generate-ncl-index.nu
[

View file

@ -1,9 +1,10 @@
# AUTO-GENERATED — do not edit manually
# Scope: blog/en/ontoref
# Updated: 2026-07-17T05:56:25Z
# Updated: 2026-07-19T04:50:00Z
# Regen: nu scripts/content/generate-ncl-index.nu
[
import "./a-harness-without-a-subject.ncl",
import "./a-rule-without-a-trigger-is-a-sign.ncl",
import "./ai-knowledge-tool-who-keeps-it-alive.ncl",
import "./context-declared-not-filled.ncl",

View file

@ -0,0 +1,165 @@
---
# Post metadata
id: "a-harness-without-a-subject"
title: "A harness without a subject: what harness engineering doesn't name yet"
slug: "a-harness-without-a-subject"
subtitle: "The AI harness is the word of the year: the five layers that wrap a model so it can do real work. But a minimal harness is a hundred lines, every vendor now ships one, and the advantage is moving one layer down — to the subject the harness consults."
excerpt: "Harness engineering just named itself as a discipline: documentation as code, architectural constraints, layered verification, periodic consistency audits. All four pillars exist — in flat markdown, held up by manual discipline. This post concedes the whole argument (“the model is commodity, the harness is the advantage”) and adds the next step: the harness governs the verb — how a change is made — and names no subject to check it against. A verb without a subject cannot drift, because drift is precisely a subject diverging from its own declared self."
# Publication info
author: "Jesús Pérez"
date: "2026-07-19"
published: true
featured: true
# Categorization
category: "ontoref"
tags: ["ontoref", "harness", "harness-engineering", "agents", "drift", "developer"]
# Display
sort_order: 9
# Image (card thumbnail + in-post featured)
thumbnail: "/images/ontoref-harness-without-a-subject.webp"
image_url: "/images/ontoref-harness-without-a-subject.webp"
og_image: "/images/og/ontoref-harness-without-a-subject.png"
---
<picture>
<source srcset="/images/ontoref-harness-without-a-subject.avif" type="image/avif">
<source srcset="/images/ontoref-harness-without-a-subject.webp" type="image/webp">
<img src="/images/ontoref-harness-without-a-subject.webp" alt="A harness without a subject: what harness engineering doesn't name yet" width="1536" height="1024" loading="lazy">
</picture>
There is a term going around — the *AI harness* — doing for 2026 what *prompt
engineering* did for 2022 and *context engineering* for 2025: naming the place
where the outcome is actually decided. The definition settling in
([webreactiva's summary](https://www.webreactiva.com/blog/ai-harness) is a good
one) is clean: the harness is **everything that wraps the model so it can do
real work** — tools, system prompt, context management, permissions, and
verification loops. The model is the engine; the harness is the whole vehicle.
And the numbers behind the thesis are serious: the same model going from 52.8%
to 66.5% on a benchmark with only the harness changed; another jumping from
6.7% to 68.3% by changing nothing but the edit format; LangChain measuring
1020 point jumps on tau2-bench just by adding model-specific harness profiles
([DataCamp's survey](https://www.datacamp.com/blog/agent-harness) collects
them and states the now-canonical equation: **agent = model + harness**).
Hence the slogan: *the model is commodity, the harness is the competitive
advantage*.
Conceded. All of it. The harness matters more than the model, and anyone who
has run a real agent doesn't need to be told twice.
Now the step the slogan doesn't take.
## The harness is becoming commodity too
The argument carries this inside itself: a minimal harness — read file, list
files, edit file — fits in **under a hundred lines**. Every vendor now ships
one, and the open-source ones come by the dozen. When the piece that was the
advantage can be reproduced in an afternoon, the advantage doesn't vanish: **it
moves one layer down**. The question stops being "which harness runs your
agent?" and becomes "what does your harness find when it asks *what is this
project, and what has it decided?*"
For most teams, the honest answer is: a pile of markdown. And that is not a
caricature — it is the component's own definition: when the harness literature
enumerates its "memory and state", what it lists is files, logs, and
summaries. State without a type, prose without a contract.
## Four verbs, no subject
Look at the full vocabulary of the agent era: you **orchestrate** workflows,
wrap them in **guardrails**, run them inside a **harness**, **harvest** their
output back into memory. Four verbs, one grammar — and all four govern the
**act**. Not one names a **subject**: a thing the project has declared itself
to be, that could be checked for still being true.
That omission is not a gap in the vocabulary; it is its function. A verb
without a subject **cannot drift**, because drift is precisely that: a subject
diverging from its own declared self over time. What you cannot name, you
cannot measure. That is why the ecosystem can ship ever-better harnesses and
still have no answer to the one question that matters at agent speed: *is what
you declared six weeks ago still true?* The question is not expressible in the
grammar it chose.
The harness is a **spatial** instrument: it contains the act in the moment it
happens — this context, these permissions, this verification, now. Drift is
**temporal**: the slow separation between what a system says it is and what it
has become, measured in commits and sessions, not in tokens. You cannot build
a fence against time.
## The four pillars, typed
What makes *harness engineering* interesting as a named discipline — Mitchell
Hashimoto named it in February 2026 — is that its four pillars map the problem
exactly. It's just that each pillar is practiced today in its informal form:
flat markdown held up by manual discipline. Each has a typed form, and the
difference between the two is whether coherence is **hoped for** or
**checked**:
| Harness engineering pillar | Informal form | Typed form in <a href="/about" class="brand-site">ontoref</a> |
|---|---|---|
| Documentation as code | `AGENTS.md` — prose the agent reads | An ontology queryable as data: `describe` answers *what is this, what can I do, what is the impact of touching X* |
| Architectural constraints | Conventions stated in text | Hard/Soft constraints on every ADR, evaluated by `validate-adrs`: a violation **fails CI** |
| Layered verification | Hooks and human review | Structural gates + a signed witness: every act that mutates the model emits a witness; every mutation is the deposit of a witnessed act |
| Garbage collection | Periodic consistency audits | Drift as a first-class value: an observer compares declaration against artifact and **proposes** the fix — never applies it silently |
The fourth row is the most revealing. "Collecting garbage" every so often is
admitting the system drifts and resigning yourself to sweeping up. Treating
drift as an observable value — something a process detects, names, and turns
into a reviewable mutation proposal — is refusing to let the gap between map
and territory stay invisible while it grows.
## Your harness doesn't compete with this — it consults it
Here is the division of roles, stated without ambiguity: **<a href="/about" class="brand-site">ontoref</a> is not a
harness and does not pretend to be one**. It doesn't own the tool loop, it
executes nothing on the model's behalf, it will not orchestrate your agents
better. It is the layer the harness finds when it asks: the **subject** — a
typed model of the project: its decisions with their constraints, its state
with its transition conditions, its tensions held rather than collapsed — that
any harness can query as data and check every change against.
The recent taxonomy distinguishes three layers — framework (LangChain,
CrewAI), runtime (LangGraph, Temporal), and harness (Deep Agents, the agent
SDKs) — and <a href="/about" class="brand-site">ontoref</a> sits in none of the three: it is what all three consult.
There is also a hard reason it must live outside, and the literature itself
supplies it when it admits its open tension: results become **tightly coupled
to the specific harness profile**, and switching harness or model is costly.
If the project's knowledge lives inside that profile, changing vehicles means
losing it — you are hostage to your harness. A subject declared outside any
harness is portable across all of them: accountable to its model, hostage to
none.
The relation is invocation, not replication: the harness (Claude Code, or any
other) carries a live pointer to the governed surface; it never copies the
content, because a copy is the first drift. And the protocol eats its own
cooking: <a href="/about" class="brand-site">ontoref</a> describes itself with its own protocol — its ontology, its
ADRs, and its reflection layer are <a href="/about" class="brand-site">ontoref</a> consuming ontoref. The subject it
asks you to declare is the one it maintains for itself, and it fails its own CI
when that stops being true.
## The honest boundary
Keep your harness. Improve it: the five layers are real and so are the
numbers. But when the freshly named discipline tells you the advantage lives
in the environment rather than the model, ask it the next question: *and what
does your environment check the project against, to know it is still what it
declared itself to be?* If the answer is a prose file nobody can query as data
or validate as a contract, you have an excellent vehicle headed to a
destination you never declared. At agent speed, a system with no declared
subject drifts faster than any human can watch.
The answer to that question is already stated — it is the set of phrases
<a href="/about" class="brand-site">ontoref</a> uses to define itself, rotating on [its own front page](/):
> [What you leave stated, someone will be able to verify.](/)
> [Knowing what you are is cheap. Knowing you still are is not — that must be
> verified.](/)
> [A project with no verifiable reason for being isn't alive: it's being
> remembered.](/)
What you only leave inside the harness, no one will be able to verify.

View file

@ -0,0 +1,23 @@
# Generated from a-harness-without-a-subject.md
# Schema: ../../../../../../../../../../jesusperezlorenzo/.local/share/rustelo/nickel/content/metadata/post_metadata.ncl
# Regenerate: nu scripts/content/md-to-ncl.nu --force
let schema = import "../../../../../../../../../../jesusperezlorenzo/.local/share/rustelo/nickel/content/metadata/post_metadata.ncl" in
schema.make_post {
id = "a-harness-without-a-subject",
title = "A harness without a subject: what harness engineering doesn't name yet",
slug = "a-harness-without-a-subject",
subtitle = "The AI harness is the word of the year: the five layers that wrap a model so it can do real work. But a minimal harness is a hundred lines, every vendor now ships one, and the advantage is moving one layer down — to the subject the harness consults.",
excerpt = "Harness engineering just named itself as a discipline: documentation as code, architectural constraints, layered verification, periodic consistency audits. All four pillars exist — in flat markdown, held up by manual discipline. This post concedes the whole argument (“the model is commodity, the harness is the advantage”) and adds the next step: the harness governs the verb — how a change is made — and names no subject to check it against. A verb without a subject cannot drift, because drift is precisely a subject diverging from its own declared self.",
date = "2026-07-19",
featured = true,
category = "ontoref",
tags = ["ontoref", "harness", "harness-engineering", "agents", "drift", "developer"],
sort_order = 9,
image_url = "/images/ontoref-harness-without-a-subject.webp",
graph = {
implements = ["witness-as-axis-seam", "drift-observation", "describe-query-layer", "self-describing"],
related_to = ["trust-is-an-output-not-an-input", "context-declared-not-filled"],
},
}

View file

@ -1,6 +1,6 @@
# AUTO-GENERATED — do not edit manually
# Scope: blog/es
# Updated: 2026-07-17T05:56:25Z
# Updated: 2026-07-19T04:50:00Z
# Regen: nu scripts/content/generate-ncl-index.nu
std.array.flatten [

View file

@ -1,6 +1,6 @@
# AUTO-GENERATED — do not edit manually
# Scope: blog/es/engineering
# Updated: 2026-07-17T05:56:25Z
# Updated: 2026-07-19T04:50:00Z
# Regen: nu scripts/content/generate-ncl-index.nu
[

View file

@ -1,9 +1,10 @@
# AUTO-GENERATED — do not edit manually
# Scope: blog/es/ontoref
# Updated: 2026-07-17T05:56:25Z
# Updated: 2026-07-19T04:50:00Z
# Regen: nu scripts/content/generate-ncl-index.nu
[
import "./a-harness-without-a-subject.ncl",
import "./a-rule-without-a-trigger-is-a-sign.ncl",
import "./ai-knowledge-tool-who-keeps-it-alive.ncl",
import "./context-declared-not-filled.ncl",

View file

@ -0,0 +1,163 @@
---
# Post metadata
id: "a-harness-without-a-subject"
title: "Un arnés sin sujeto: lo que el «harness engineering» aún no nombra"
slug: "un-arnes-sin-sujeto"
subtitle: "El arnés («harness») es la palabra del año: las cinco capas que envuelven al modelo para que haga trabajo real. Pero un arnés mínimo son cien líneas, cada proveedor ya envía el suyo, y la ventaja se está desplazando una capa hacia abajo: hacia el sujeto que el arnés consulta."
excerpt: "El «harness engineering» acaba de nombrarse como disciplina: documentación como código, restricciones arquitectónicas, verificación por capas, auditorías periódicas. Los cuatro pilares existen — en markdown plano, sostenidos por disciplina manual. Este post concede el argumento entero («el modelo es commodity, el arnés es la ventaja») y le añade el paso siguiente: el arnés gobierna el verbo — cómo se hace un cambio — y no nombra ningún sujeto contra el que comprobarlo. Un verbo sin sujeto no puede derivar, porque la deriva es exactamente un sujeto divergiendo de su propio yo declarado."
# Publication info
author: "Jesús Pérez"
date: "2026-07-19"
published: true
featured: true
# Categorization
category: "ontoref"
tags: ["ontoref", "harness", "harness-engineering", "agentes", "drift", "developer"]
# Display
sort_order: 9
# Image (card thumbnail + in-post featured)
thumbnail: "/images/ontoref-harness-without-a-subject-es.webp"
image_url: "/images/ontoref-harness-without-a-subject-es.webp"
og_image: "/images/og/ontoref-harness-without-a-subject-es.png"
---
<picture>
<source srcset="/images/ontoref-harness-without-a-subject-es.avif" type="image/avif">
<source srcset="/images/ontoref-harness-without-a-subject-es.webp" type="image/webp">
<img src="/images/ontoref-harness-without-a-subject-es.webp" alt="Un arnés sin sujeto: lo que el «harness engineering» aún no nombra" width="1536" height="1024" loading="lazy">
</picture>
Circula un término — «AI harness», el arnés — que está haciendo por 2026 lo que
*prompt engineering* hizo por 2022 y *context engineering* por 2025: darle nombre
a donde se decide de verdad el resultado. La definición que se está asentando
([webreactiva la resume bien](https://www.webreactiva.com/blog/ai-harness)) es
limpia: el arnés es **todo lo que envuelve al modelo para que haga trabajo real**
— herramientas, system prompt, gestión de contexto, permisos y bucles de
verificación. El modelo es el motor; el arnés, el vehículo completo. Y los números
que acompañan la tesis son serios: el mismo modelo pasando de 52,8 % a 66,5 % en
un benchmark cambiando solo el arnés; otro saltando de 6,7 % a 68,3 % cambiando
únicamente el formato de edición; LangChain midiendo saltos de 1020 puntos en
tau2-bench con solo añadir perfiles de arnés específicos por modelo
([el repaso de DataCamp](https://www.datacamp.com/es/blog/agent-harness) los
recoge y deja la ecuación en canon: **agente = modelo + arnés**). De ahí la
consigna: *el modelo es commodity, el arnés es la ventaja competitiva*.
Concedido. Entero. El arnés importa más que el modelo, y quien lo haya
comprobado con un agente real no necesita que se lo repitan.
Ahora el paso que la consigna no da.
## El arnés también se está volviendo commodity
El propio argumento lo trae dentro: un arnés mínimo — leer fichero, listar,
editar — cabe en **menos de cien líneas**. Cada proveedor envía hoy el suyo, y
los de código abierto se cuentan por docenas. Cuando la pieza que era ventaja se
puede reproducir en una tarde, la ventaja no desaparece: **se desplaza una capa
hacia abajo**. La pregunta deja de ser «¿qué arnés ejecuta a tu agente?» y pasa a
ser «¿qué encuentra tu arnés cuando pregunta *qué es este proyecto y qué ha
decidido*?».
Para la mayoría de los equipos, la respuesta honesta es: un montón de markdown.
Y no es una caricatura — es la definición del propio componente: cuando la
literatura del arnés enumera su «memoria y estado», lo que lista es ficheros,
logs y resúmenes. Estado sin tipo, prosa sin contrato.
## Cuatro verbos, ningún sujeto
Mira el vocabulario completo de la era de agentes: **orquestas** flujos, los
envuelves en **guardarraíles**, los ejecutas dentro de un **arnés**, **cosechas**
su salida de vuelta a memoria. Cuatro verbos, una gramática — y los cuatro
gobiernan el **acto**. Ninguno nombra un **sujeto**: algo que el proyecto haya
declarado ser, y que pueda comprobarse que sigue siendo cierto.
Esa omisión no es un hueco del vocabulario; es su función. Un verbo sin sujeto
**no puede derivar**, porque la deriva es exactamente eso: un sujeto divergiendo
de su propio yo declarado a lo largo del tiempo. Lo que no puedes nombrar, no lo
puedes medir. Por eso el ecosistema puede enviar arneses cada vez mejores y
seguir sin respuesta para la única pregunta que importa a velocidad de agente:
*¿sigue siendo verdad lo que declaraste hace seis semanas?* La pregunta no es
expresable en la gramática elegida.
El arnés es un instrumento **espacial**: contiene el acto en el momento en que
ocurre — este contexto, estos permisos, esta verificación, ahora. La deriva es
**temporal**: la separación lenta entre lo que un sistema dice ser y aquello en
lo que se ha convertido, medida en commits y sesiones, no en tokens. No se puede
levantar una valla contra el tiempo.
## Los cuatro pilares, tipados
Lo interesante del «harness engineering» como disciplina nombrada — Mitchell
Hashimoto le puso nombre en febrero de 2026 — es que sus cuatro pilares son un
mapa exacto del problema. Solo que cada pilar se practica hoy en su forma
informal: markdown plano sostenido por disciplina manual. Cada uno tiene una
forma tipada, y la diferencia entre ambas es si la coherencia **se espera** o
**se comprueba**:
| Pilar del «harness engineering» | Forma informal | Forma tipada en <a href="/about" class="brand-site">ontoref</a> |
|---|---|---|
| Documentación como código | `AGENTS.md` — prosa que el agente lee | Ontología consultable como datos: `describe` responde *qué es esto, qué puedo hacer, qué impacto tiene tocar X* |
| Restricciones arquitectónicas | Convenciones enunciadas en texto | Restricciones Hard/Soft de cada ADR, evaluadas por `validate-adrs`: una violación **rompe CI** |
| Verificación por capas | Hooks y revisión humana | Gates estructurales + testigo firmado: cada acto que muta el modelo emite un testigo; cada mutación es el depósito de un acto testificado |
| Garbage collection | Auditorías periódicas de consistencia | La deriva como valor de primera clase: un observador compara declaración contra artefacto y **propone** la corrección — nunca la aplica en silencio |
La cuarta fila es la más reveladora. «Recolectar basura» cada cierto tiempo es
admitir que el sistema deriva y resignarse a barrerlo. Tratar la deriva como un
valor observable — algo que un proceso detecta, nombra y convierte en propuesta
de mutación revisable — es negarse a que la separación entre mapa y territorio
sea invisible mientras crece.
## Tu arnés no compite con esto: lo consulta
Aquí está el reparto de papeles, y conviene decirlo sin ambigüedad: **<a href="/about" class="brand-site">ontoref</a> no
es un arnés y no pretende serlo**. No posee el bucle de herramientas, no ejecuta
nada por el modelo, no orquestará mejor a tus agentes. Es la capa que el arnés
encuentra cuando pregunta: el **sujeto** — un modelo tipado del proyecto: sus
decisiones con sus restricciones, su estado con sus condiciones de transición, sus
tensiones sin colapsar — que cualquier arnés puede consultar como datos y contra
el que cada cambio puede comprobarse.
La taxonomía reciente distingue tres capas — «framework» (LangChain, CrewAI),
«runtime» (LangGraph, Temporal) y arnés (Deep Agents, los SDK de agentes) — y
<a href="/about" class="brand-site">ontoref</a> no está en ninguna de las tres: es lo que las tres consultan. Hay además
una razón de peso para que viva fuera, y la aporta la propia literatura cuando
admite su tensión abierta: los resultados quedan **acoplados al perfil concreto
del arnés**, y cambiar de arnés o de modelo sale caro. Si el conocimiento del
proyecto vive dentro de ese perfil, cambiar de vehículo es perderlo — eres rehén
de tu arnés. Un sujeto declarado fuera de cualquier arnés es portable entre
todos ellos: responsable ante su modelo, rehén de ninguno.
La relación es de invocación, no de réplica: el arnés (Claude Code, el que sea)
lleva un puntero vivo a la superficie gobernada; no copia su contenido, porque
una copia es la primera deriva. Y el protocolo se aplica a sí mismo el cuento:
el propio <a href="/about" class="brand-site">ontoref</a> se describe con su propio protocolo — su ontología, sus ADRs y
su capa de reflexión son <a href="/about" class="brand-site">ontoref</a> consumiendo ontoref. El sujeto que te pide
declarar es el que él mismo mantiene, y falla su propia CI cuando deja de ser
cierto.
## La frontera honesta
Quédate con tu arnés. Mejóralo: las cinco capas son reales y los números también.
Pero cuando la disciplina que acaba de nombrarse te diga que la ventaja está en
el entorno y no en el modelo, hazle la pregunta siguiente: *¿y contra qué
comprueba tu entorno que el proyecto sigue siendo el que declaró ser?* Si la
respuesta es un fichero de prosa que nadie puede consultar como datos ni validar
como contrato, tienes un vehículo excelente hacia un destino que nunca
declaraste. A velocidad de agente, un sistema sin sujeto declarado deriva más
rápido de lo que ningún humano puede vigilar.
La respuesta a esa pregunta ya está dicha — son las frases con las que <a href="/about" class="brand-site">ontoref</a>
se define a sí mismo, rotando en [su propia portada](/):
> [Lo que dejas dicho, alguien podrá comprobarlo.](/)
> [Saber lo que eres es barato. Saber que sigues siéndolo, no — eso hay que
> comprobarlo.](/)
> [Un proyecto sin razón de ser verificable no está vivo: está siendo
> recordado.](/)
Lo que solo dejas dentro del arnés, nadie podrá comprobarlo.

View file

@ -0,0 +1,23 @@
# Generated from a-harness-without-a-subject.md
# Schema: ../../../../../../../../../../jesusperezlorenzo/.local/share/rustelo/nickel/content/metadata/post_metadata.ncl
# Regenerate: nu scripts/content/md-to-ncl.nu --force
let schema = import "../../../../../../../../../../jesusperezlorenzo/.local/share/rustelo/nickel/content/metadata/post_metadata.ncl" in
schema.make_post {
id = "a-harness-without-a-subject",
title = "Un arnés sin sujeto: lo que el «harness engineering» aún no nombra",
slug = "un-arnes-sin-sujeto",
subtitle = "El arnés («harness») es la palabra del año: las cinco capas que envuelven al modelo para que haga trabajo real. Pero un arnés mínimo son cien líneas, cada proveedor ya envía el suyo, y la ventaja se está desplazando una capa hacia abajo: hacia el sujeto que el arnés consulta.",
excerpt = "El «harness engineering» acaba de nombrarse como disciplina: documentación como código, restricciones arquitectónicas, verificación por capas, auditorías periódicas. Los cuatro pilares existen — en markdown plano, sostenidos por disciplina manual. Este post concede el argumento entero («el modelo es commodity, el arnés es la ventaja») y le añade el paso siguiente: el arnés gobierna el verbo — cómo se hace un cambio — y no nombra ningún sujeto contra el que comprobarlo. Un verbo sin sujeto no puede derivar, porque la deriva es exactamente un sujeto divergiendo de su propio yo declarado.",
date = "2026-07-19",
featured = true,
category = "ontoref",
tags = ["ontoref", "harness", "harness-engineering", "agentes", "drift", "developer"],
sort_order = 9,
image_url = "/images/ontoref-harness-without-a-subject-es.webp",
graph = {
implements = ["witness-as-axis-seam", "drift-observation", "describe-query-layer", "self-describing"],
related_to = ["trust-is-an-output-not-an-input", "context-declared-not-filled"],
},
}

View file

@ -37,13 +37,13 @@
<rect x="290" y="176" width="210" height="50" rx="7" fill="#1b2330" stroke="#1c2530"/>
<text x="302" y="194" class="t" font-size="10.5" fill="#c0ccd8">adrs/</text>
<text x="302" y="208" class="s" font-size="9.5" fill="#8090a4"><tspan class="t" fill="#e8a838">73</tspan> records · <tspan class="t" fill="#e8a838">69</tspan> accepted</text>
<text x="302" y="208" class="s" font-size="9.5" fill="#8090a4"><tspan class="t" fill="#e8a838">74</tspan> records · <tspan class="t" fill="#e8a838">70</tspan> accepted</text>
<text x="302" y="220" class="s" font-size="9.5" fill="#8090a4">typed constraints · warrant · ondaod</text>
<rect x="512" y="176" width="186" height="50" rx="7" fill="#1b2330" stroke="#1c2530"/>
<text x="524" y="194" class="t" font-size="10.5" fill="#c0ccd8">reflection/schemas/</text>
<text x="524" y="208" class="s" font-size="9.5" fill="#8090a4"><tspan class="t" fill="#e8a838">32</tspan> schemas · <tspan class="t" fill="#e8a838">8</tspan> forms</text>
<text x="524" y="220" class="s" font-size="9.5" fill="#8090a4"><tspan class="t" fill="#e8a838">47</tspan> migrations</text>
<text x="524" y="208" class="s" font-size="9.5" fill="#8090a4"><tspan class="t" fill="#e8a838">35</tspan> schemas · <tspan class="t" fill="#e8a838">11</tspan> forms</text>
<text x="524" y="220" class="s" font-size="9.5" fill="#8090a4"><tspan class="t" fill="#e8a838">48</tspan> migrations</text>
<rect x="710" y="176" width="206" height="50" rx="7" fill="#1b2330" stroke="#1c2530"/>
<text x="722" y="194" class="t" font-size="10.5" fill="#c0ccd8">positioning/</text>
@ -70,7 +70,7 @@
<rect x="430" y="286" width="486" height="46" rx="7" fill="#1b2330" stroke="#1c2530"/>
<text x="442" y="305" class="t" font-size="10.5" fill="#c0ccd8">reflection/modules/ · <tspan fill="#e8a838">38</tspan> Nushell modules</text>
<text x="442" y="320" class="s" font-size="9" fill="#8090a4"><tspan class="t" fill="#e8a838">29</tspan> DAG modes · describe · adr · validate · backlog · sync · graph · view · qa · …</text>
<text x="442" y="320" class="s" font-size="9" fill="#8090a4"><tspan class="t" fill="#e8a838">30</tspan> DAG modes · describe · adr · validate · backlog · sync · graph · view · qa · …</text>
<!-- ── BAND 3 · CRATES ───────────────────────────────────────────── -->
<rect x="30" y="362" width="900" height="100" rx="9" fill="#161c24" stroke="#2a3644"/>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB