sprig and prose docs classic docs
Reference› ADRs› ADR014 Literals

ADR014 Literals

Title: Literals

  • Status: Accepted
  • Decided: D2026-03
  • Recorded: D2026-09-07
  • Follows: ADR013 ExecutableLayer
  • Followed by: ADR018 TemporalDimensions, ADR022 ReceiptBoundary, ADR027 DatesAsNames
  • Affects: Parser, Prose, Title

Context

With the executable layer removed (013), prose was purely descriptive. The March 2026 rewrite was the moment that became concrete: the parser was written by hand, and the shipped ontology (universe, anthology, series, book, chapter, concept) collapsed to universe, concept, dimension, relationship.

A handwritten parser has to decide what a value looks like. The scene language had already answered that question for itself — minLength { 1 }, maxLength { 64 } — in the same block shape prose uses. So the syntax for prose-with-literals was not hypothetical. It existed, it worked, and it had just been archived.

Nothing was asking for literals at the time. This exclusion was preemptive, which is worth stating plainly rather than dressing up as a response to pressure.

Decision

Prose has no literals. Every position in the language holds an identifier or a prose block. No numbers, no quoted strings, no dates, no booleans as values.

describe and title are prose blocks, not string values. Authored text is not a literal. The rule is about values in structural positions — the places a machine would read a field out of.

Consequences

Why preemptively, and not later when something asked. Because the failure mode is erosion, not a decree. A literal in prose is a value that somebody will extract. Once prose is supplying values to running code, prose is participating in execution, and 013 is undone one extracted field at a time — with no decision ever having been made to undo it. The temptation is concrete and named: metadata systems of the kind built at work would happily read a value straight out of a universe, and the author of this record would be tempted to let them.

What this makes impossible. No timelines. No cardinality or counts, which is part of why the proposed expects block is presence-only (011). No versions, no measurements, no dates — which is why these records carry their dates in markdown and the prose ADR nodes do not, and why the temporal-dimensions question is still open. (That record does not have a number yet; it currently exists only as adr TemporalDimensions in universe/adr.prose.)

Where values legitimately live: the receipt side. This is the dividing line, and it is not a loophole — it is the arrangement. Receipts may carry literals. Prose may not. A receipt already holds a file path, a line, a column and a context hash, and it is the designated home for anything else that has to be a value: a URL, a revision, a timestamp.

That line is also why prose no longer constructs documentation. The earlier language had a documentation { document { path { '/docs/items/overview.md' } } } form, attaching external files to a concept — visible in archive/universe/test/fixtures/named-document.prose. Its path and kind are quoted string literals, so the feature could not survive this record. Once receipts existed, documentation linkage moved to the side that is allowed to hold a URL, and prose stopped constructing it at all.

So the test for any proposal is not whether values exist somewhere in the ecosystem. It is which side of that line holds them.

Title's history, and why it is not an exception. title was originally a literal: title { 'this is the title' }, with quotes. The quotes went away when describe-style blocks won, and title became a prose block with a short body. So title did not survive as a string slot; it stopped being one by construction. ADR 010 describes title as "the only string-valued slot in prose," which predates this definition — its ruling stands unchanged, and its watch item (title having been used to hold URL patterns) is this record's erosion, observed in the wild.

What to watch. The erosion does not require the word "literal." Any author-written value landing in a machine-readable slot is the same pressure, whatever it is called. A short prose block that a consumer parses as a value is the same thing as a literal with extra steps, which is precisely what happened to title.

Alternatives considered

Scalar attributes on concepts. A general attribute name { value } form. Rejected. It is the most honest version of the request and the most direct route to extraction, since a named scalar is exactly what a consumer wants to read.

A typed attribute block, quarantined from the rest of the language. Values allowed, but only inside one clearly marked box. Rejected. Quarantine is where erosion starts, not where it stops: the box is a supported feature the moment it ships, and the argument for widening it is always the next specific case.

Dates only, as a narrow exception. Deferred rather than rejected, to the open temporal-dimensions question. Worth recording alongside it: if a timeline is modelled as concepts — an episode inside a season, ordered by follows — then ordering and validity may be expressible with no date literal at all, and only derived values over time (an age that depends on when you ask) would genuinely need one. That would put the remaining case back inside 013's refusal rather than this one's.

Keep the quoted-string form for title. Not so much rejected as overtaken, when describe-style blocks became the way authored text is written. The simplification that removed the quotes is what made title prose.

Evidence

The scene compiler is the strongest evidence, because it shows what the alternative looked like in practice rather than in theory: archive/scenes/test/string-constraints.test.js and its integer counterpart exercise minLength, maxLength, min, max, step and of as values in block syntax. Prose with literals is not a thought experiment; it was a working compiler, and it was archived.

The absence of a forcing case is itself evidence, and it cuts both ways. No universe was blocked at the time, so this record cannot claim to have solved a live problem. What it can claim is that the pressure arrived later and from the predicted direction: title being used to hold URL patterns, ADR metadata wanting dates and numbers, and expects wanting cardinality. Three requests, none of them calling itself a request for literals.