Rites Protocol

The specification

ritual/1 — the present tense

Status: draft. The estate keeps two tenses already — backlog/1 is the future and shipped/1 is the past — and docs/tenses.md explains why they are shaped differently on purpose. This format is the tense between them: the recurring, witnessed, consequence-bearing act, happening now, on a rhythm a stranger can check.

0 · Why a third tense

An intention decays unless somebody restates it. A fact about the past is sealed and never decays. Neither shape fits a practice: a practice is neither a plan nor a record — it is a rhythm, and its evidence is that the rhythm holds. A repository whose emitters refresh its fragments every day is practising something; today the estate can only see that practice by its side effects. ritual/1 gives the practice a first-class record.

The word is chosen deliberately. Ritual is the oldest coordination technology there is: a prescribed act, performed on a shared calendar, whose performance is legible to others and whose meaning is conferred by a community rather than claimed by the performer. Every clause of that sentence appears below as a validation rule.

1 · The four refusals that outrank everything else here

Agents observe; humans consecrate. A performer is an agent/ id acting for an org/ or person/ who answers — the work layer's own doctrine, inherited verbatim. A consecration.by is a person/ id and nothing else. There is no code path by which an agent approves an observance into consequence, for the same reason there is no argument that makes directoryService write an approved fact: an agent that could consecrate its own observance would make the consent layer decorative.

Unwitnessed practice moves nothing. An observance reaches witnessed only when a party who is neither the performer nor its principal signs it — and signs with a basis of their own. A witness whose basis is a copy of the performer's evidence is the citation loop estate-coherence fails fragments for, rebuilt one level down: one source, counted twice, looking like corroboration. Consecrating an unwitnessed observance is refused outright; consecrate() has no flag, argument or caller that skips the witness.

An observance carries no value and no standing. The money vocabulary is refused at any depth (the holding/1 and reward/1 rule), and so are amount, aurum, gold, reward, points and payout. Standing words — streak, rank, score, level — are refused as derived-only, because standing comes from what others assert about sealed outcomes and never from unilateral activity (the magician doctrine). Accrual against consecrated observances belongs in reward/1, where the caps live; keeping the two formats apart means the coupling has to be written by somebody rather than merely permitted. An observance that could carry its own reward is a slot machine with liturgical vocabulary.

The log is append-only. A correction is a new observance whose supersedes names the old one and whose state is void. Nothing is edited; nothing is deleted. A void that names nothing is a deletion with better manners, and the validator says so.

2 · The shape

A fragment is one subject's calendar: the liturgies it publishes and the observances performed against them.

{
  "contract": "ritual/1",
  "subject": "org/ritualos",
  "generated": "2026-09-01T06:00:00Z",
  "liturgies": [
    {
      "id": "daily-office",
      "title": "The Daily Office",
      "cadence": "daily",
      "rite": ["refresh the fragment", "seal the log", "dispatch to the merge"],
      "published_by": "person/michael",
      "since": "2026-09-01T00:00:00Z"
    }
  ],
  "observances": [
    {
      "id": "obs-2026-09-01-flashyos",
      "liturgy": "daily-office",
      "performer": "agent/flashyos-ci",
      "for": "org/flashyos",
      "at": "2026-09-01T04:23:00Z",
      "recorded": "2026-09-01T04:23:07Z",
      "evidence": "https://github.com/FlashyLabs/flashyos/actions/runs/17284",
      "state": "witnessed",
      "witness": {
        "by": "org/gda-capital",
        "basis": "https://gda.group/.well-known/flashyos-directory.json",
        "at": "2026-09-01T05:00:00Z"
      }
    }
  ]
}

A liturgy

FieldRule
idUnique within the fragment
titleRequired — a name a person recognises
cadenceOne of daily, weekly, monthly, seasonal, once. A closed list, so a new rhythm is a decision
riteA non-empty list of steps. A liturgy with no rite is a name with no practice
published_byA person/ id. Publishing a liturgy asks agents to act, and a person answers for the asking
sinceISO timestamp

An observance

FieldRule
idUnique within the fragment — append-only, a correction is a new observance
liturgyMust name a liturgy in this fragment. An observance of nothing is activity, not practice
performerAn agent/ id. A person's own act is work, recorded in shipped/1; ritual/1 records what agents observe
forAn org/ or person/ id — the principal who answers
at / recordedISO timestamps; recorded may not precede at (the holding/1 lag rule)
evidenceAn https:// URL a stranger can open. Without one the observance is a claim, and this format does not carry claims
stateperformedwitnessedconsecrated, or void. No state is skippable upward, and a performed observance carrying witness or consecration blocks is refused — the ladder is climbed by transition, never by assertion
witnessRequired from witnessed up: { by, basis, at }. by is an org/ or person/ that is neither the performer nor its principal; basis is the witness's own https:// URL
consecrationRequired at consecrated: { by, at }. by is a person/ id
supersedesRequired when void; must name another observance in this log

3 · The transitions

The package (and the vendored copy — they are the same file) exposes the ladder as functions, each of which re-validates the whole fragment and refuses on any problem:

  • observe(fragment, observance) — the only door in. Arrives performed; passing state, witness or consecration throws.
  • witness(fragment, id, { by, basis, at }) — a second party signs. Self-witness throws.
  • consecrate(fragment, id, { by, at }) — a named human confers consequence. A non-person/ id throws; an unwitnessed observance throws.

Each returns a new fragment. Nothing mutates.

4 · What version 1 refuses to carry

No money, no amounts, no scores — see §1. When a consecrated observance is to accrue anything, the accrual is a reward/1 entry whose basis is the observance's evidence URL, written by an integration a human chose to build. The two fragments then say, between them, exactly what happened: an act, witnessed, consecrated, and separately an entitlement, capped and unpayable until a venue exists. Neither format can tell the story alone, and that is the design.

5 · Metrics, and the anti-metric

metrics() ships witnessed and consecrated with the raw count so a renderer cannot take the flattering number alone. Raw observance volume is the anti-metric: it is the number a faucet inflates, and any surface showing it without the witnessed share beside it is the failure this format exists to refuse. project() carries a note a renderer may not drop, stating how many observances carry consequence.

6 · Serving

A practising subject serves its fragment — whole and verbatim — at /.well-known/ritual.json. One path, one spelling, exported as WELL_KNOWN by the reference implementation so no consumer retypes it. The surface is the fragment and never a derived summary: a summary strips the evidence URLs a stranger verifies and keeps the flattering digits, which is §5's rule run backwards. node vendor-ritual.mjs check <https://domain> fetches and validates the served copy the way a stranger would, and is the only check that can tell a committed calendar from a served one.

7 · What this is not, yet

No sealing, no checkpoint leaves, no epochs beyond the cadence word, no federation of witnesses across fragments, and no publication of this package. The estate's rule applies: publication on the first adopter, never before. The vendored file travels by copy, which is how every record format here actually travels, so being unpublished costs an adopter nothing. Sealing (so an observance can become a checkpoint/1 leaf) is the first candidate for version 2, and it will reuse the shared canonicalisation in @flashyos/verify rather than restating it — sealing rules are shared, never restated.

Raw sources: spec.md · ritual-1.schema.json · conformance/corpus.json — served here as byte copies of the files the validator ships with.