Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .lycheeignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
^https://www\.ansi\.org/?$
^https://www\.astm\.org/?$
^https://www\.csagroup\.org/?$
^https://www\.fil-idf\.org/?$
^https://www\.iec\.ch/?$

# Local-asset patterns from VitePress era (not used by Astro, but harmless).
Expand Down
5 changes: 5 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export default defineConfig({
{ label: 'PubID Algebra', link: '/concepts/algebra' },
{ label: 'Relationships', link: '/concepts/relationships' },
{ label: 'Identifier Relations', link: '/concepts/identifier-relations' },
{ label: 'Lossless Faces', link: '/concepts/lossless-faces' },
{ label: 'Dual Perspectives', link: '/concepts/dual-perspectives' },
{ label: 'The D= Designator', link: '/concepts/draft-designator' },
{ label: 'Stage Words', link: '/concepts/stage-words' },
{ label: 'The Corpus Contract', link: '/concepts/corpus' },
{ label: 'URN Mapping', link: '/concepts/urn' },
],
},
Expand Down
59 changes: 59 additions & 0 deletions src/content/concepts/corpus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: "The Corpus Contract"
description: "The shared, versioned record every implementation must reproduce."
---

# The Corpus Contract

The corpus is the **single source of truth** for what an identifier
means and how it renders. The Ruby reference produces it; every mirror
consumes it. When the reference and a mirror disagree, the corpus — not
either implementation's memory — decides which row is wrong.

## What a case records

Each case names one document with its typed identity (the attributes,
not a string), and the forms that identity must produce: the human
canonical, the URN, and the serialized hash. Aliases are listed
separately, with the number of render/parse rounds they need to reach
the canonical. A case may also be a **negative** — an input that every
implementation must reject.

Around the cases sit the gates that keep the record honest:

- a **schema** the cases are validated against, so a missing field or a
wrong type fails before a human reads the diff;
- a **provenance manifest** pinning the reference commit, the grammar
commit and every payload's checksum — a changed payload without a
provenance receipt is treated as tampering;
- a **ledger** of known-pending cases, each with a reason and a
reference, and an alarm when a pending case starts passing;
- a **quarantine class** for rows the reference itself cannot
reproduce (see [Lossless Faces](/concepts/lossless-faces)), counted
so it can neither grow silently nor be forgotten.

## The three-layer rhythm

A change travels in one direction, with a receipt at every step:

1. the **reference** changes and its corpus is regenerated, with
provenance;
2. the **mirror** pins the new corpus revision and re-derives its
ledger from a fresh run — never from the previous ledger;
3. the **package** is released and downstream consumers pin the
release.

Skipping a receipt is the failure mode the contract exists to prevent:
a mirror that quietly kept an old corpus, or a ledger that recorded a
count instead of the failing identifiers, looks green while testing
something else.

## Why it is the contract

Because it is shared and executable. A doctrine ruling is not real
until a corpus row records its consequence; a grammar change is not
landed until the corpus regenerates byte-identically or the diff is
explained; a mirror is not at parity until it reproduces every row
except the documented, referenced exceptions. The corpus is how the
project turns prose rulings into something CI can refuse to merge
against.
46 changes: 46 additions & 0 deletions src/content/concepts/draft-designator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "The D= Designator"
description: "How IEEE project drafts spell their stage."
---

# The D= Designator

An IEEE draft has two kinds of information that are easy to conflate:
the **project** it belongs to and the **stage** it has reached. The
project is spelled with a leading `P` (`P42010`), and the stage is
spelled with the `D=` designator (`D=CD`, `D=DIS`, `D=FDIS`).

## What the designator carries

- The stage token itself, in the organization's own vocabulary:
`D=CD`, `D=DIS`, `D=FDIS`.
- An iteration, glued to the stage with a dot: `D=CD.2` is the second
committee-draft iteration. The iteration is part of the designator,
not a separate suffix.
- A year, joined in IEEE's own format. The canonical face is a dash:
`P42010/D=CD-2020`. A colon spelling is accepted as an alias and
canonicalizes to the dash face.

## What it does not mean

- `P` is not "proposed". It marks the project; it is identity-bearing
and is never added or dropped by a renderer.
- A doubled `D` (`DDIS`) is not a stage. It is an alias spelling of
`DIS`; the canonical face is the single designator `D=DIS`.
- The designator is not a free-form suffix. A stage word outside the
vocabulary is a different construct, not a D= tail.

## Two faces, one identity

When a joint publication is led by ISO, the rendered face uses the
stage word in the ISO position (`IEEE/ISO/IEC CD 42010:2020`); the
IEEE face uses the designator. Both express the same stage, and both
re-parse to the same identity. Which face a catalogue prints follows
the perspective of the catalogue (see [Dual
Perspectives](/concepts/dual-perspectives)), not a preference of the
library.

The invariant is round-tripping: a designator face must re-parse to the
same stage, iteration and year it printed. If it cannot, it is a
reference defect recorded in the corpus, not a spelling to normalize
away.
45 changes: 45 additions & 0 deletions src/content/concepts/dual-perspectives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: "Dual Perspectives"
description: "When two organizations assign one document two identifiers."
---

# Dual Perspectives

Standards are often published jointly. `ISO/IEEE 11073-10101` and
`IEEE/ISO 11073-10101` name the same document, and the difference is
not a spelling preference: each organization assigns the identifier
from **its own perspective**, with its own lead publisher and its own
catalogue key. `ISO|IDF` and `IDF|ISO` are the same situation.

## The rule

