### `wai lock`, `wai check` and `wai build`: a project pinned, validated and built

- **`wai lock`** (`project.lock`) writes `wai.lock` beside `wai.json`: `wai`'s version and its
  catalogue's digest, the capability registry's, the template's, each parameter set's pin, the
  packaging policy's canonical digest and each conformance corpus's digest. The file is RFC 8785 on
  one line with no line ending and no time and no path, so the same inputs give the same bytes on
  any host, and a checkout with `core.autocrlf=true` leaves it as it is. A member with nothing to pin
  is absent. It refuses when it cannot compute something the project states. `wai lock --check` holds
  the file to what it pins: exit 1 when it is stale or was edited by hand, each member named. Its
  reader refuses a later version as that version, a member it does not know, and bytes that are not
  the canonical form. The schema is [schema/wai-lock-1.schema.json](schema/wai-lock-1.schema.json);
  [project-conformance/](project-conformance/README.md) pins the bytes and holds a second reader, in
  Python with the standard library alone, that recomputes every digest.
- **`wai check`** (`project.check`) validates a project without building it: `wai.json`, each file it
  states, the capabilities it requires against the registry, its app by `wai app check`, what a build
  would make of its site, and `wai.lock` against what it pins. It reports every finding, not the
  first. What it could not run here (no `wai-pack`, no checkout of the standard, a build without the
  feature `app`) says `not checked`, and is never counted as holding: the exit is 1 when a check
  failed, 5 when none failed and one or more could not run, 0 only when every check ran and held.
  Each finding carries `fix`, the command that fixes it where one does: a lock that is stale, was
  edited by hand or is not there says `run wai lock` (with the directory and the `--root` and
  `--packer` the check was given), in the text and in the envelope, and one another `wai` wrote
  says which version wrote it.
- **`wai build`** (`project.build`) writes `dist/`, after the same checks, and refuses with nothing
  written when one fails, a stale lock among them: the site, the app, and the package by `wai-pack
  pack` and `wai-pack verify`. The same project and lock give the same bytes, in any directory at any
  time. It leaves out what the site holds that is not the site's: the project's working files, what
  the package reads, and a file holding a signing key's secret; each is named in its report. Last it
  writes `dist/wai-build.json`, naming the canonical digest of `wai.json` and the digest of `wai.lock`
  ([schema/wai-build-1.schema.json](schema/wai-build-1.schema.json)), so a built tree says what it
  came from. A project that states nothing for `wai` to build gets the record alone.
- **`wai create`** writes the project's `wai.lock` with its other files, so a new project passes
  `wai check` and `wai build` as it is.
- **The watch room's sample sound** is made in integer arithmetic, its samples a rational form of
  the sine, at most 10 of 6000 from it: the same tone, and the same bytes on every host by
  construction, so the digest of the template a lock pins does not rest on a platform's floating
  point. [project-conformance/vectors.json](project-conformance/vectors.json) pins its digest.
- **The project file** gains the members these read: `requires.capabilities`, `priors`, `package`
  (`policy`, `sources`, `out`) and `conform.suites`. Its reader now goes through a whole file, so
  `wai check` names every problem, and refuses a member named twice.
- **`wai mcp`** serves the three as `project_check`, `project_lock` and `project_build`.
- **Line endings.** `wai/examples/`, `wai/LICENSE`, `wai/catalogue/ops.json` and the corpora a suite
  reads are never converted on a checkout, so a template's digest, the catalogue's and a corpus's are
  one on every host; a test holds each to that.
