# Flags: declared, read and tested

Each flag in `flags { }` is a `Bool` or an enum whose variants carry no
data, with a `default:` written as a test's `flags:` writes a value, an
`owner:` in quotes, and an `expires:` date in quotes, `"2027-01-31"`. A
flag missing one of the three, giving one that isn't valid, of another
type, or declared twice is `flag.declaration_invalid`. `client: true`
or `false` says whether `GET /flags` answers it
([`GET /flags`](flags-changed-listed-and-removed.md#get-flags)).

## Where it is read

An action, a test and a route's `run:` arguments read `flags.<name>`; a
test's read gives what its `flags:` says. A function, a constant, a rule
or a template reading one is `flag.read_context`, and a name no flag has
is `flag.unknown`, in code and in a test's `flags:`, with the nearest
flag's name offered.

## The state each server holds

`jadpo_flags` holds a row for each flag that has been changed: everyone's
value, `NULL` for the `default:`; the named users' values, an object of
each user's `id` to a value; and the shares of users, an array of
`[value, percent]` pairs in order, each a whole percentage. A value is
`true` or `false`, or a variant's name. A flag with no row, and a value
no variant has any more, give the `default:`.

Each server reads the table at most every five seconds, as a request or
a tick begins, so a change applies within ten seconds. A read that
fails keeps what the server had, or nothing, which gives every flag its
`default:`, and the next is tried five seconds later. The first flag an
operation reads fixes the state for the rest of it, and each flag's
value is worked out once per operation.

## Who is in a share

A user's place among 100 is the first eight bytes of the BLAKE3 hash of
the flag's name, a zero byte and the user's `id` in lowercase, read as a
little-endian number, modulo 100. A user whose place is below the first
share's percentage gets its value, below the first two shares' sum the
second's, and so on; anyone else gets everyone's value. A named user's
value comes before any share. A service, a schedule's run, a
subscriber's delivery and a request no one signed in to have no user, so
they get everyone's value.

## Judging a flag's date

`jadpo check` judges `expires:` at today and `jadpo build` at the
commit's date, both in UTC, or at `SOURCE_DATE_EPOCH` when it is set. A
flag is past its date from the day after it, and `flag.expired` names
its owner. Running a program or its tests judges no date.
