GoogleCloudPlatform / GoogleCloudPlatform/open-knowledge-format
Proposal: register query-time semantics for `supersedes` and `contested_by` on the #16 carrier
- Dominant language
- HTML
- Stars
- 467
- Forks
- 35
- Avg merge
- 9m
- Merged PRs (30d)
- 1
Description
#16 gives relationships a carrier: `to`, `type`, `direction`, and the §5.3 trust shapes one level down. It deliberately leaves `type` producer-defined and unconstrained, and that is the right call for the carrier. This proposes the other half for two specific types, because a consumer reading `type: supersedes` still cannot tell whether to drop the target from an in-force query.
Two lifecycle edges, each with its query-time semantics registered beside it. They differ on the axis that drives consumer behaviour: **directionality**.
*Revised 2026-09-14 from review by @ajdelaguila and @3a3; the comment thread carries the derivation.*
## The two types
| | `supersedes` | `contradicts` |
|---|---|---|
| Symmetry | **Asymmetric.** The carrying concept replaces the referenced one. | **Semantically symmetric, stored one-sided.** Written on the asserting concept only; the reverse is derived. |
| Direction | Backward in time, to the concept being replaced. | Outbound from the asserting concept. `direction` reads as it does for any asymmetric type. |
| Query-time | **Exclude, once affirmed.** Drop the superseded concept from in-force retrieval; retain it in the bundle for lineage. | **Surface both.** Never resolve the edge as a verdict, collapse "contested" into "false", or drop a side without human or higher-signal input. |
| Encodes a verdict? | Once affirmed, yes. | **No.** Records that a dispute exists, never who is right. |
| Inverse emitted? | **No.** Resolution keys on the forward edge only. An inverse spelling gives a producer two ways to state one relationship and a consumer two places to look, which is how a reversed edge survives review (@jpavley shipped one inverted, `GoogleCloudPlatform/knowledge-catalog#148`). | **Derived, never written.** See the naming section. |
| Dangling target | Not an error. If the target is absent, the carrying concept is current. | Not an error. |
```yaml
relationships:
- to: remember://lesson/lesson-corpus-est-000
type: supersedes
generated: { by: remember/0.2, at: 2026-09-12T09:00:00Z }
```
Targets are stable concept ids per `GoogleCloudPlatform/knowledge-catalog#120`, not paths, so a file move cannot reattach an edge to the wrong incumbent. @pixie79 reports the same conclusion from the other end on #16: deriving a concept's path from its key guessed wrong for 43 links on a real render, each a valid-looking path to a file that did not exist.
## Naming: one stored key, one derived view
- **`contradicts`** is the stored key, on the asserting concept, one side only.
- **`contradicted_by`** is the derived reverse view, computed at read time and **never written to frontmatter**.
- **`contested_by`** is a deprecated spelling. Consumers SHOULD read it as `contradicts`.
Under one-sided storage `contested_by` is inverted: the asserter would carry a key saying it *is* contested, the reverse of what it asserts. This registration, @inkxel's implementation and Data Olympus's are then one vocabulary rather than three. We ship `contested_by` today and have the migration to do.
A mirror write, with both records carrying the edge, is a two-write that can half-apply, and a one-sided contest is a different condition from the dangling target ruled benign above. Derivation costs a consumer nothing that it is not already paying: a reverse index over stable ids is what resolves targets at all.
## Three rungs, and only the third authorizes exclusion
With `verified` and `generated` available on the edge it is tempting to read edge provenance as authorization. It is not, and neither is the middle rung.
1. **Edge provenance** - `generated` / `verified` on the relationship. Says who asserted the edge. Authorizes nothing. @pixie79 confirms the carrier claims no more: "our edges carry `kind`, `provenance` and `confidence`, and none of that is an affirmation."
2. **Source eligibility** - the asserting concept is itself in force. Data Olympus ships this: an in-force query excludes the target of a `supersedes` edge whose source is in force, an edge asserted from an inbox document excludes nothing whatever status it claims, and a dangling edge excludes nothing because the join needs both ends to resolve.
3. **Attributed resolution** - someone affirmed *this* retirement, recorded as an appended, attributed event.
**Exclusion follows affirmation.** An automated writer may propose a supersession; the incumbent keeps governing until the resolution event is recorded. Rung 2 is weaker than rung 3 and is named rather than collapsed into it, because it is what a real implementation runs today: source eligibility says the edge comes from something currently governing, it does not say anyone affirmed this particular retirement. That distinction is what a consumer needs when 18,803 of 19,349 edges are derived.
**Exclusion requires both.** A present forward edge **and** a readable affirmation, together. An affirmation surviving in `log.md` after the edge is removed from the successor, or after the successor leaves the bundle, is incomplete evidence, so the incumbent stays in force.
## The resolution event
**Where it lives: the bundle-root `log.md`.** Nearest-ancestor would make the in-force answer depend on where a concept sits in the tree, so a file move could change which log governs its retirement, contradicting the stable-id rule above. **A resolution entry in a non-root `log.md` is not an affirmation**; §9 permits the file at any level, and an entry elsewhere excludes nothing.
**§9 supplies the file's shape and no preservation duty** (`SPEC.md:535-552`), so the append-only behaviour this registration depends on is the producer's to supply, not the format's. For a bundle assembled in parts that producer is the merging one: it writes root reserved files after merging and carries forward every `log.md` entry it did not write, from the existing file or from its own bookkeeping. @3a3's #26 §3.2 now states this (revised 2026-09-14); their merge becomes read-then-add, keeping each such entry unchanged under its original date heading.
**Entry shape**, in §9's own idiom - its `**Update**` / `**Creation**` / `**Deprecation**` convention, made normative inside this registration rather than in core:
```markdown
## 2026-09-14
* **Supersession**: `remember://lesson/b` supersedes `remember://lesson/a`. Affirmed by `human:1c8998e4`.
* **Dismissal**: contest between `remember://lesson/c` and `remember://lesson/d` dismissed. Affirmed by `human:1c8998e4`.
```
A consumer MUST find four things: the disposition word, both stable concept ids, and the attributing actor in §7's form. The date comes from the enclosing §9 heading.
**Absent, malformed or undecidable evidence fails safe.** If any of the four is missing or unparseable, the entry is not a readable affirmation, so the incumbent stays in force and nothing is excluded. The same answer covers conflicting entries: §9 orders dates newest first and says nothing about order inside a date, so two conflicting entries on one pair under one heading are not decisive. This also answers a real adoption case - @pixie79 reports on #16 that Roteiro populates no author fields and discards a peer's `author` on import. That producer excludes nothing rather than excluding wrongly, and needs no change to stay conformant.
**Where the conformance layer lands: in this registration, not §11.** Core should not carry a clause for a community extension it does not define. A consumer implementing this registration gets a checkable bar; a bundle that ignores the registration is unaffected.
## Three states, read from the bundle alone
No adjudication engine, no edited records, every resolution an append.
1. **Never questioned** - no `contradicts` pointing at it, not the target of any `supersedes`. Absence means no dispute asserted, not proven undisputed.
2. **Contested and open** - the edge exists, both concepts present, no resolution recorded. Surface both.
3. **Contested and settled** - a resolution is recorded, not edited in. The `contradicts` edge is **retained whatever the disposition**, so a settled contest stays distinguishable from one never raised. Drop it and a reader returning later cannot tell a question that was settled from one that was never asked.
A `supersedes` edge with no resolution event yet is a proposal, not a fourth state: both concepts surface and the incumbent stays in force.
Settlement comes two ways.
**Supersession** - the winner gains the `supersedes` edge, the loser's validity window closes, both retain `contradicts`, and exclusion keys on the resolution entry rather than on the closed window. A closed window with no entry is decay, and reading it as affirmation would collapse decay and affirmed supersession into one, which is the silent displacement the invariant forbids.
**Dismissal withdraws the contest, not a concept.** No window closes, both concepts stay in force, and the entry's whole job is moving the pair from contested-and-open to contested-and-settled. A concept examined and judged wrong is #13's `refuted` or a supersession; dismissal is not a third way to retire something, and `contradicts` carries no verdict by design.
## Composition
- **§5.4 `status`** says whether a concept is current; the edge says where current lives. Neither implies the other, and a consumer that understands only one still behaves correctly. §5.4 defines `deprecated` as kept for links and history, but nothing in frontmatter points from a deprecated concept to its replacement, so a consumer filtering out deprecated concepts drops the only breadcrumb at the moment it acts on the signal.
- **§5.3 trust tiers** derive from `verified`, and §10.6 makes verification doc-level and recorded in the bundle, so a concept human-reviewed in June can be contested in July with its tier intact. A tier-filtering consumer surfaces it as human-reviewed and never sees the dispute unless the contest is typed.
- **#13 (`refuted`)** is a trust event on a concept that exists; these are relations between concepts. `contradicts` defers adjudication by design and carries no verdict, so it does not overlap.
- **#15 (`imported`)** composes: an imported relationship reports the upstream's tier without inheriting it.
- **#11 (deletion semantics)** is the presence axis. A superseded concept stays in the bundle; a removed one needs an absence record.
- **#26 (bundles in parts)** carries the merge-side obligation this registration depends on.
**Floor for partial implementation.** A consumer that implements the carrier and not this registration excludes nothing. Under exclusion follows affirmation that is the designed failure direction, not an accident.
## Provenance
Two production implementations, arrived at independently. Ours ships both keys in the *remember* exporter with the sample bundle at https://github.com/andrewcrenshaw/remember-okf-sample-bundle. @ajdelaguila's Data Olympus ships source-gated supersession over concept ids and holds `contradicts` as annotation that never filters, with contest disposition open at knaisoma/data-olympus#241. The two differ on one mechanism: Data Olympus reads a contest-to-supersession count out of git diffs, and we record a resolution event at settlement so the same count is a query. That difference is the argument for rung 3.
The prior text, with the extended composition and a worked sample bundle, was `GoogleCloudPlatform/knowledge-catalog#195`, now retired in favour of this. Its own adjacent-proposals section predicted this carrier: a broader carrier for producer-defined relations can host these two edges without redefining them, because the query-time semantics attach to the relation and not to the syntax.
Glad to take further convergence edits here and then put it up as a PR.
Contributor guide
Research direction
Start with SPEC.md sections 5.3, 5.4, 7, 9, 10.6, and 11, then compare the issue's proposed carrier and resolution rules with the existing specification. Done means the registration is incorporated consistently, including stable-id relationship semantics, bundle-root log affirmations, safe failure behavior, and composition with the referenced issues.
Written by the indexing model from the issue text.
Assessment
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100