sprig and prose docs classic docs
Reference› ADRs› ADR027 DatesAsNames

ADR027 DatesAsNames

Title: Dates as names

  • Status: Accepted
  • Decided: D2026-09-24
  • Follows: ADR014 Literals, ADR018 TemporalDimensions
  • Followed by: ADR028 RecordFactsInTheUniverse
  • Affects: date

Context

014 refused literals and named what that gives up: "No versions, no measurements, no dates — which is why these records carry their dates in markdown and the prose ADR nodes do not". 018 left the question open and called the arrangement "tolerable, and currently the working arrangement."

It stopped being tolerable when the docs began generating pages from the universe. A generated page can only say what the universe holds. Every other fact in an ADR header already was an edge or could become one; the date could not. It was the fact that kept every header hand-written.

The first answers to reach for were literals in costume. One was frontmatter or templating in the markdown files. The other used a title to hold the date, and it compiles today:

concept Something {
  date CreatedDate {
    title { 2026-08-09 }
  }

  relationships {
    createdOn { Something.CreatedDate }
  }
}

That is the case 014 warned about: "A short prose block that a consumer parses as a value is the same thing as a literal with extra steps." The title exists only to be parsed.

The part of that example worth keeping is everything except the title. A date is a thing people point at. Everyone already knows what a day is, and saying a record was decided on one is a fact about the record, the same kind of fact as its status. What would make it a literal is not a date appearing in the universe. It is something reading a value out of it.

018 had already found this shape, for episodes: "If the timeline is modelled as concepts, Season4Episode7 is an identifier, and a validity interval references identifiers the way every other endpoint does." A calendar day is the same kind of thing as an episode. Nobody had applied that to the calendar.

Decision

A date is a name. It is a node, named for the day it stands for, and things point at it like any other node:

dimension date { }

date D2026-08-09 { }

concept Something {
  relationships {
    createdOn { D2026-08-09 }
  }
}
  • The spelling is D and the day, as in D2026-08-17, or D and the month when the month is all anyone knows, as in D2026-03. The letter is there because the parser drops digits at the start of a name. A day and a month are different nodes, because a month is not a day.
  • Nothing reads the spelling. Not the language, which sees an identifier, and not a consumer. The name means something to a person and nothing to a machine, the same as SP001. This is 010's rule taken one step further: a consumer that may not humanize an identifier may not parse one either.
  • A day is shared. Everything that happened on a day points at the same node, and that is most of what makes a date worth being a node: its page shows what else happened then.
  • Order is a relationship. Sorting the names is fine for finding one. It is not a way to present one date as coming after another. Where succession matters to a reader, a relationship says so, the way follows orders these records.
  • A date node is empty. No describe and no title. A day with something worth saying about it is an event, and an event is a concept that points at its day.

None of this changes the language. The parser already accepts every name above, and it enforces none of these rules, because checking any of them would mean reading the name.

Consequences

What this permits. Any universe can say when something happened, today. These records gain their dates in the universe (028), and each day's page lists what was decided on it.

What it does not permit is a timeline. Nothing can place these nodes on an axis, measure the gap between two of them, or say which came first, because each of those reads a value out of a name. That is the whole of what this gives up compared with a date literal, and it is exactly what 014 refused. A universe that needs order states it. A universe that needs arithmetic over time is asking for behaviour, which 013 refuses.

What it costs. The names read worse than dates. D2026-08-17 is uglier than 17 August 2026, and it stays that way on every page, because a projection renders it verbatim. The letter in front exists only to satisfy the grammar. Nothing checks that a name is a real day, so D2026-02-30 compiles, and nothing will notice, because noticing would mean reading the name. A month and its days are unrelated nodes, so "what happened in August" is not a question the graph answers unless an author relates them.

What to watch. Nobody is going to propose a date literal. Somebody will write a consumer that sorts date names to draw a timeline, or slices the year out of one to group by it, and each will look like a harmless convenience. Either one turns these names into literals with extra steps. The same holds for any name that spells a value: ADR012 is not the number twelve, and SP001 is not one.

Alternatives considered

A date literal. Refused by 014 and not reopened here. This record exists because what a reader needs from a date never depended on it being a value.

A date in a title. The example in the context. It compiles, and the title exists only to be parsed. Rejected, and it is why a date node carries no title.

A date per thing. Each record owning its own date node, as that example does. Two records decided on the same day would never meet. Rejected, because a shared day is most of what a date node is for.

Keep dates in the markdown. The arrangement 014 described. It keeps the value out of prose by keeping it out of the universe, which is what forced every header to be written by hand. Rejected for these records. It is still the right answer for a value about a file rather than about the thing a universe describes: a receipt's timestamp belongs to the receipt.

A bare 2026-08-17 as the name. Not possible today, and not for a principled reason. The scanner skips any character it does not recognise, so the digits are dropped and nothing is left to be a name; a name that only starts with digits, like 3M, silently loses them. Whether a name may begin with a digit is a question about the grammar, and this record does not need it answered, because D2026-08-17 works now. Deferred.

One follows chain through every date. Order stated once, for every date. Not needed until something presents dates as a sequence, and a list of records ordered by number does not. Deferred, and cheap to add later, since the nodes already exist to be connected.

Evidence

The parser accepts all of it today. D2026-08-17, D2026_08_17, D20260817 and D2026-03 are all legal names. A bare 2026-08-09 fails with SP016, because the scanner drops the digits and finds no name at all. A shared node receives the mirrored edge from every record that points at it, so a day really does list what happened on it. The hyphenated form was chosen because it is the spelling a reader already knows, with one letter in front.

These records are the first data set. The twenty-six before this one carry nineteen distinct dates. Five know only a month (013, 014, 019, 020, 021), and four are retrospective, with a second date for when they were written (012, 013, 014, 019). The month-only records settled the rule about granularity: whoever wrote them did not know the day, and a month node says exactly that much and no more.

The first version of this idea counted sortability as a feature. ISO spelling sorts chronologically, so sorted names would have been a timeline for free. That is the reading this record forbids, and it was caught in discussion: the names can be sorted, but they are opaque, so showing that one comes after another takes a relationship.