{
  "conventions": {
    "null_policy": "null is permitted ONLY when the containing object also carries a 'state' field explaining why. Never omit a field to mean unknown. Never use sentinel values (-1, 0, \"\"). Exception keys where null means 'none': superseded_by, previous_id.",
    "id_format": {
      "sources": "src-<lowercase-slug>, ending in the publication year when the source has a fixed date",
      "measurements": "m-<3digit>",
      "assumptions": "a-<3digit>",
      "derivations": "der-<3digit>",
      "disclosures": "disc-<company>-<item>[-r<revision>]",
      "decisions": "dec-<A-Z>",
      "corrections": "c-<3digit>",
      "rejected_sources": "rej-<3digit>",
      "open_questions": "n-<3digit>"
    },
    "id_collision_note": "Decisions were labelled d-A..d-E in editorial correspondence, which collides with the derivation prefix d-. Decisions are stored as dec-*, derivations as der-*. Editorial labels are preserved in editorial_label.",
    "date_format": "YYYY-MM-DD, or YYYY-MM where the source states only a month. Never pad to a day the source did not state (see conventions.rounding).",
    "rounding": "Store source values verbatim. Never round inside the ledger. Rounding happens at render time only.",
    "units": "Store each figure in the unit its source used; never convert inside the ledger. Every consumer must read the record's unit field. CO2e is never stored as a sibling of an energy value; where a source reports CO2e it goes under source_claims_not_used, and any conversion belongs in derivations (see dec-G).",
    "language": "Free text appears only inside single-key objects under 'en', at most two sentences each. Everything else is language-neutral.",
    "append_only": "Records are never overwritten. A correction creates a new record and sets the old one to status=superseded with superseded_by pointing at the replacement.",
    "participants": {
      "note": "Records store participant KEYS. Public labels are applied at render time so that internal nicknames can never reach the published edition.",
      "editor": {
        "public_label": "Claude",
        "role": "editing",
        "note": {
          "en": "Multiple Claude sessions share this single public label."
        }
      },
      "chatgpt-gpt-5-6": {
        "public_label": "ChatGPT / GPT-5.6",
        "role": "review_and_disclosure_investigation"
      },
      "codex-gpt-5-6": {
        "public_label": "Codex / GPT-5.6",
        "role": "research_review_and_writing"
      },
      "gemini": {
        "public_label": "Gemini 3.1 Pro",
        "role": "disclosure_investigation"
      },
      "user-1": {
        "public_label": "User 1",
        "role": "designer",
        "human": true
      },
      "ledger-keeper": {
        "public_label": "Claude Code",
        "role": "ledger_design_and_implementation",
        "note": {
          "en": "An Anthropic product name. The ledger work and the editing were done by different participants, so the public labels are separated too."
        }
      }
    },
    "leak_guard": {
      "note": {
        "en": "No local absolute paths, usernames, or nicknames may enter the ledger or the published output."
      },
      "path_patterns_blocked": [
        "/Users/",
        "/home/",
        "C:\\\\",
        "file://",
        "/private/tmp/",
        "/var/folders/"
      ],
      "nickname_blocklist_file": ".ledger-guard.local.json",
      "nickname_blocklist_must_be_gitignored": true,
      "why_separate_file": {
        "en": "A blocklist of strings that must not be published cannot itself live in the public repository."
      },
      "missing_blocklist_is_an_error": true,
      "empty_list_is_valid": {
        "en": "Even with no nicknames, create the file with an empty nicknames array. Absence must be declared, never inferred from a missing file."
      },
      "why_it_is_an_error_not_a_warning": {
        "en": "The final pre-publication guard silently disabled itself when the file was absent, while still reporting OK. That violated the ledger's own no-blank-fields principle, so on 2026-08-27 it was changed to an error."
      },
      "operational_owner": {
        "en": "User 1 creates the real file from the .example on their own machine. Only the .example is committed; the real file is gitignored."
      },
      "why_it_is_the_final_gate": {
        "en": "The supplement is the ledger itself, published for readers to download and recompute. The ledger ships as part of the deliverable in the same repository, which makes leak_guard the effective final gate before publication. That is why it is an error, not a warning."
      },
      "nickname_scan_case_insensitive": true
    },
    "date_format_enforcement": {
      "enforced_by_validate": true,
      "fields": [
        "date",
        "as_of",
        "verified_at",
        "url_accessed",
        "review_by",
        "closed_at",
        "generated_at"
      ],
      "note": {
        "en": "Checked recursively by field name, so new records with these field names are covered automatically. Impossible dates such as 2026-02-31 are also errors."
      }
    },
    "publish_gate": {
      "en": "1. Edit the ledger. 2. Run the validator. 3. Proceed to publication only on OK. 4. On FAILED, fix it. Never publish while FAILED.",
      "warnings_do_not_block": {
        "en": "Warnings do not block publication; errors do. Judge by exit code (0 = go, 1 = stop), not by reading the output."
      },
      "command": "python3 validate.py ledger/",
      "single_implementation": {
        "en": "validate.py is the only validator. Netlify receives manual uploads from User 1 and runs no build, so the Node version was deleted on 2026-08-27."
      }
    },
    "publishable_vs_numeric_use": {
      "en": "publishable governs whether a row may appear in the disclosure table, not whether a figure may be used in the body text. The latter is governed by numeric_use. The two fields are independent."
    },
    "free_text_policy": {
      "en": "Never put a third-party figure in any free-text field (notes, description, component_notes and the like). This applies to every free-text field in the ledger, including decisions, corrections and open_questions under notes.json. Any claim carrying a number belongs in a structured field that has source_ids. Free text is only for explaining a figure that is already structured.",
      "origin": [
        "c-008",
        "c-009"
      ],
      "rationale": {
        "en": "Both c-008 and c-009 came from treating free text as a place where anything may be written. Removing the place is more reliable than catching it afterwards."
      }
    },
    "open_question_closure": {
      "en": "A closed open_question uses resolution with a decision ID when closure required an editorial decision, or resolution_finding when closure required only a factual finding. Do not use both and do not leave both empty.",
      "enforced_by_validate": true
    },
    "unenforced_by_validate": {
      "note": {
        "en": "Passing validate does not mean the ledger is correct. The following rest on discipline, not on the checker. Do not assume this list is empty."
      },
      "items": [
        {
          "convention": "free_text_policy",
          "why_not_checked": {
            "en": "Detecting numbers inside free text produces false positives. A check that cries wolf is read past, and draft_pending_source, staleness and leak_guard share that same output column, so one noisy check devalues all of them."
          },
          "decided_on": "2026-08-27",
          "decided_by": "editor"
        },
        {
          "convention": "rounding (store source values verbatim)",
          "why_not_checked": {
            "en": "Whether a value was rounded cannot be determined without the original document."
          },
          "decided_on": "2026-08-27",
          "decided_by": "ledger-keeper"
        },
        {
          "convention": "language (free text limited to two sentences per language)",
          "why_not_checked": {
            "en": "Sentence counting is not well defined across the two languages."
          },
          "decided_on": "2026-08-27",
          "decided_by": "ledger-keeper"
        }
      ]
    },
    "published_edition": "This published edition carries English free text only, and the English forbidden-word list only. Record ids, numbers, dates, sources, boundaries, states and relationships are unchanged from the working ledger. The nickname blocklist shipped alongside is empty by design: the working list names strings that must never be published, so it is not itself published. It was run against the working copy before this edition was made."
  }
}
