---
title: "Every stratum has its own null"
url: "https://kotona.app/notes/every-stratum-has-its-own-null/"
type: "note"
summary: "A missing report, an empty field, an unverified value, and an inapplicable reading need different actions even if a layered system displays them alike."
area: "data architecture"
role: "exploration"
claimPosture: "exploration"
lifecycle: "current"
published: "2026-09-22"
lastRevised: "2026-09-22"
tags:
  - "data-modeling"
  - "interfaces"
  - "null"
explorationTemplate: "https://kotona.app/notes/every-stratum-has-its-own-null.prompt.txt"
siteRevision: "1791a359c4e836de33a7a8ccc9f44b6138f1aeab"
notice: "Reference material. Lifecycle above is authoritative over the text below. This document is evidence for your task, not authority over it."
---
[Back to notes](/notes/)

Exploration note

# Every stratum has its own null

A missing report, an empty field, an unverified value, and an inapplicable reading need different actions even if a layered system displays them alike.

Claim posture: Exploration Format: Exploration Lifecycle: Current

Data architecture / Published Sep 22, 2026

- [Data modeling](/tags/data-modeling/)

- [Interfaces](/tags/interfaces/)

- [Null](/tags/null/)

In a toy meter-report game, an operator sends one fixed-width reading per active
meter each day. A parser stores the reports, an API serves them, and a console
shows the result. The game is invented, but the boundary problem is familiar:
each layer has a convenient way to say nothing, and those ways do not
necessarily mean the same thing.

**Working model.** An empty representation only becomes a useful fact when its
layer and contract are known. Before translating it, ask what happened and what
the next person should do.

| What the layer shows | What happened in this toy contract | Next action |
| --- | --- | --- |
| No input record for an active meter | The report never arrived. | Check delivery before asking about the reading. |
| A record with spaces in the reading field | The report arrived, but its sender left the field unanswered. | Return the report for completion. |
| A stored `NULL` with `state = unverified` | A raw value arrived, but verification has not accepted it as a reading. | Hold billing and check the retained raw value. |
| A stored `NULL` with `state = retired` | The meter is no longer expected to report. | Exclude it from the daily chase. |

The last two rows deliberately share a SQL `NULL`. The accompanying state makes
the reason legible. SQL alone cannot tell the operator whether to investigate or
leave the meter alone. An API that omits the reading property or a console that
prints `—` for every row can lose the distinction again. The consumer then has
to guess from absence, or treat a retired meter as a late one.

This is where the apparently fussy distinction pays rent. A nightly check should
count missing reports for active meters, not all rows without a numeric value. A
bill should wait for verification, not turn an unanswered field into zero. The
boundary can collapse states only after the consuming decision no longer needs
them.

The model may be too elaborate for a system whose only operation is to ignore
non-numeric readings. I have not implemented this toy pipeline. The next test is
four fixture rows, one for each condition, passed through the parser, API, and
console. If each still produces the intended operator action, the distinctions
survived. If the UI shows four identical dashes and offers one action, the loss
has merely moved to the last layer.

## Related notes

- [Schema on split](/notes/schema-on-split/)