Neither form is a canonicalization of the other. There is no "true"
order of the publishers, no hint that says which one a caller meant,
and no routing that silently converts one into the other. A dual
perspective is identity-bearing: the printed order is the assignment.

This matters because the two organizations file the document under
different keys. A lookup performed against the ISO catalogue must use
the ISO-perspective identifier; a lookup against the IEEE catalogue
must use the IEEE-perspective one. Collapsing them would make one
catalogue answer for the other's key.

## What follows for consumers

- Parse the printed face and record which perspective it is.
- Route to the catalogue that owns that perspective — the printed
perspective *is* the routing information.
- If that catalogue does not hold the document, fall through to the
other publishers the parsed identifier names; do not rewrite the
identifier to force a match.

## What does not exist

- No canonical form that both sides normalize to.
- No "hint" parameter that selects a perspective out of band.
- No automatic conversion between perspectives during parse, render,
or URN generation.

The two faces are peers. A library that makes one subordinate to the
other has quietly decided an ownership question that belongs to the
organizations.
48 changes: 48 additions & 0 deletions src/content/concepts/lossless-faces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "Lossless Faces"
description: "Why a printed identifier keeps the form it was printed in."
---

# Lossless Faces

A publication identifier is not a string that happens to spell a
document — the spelling *is* part of the identity. A catalogue prints
`IEEE P61850-9-3/D=FDIS-2016`, a citation prints
`IEC/IEEE P61850-9-3_FDIS, February 2016`, and both name the same
project draft. PubID treats each printed form as a **face**: it parses
the spelling it was given, records what each token meant, and renders a
canonical face that carries the same information.

## The rule

A transformation may not silently discard a token the source spelled.
When two spellings name one document, they are **aliases**, and the
canonical face is the one the reference's own renderer produces. An
alias is allowed to be lossy in its *surface* only if re-parsing the
canonical recovers the identity — which is what the corpus checks.

This is why aliases can need more than one round trip. A spelling can
normalize into another alias rather than straight into the canonical;
the corpus records how many render/parse rounds each alias needs (its
`rounds` value), and both the exporter and the mirror walk them.

## When the canonical cannot re-parse

Some reference renders are themselves lossy — a narrative tail or a
joint render can produce a human face that the reference's own grammar
rejects. Those rows are not "bad data" to be re-spelled; they are
recorded as a **quarantine class** (`render_unreparseable`) with the
exact count, so the defect is visible and a fix trips the ledger. A
quarantine is a documented reference defect, never a silent skip.

## The tests that keep it honest

- Every corpus case's canonical must re-parse to itself.
- Every alias must reach the canonical within its recorded rounds.
- Every quarantine row must stay counted at the same number.
- A run that changes any of these numbers is a corpus round, with a
provenance receipt and an explanation — not an edit to make CI green.

The faces are lossless in the only sense that matters: no consumer has
to guess which document was meant, and no authority's spelling is
overwritten by another's.
48 changes: 48 additions & 0 deletions src/content/concepts/stage-words.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "Stage Words"
description: "How the word \"Draft\" and the stage vocabulary behave in a rendered identifier."
---

# Stage Words

A rendered identifier says how far a document has progressed. The
vocabulary is organization-specific — `CD`, `DIS`, `FDIS` for ISO/IEC;
`Draft`, `Unapproved`, `Redline` for IEEE — and the same identity can
have a stage word in one face and a designator in another.

## The draft word

When a rendered face says a document is a draft, it says **`Draft`
alone**. It does not prepend the type (`Draft Standard`), repeat the
status (`Draft Unapproved Draft`), or invent a stage.

The exception is a **project-stage draft**: when the draft belongs to a
project whose stage is itself the identity (an IEEE `P` project), the
type word is suppressed rather than embellished. A date-only draft
whose parsed designator carries no version takes the fabricated first
stage (`D1`); a designator that names an ISO stage token takes that
stage.

## Stage words and designators

When a joint publication is led by ISO, the stage prints as a word in
the ISO position. When IEEE leads, the stage prints as the `D=`
designator (see [The D= Designator](/concepts/draft-designator)). The
underlying stage is the same; the face follows the perspective.

The stage also has a **harmonized code** — the numeric ladder ISO and
IEC share — which is what queries use when they ask "show me everything
at the enquiry stage" rather than "show me this token". Typed stages
carry both: the abbreviation the source printed and the harmonized
code(s) it maps to. A query by code must never miss a document because
its face printed a word instead.

## What stage words must not do

- They must not be dropped to make a render parseable; a face that
cannot re-parse is a corpus defect, not a licence to elide the stage.
- They must not be guessed from context when the source did not spell
them. A missing stage is missing; the library does not infer one from
a year or a project number.
- They must not be conjugated across faces. `FDIS` stays `FDIS`; the
alias `DFDIS` canonicalizes to it rather than becoming a new stage.
2 changes: 1 addition & 1 deletion src/pages/concepts/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import BaseLayout from '~/layouts/BaseLayout.astro'
import { getCollection } from 'astro:content'

const conceptOrder = ['anatomy', 'metaschema', 'components', 'algebra', 'relationships', 'identifier-relations', 'urn']
const conceptOrder = ['anatomy', 'metaschema', 'components', 'algebra', 'relationships', 'identifier-relations', 'lossless-faces', 'dual-perspectives', 'draft-designator', 'stage-words', 'corpus', 'urn']
const items = (await getCollection('concepts')).sort((a, b) =>
conceptOrder.indexOf(a.id) - conceptOrder.indexOf(b.id))
---
Expand Down
Loading