protocol: ask for a create's default grid by absence #167

Merged
natsukium merged 2 commits from fix/proto-dims-presence-146 into main 2026-09-06 16:47:08 +09:00
Owner

Fixes #146.

SpawnArgs.dims is now optional GridDims: a create asks for the daemon's
default by absence, and rows/cols 0 is refused like any other value outside
REQ-605a's band. That collapses RequestedDims::admit_announced into admit,
leaving two admissions on the type — admit (refuse out of band) and clamp
(live resize) — instead of three whose only difference was which zeros they
forgave. The pixel pair keeps its 0 = unknown, since a headless create and an
unmapped window genuinely have no pixel size to name. On the CLI --rows and
--cols become a required pair, refused locally at exit 2, and the bridge
answers half a geometry with invalid_request.

The second commit freezes the 1.0 vocabulary for the other two "none" spellings
the issue raised: ResourceReport's optional ceilings (per_subject_limit,
global_limit, max_subject_used) are absent — not 0 — where the scope
defines no such number, and the env / env_base split states which layer is
UTF-8 by construction and which carries host-native bytes.

The wire break is acknowledged in crates/felis-protocol/proto/BREAKING.md
against the current main tip; buf reports nothing, because a message field
already carries presence in proto3, so only the meaning moved.

Doc cascade: docs/reference/ipc.md (absent-value tables for the create
geometry and the resource ceilings), docs/reference/cli.md (the --rows /
--cols pair and the JSON null-vs-0 reading), docs/reference/spec.md,
docs/explanation/architecture/ipc.md ("Absence is the only thing a sentinel
may mean" extended to the request side and the ceilings),
docs/explanation/architecture/session-lifecycle.md, skills/felis/SKILL.md,
and a CHANGELOG.md entry.

Verified: just check green (fmt, clippy, nextest, deny, proto-compat); reviewed by pi luna + pi sol; docs proofread by Gemini gemini-3.8-flash-high

Deferred

  • Run the Kani harness admitted_geometry_is_bounded_or_refused under just kani — the harness was re-pointed at admit and compiles, but Kani needs the .#kani shell, which is outside the just check gate this task runs; the property is unchanged from the version that was proved before.
  • felis.proto's ResourceReport and EnvPair/EnvBytesPair comments were left as they are — the accepted plan is docs-only for both; the freeze tables now live in reference/ipc.md and reference/cli.md.
Fixes #146. `SpawnArgs.dims` is now `optional GridDims`: a create asks for the daemon's default by absence, and rows/cols `0` is refused like any other value outside REQ-605a's band. That collapses `RequestedDims::admit_announced` into `admit`, leaving two admissions on the type — `admit` (refuse out of band) and `clamp` (live resize) — instead of three whose only difference was which zeros they forgave. The pixel pair keeps its `0` = unknown, since a headless create and an unmapped window genuinely have no pixel size to name. On the CLI `--rows` and `--cols` become a required pair, refused locally at exit 2, and the bridge answers half a geometry with `invalid_request`. The second commit freezes the 1.0 vocabulary for the other two "none" spellings the issue raised: `ResourceReport`'s optional ceilings (`per_subject_limit`, `global_limit`, `max_subject_used`) are absent — not `0` — where the scope defines no such number, and the `env` / `env_base` split states which layer is UTF-8 by construction and which carries host-native bytes. The wire break is acknowledged in `crates/felis-protocol/proto/BREAKING.md` against the current `main` tip; `buf` reports nothing, because a message field already carries presence in proto3, so only the meaning moved. Doc cascade: `docs/reference/ipc.md` (absent-value tables for the create geometry and the resource ceilings), `docs/reference/cli.md` (the `--rows` / `--cols` pair and the JSON `null`-vs-`0` reading), `docs/reference/spec.md`, `docs/explanation/architecture/ipc.md` ("Absence is the only thing a sentinel may mean" extended to the request side and the ceilings), `docs/explanation/architecture/session-lifecycle.md`, `skills/felis/SKILL.md`, and a `CHANGELOG.md` entry. Verified: just check green (fmt, clippy, nextest, deny, proto-compat); reviewed by pi luna + pi sol; docs proofread by Gemini gemini-3.8-flash-high ## Deferred - Run the Kani harness `admitted_geometry_is_bounded_or_refused` under `just kani` — the harness was re-pointed at `admit` and compiles, but Kani needs the `.#kani` shell, which is outside the `just check` gate this task runs; the property is unchanged from the version that was proved before. - `felis.proto`'s `ResourceReport` and `EnvPair`/`EnvBytesPair` comments were left as they are — the accepted plan is docs-only for both; the freeze tables now live in `reference/ipc.md` and `reference/cli.md`.
`SpawnArgs.dims` spelled "give me your default" as rows or cols `0`, a
value drawn from the geometry's own domain. That forced one wire type
to carry three admissions — refuse-with-sentinel on a create, refuse
without one on announced geometry, clamp on a resize — whose only
difference was which zeros they forgave, and the choice between them
was made by the verb rather than by the type. A caller that knew one
axis could also half-express itself.

Presence says it instead, and costs nothing: a message field already
carries presence in proto3, so the encoding is unchanged and only the
meaning moves. `admit` and `admit_announced` collapse into one
admission, leaving `admit` (refuse) and `clamp` (live resize), and the
daemon resolves the default in the single create path so no zero
geometry travels inside it either. The pixel pair keeps its `0` =
unknown: a headless create and an unmapped window have no pixel size
to name.

On the CLI the same rule makes `--rows` and `--cols` a required pair,
refused locally at exit 2, and the bridge answers half a geometry with
`invalid_request`.

The semantic break is acknowledged in `proto/BREAKING.md` against the
current main tip: `buf` sees nothing, but an old client's `rows: 0`
create is now refused and a new client's absent `dims` makes an old
daemon's converter raise `MissingField`.

Refs #146
docs: freeze how a create, a ceiling, and an env layer say "none"
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m8s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m6s
pr / wire schema is compatible with the base (pull_request) Successful in 9s
darwin / build felis (aarch64-darwin) (pull_request) Successful in 49s
pr / nix flake check (pull_request) Successful in 25s
pr / cargo build / clippy / test / deny (pull_request) Failing after 1m11s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
windows / cargo nextest (Windows) (pull_request) Successful in 6m24s
windows / frontend smoke (Windows) (pull_request) Successful in 1m41s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 58s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
f407606922
Three core IPC shapes said "no value" in three different vocabularies,
and only the proto comments knew which. A reader had to reconstruct
from source that a create's default grid is absence rather than `0`,
that an absent `ResourceReport` ceiling is "no ceiling" rather than
"nothing permitted" (and an omitted key in `--format json`), and that
`SpawnArgs.env` is UTF-8 the user typed while `env_base` is
platform-native bytes captured from a host.

Each is now a table in the reference twin that owns it, so freezing the
wire for 1.0 freezes the reading too, with the argument for presence
over sentinels in the IPC explanation and a Revisit trigger for the one
sentinel that survives, the pixel pair's `0` = unknown.

Refs #146

Assisted-by: Claude Code
natsukium force-pushed fix/proto-dims-presence-146 from f407606922
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m8s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m6s
pr / wire schema is compatible with the base (pull_request) Successful in 9s
darwin / build felis (aarch64-darwin) (pull_request) Successful in 49s
pr / nix flake check (pull_request) Successful in 25s
pr / cargo build / clippy / test / deny (pull_request) Failing after 1m11s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
windows / cargo nextest (Windows) (pull_request) Successful in 6m24s
windows / frontend smoke (Windows) (pull_request) Successful in 1m41s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 58s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
to f0c3c0b3fa
Some checks failed
pr / nix flake check (push) Successful in 7s
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 49s
bench / Criterion regression gate (pull_request) Failing after 2m13s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m8s
pr / nix flake check (pull_request) Successful in 6s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m44s
pr / wire schema is compatible with the base (pull_request) Successful in 9s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 58s
windows / cargo nextest (Windows) (pull_request) Successful in 6m21s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 17s
windows / frontend smoke (Windows) (pull_request) Successful in 1m47s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (push) Has been skipped
darwin / build felis (aarch64-darwin) (push) Successful in 15s
fuzz / cargo fuzz smoke (per target) (push) Successful in 1m40s
pr / wire schema is compatible with the base (push) Successful in 11s
pr / publish felis (x86_64-linux) (push) Has been skipped
windows / cargo nextest (Windows) (push) Successful in 7m19s
windows / frontend smoke (Windows) (push) Successful in 1m58s
pr / cargo build / clippy / test / deny (push) Failing after 1m29s
pr / frontend smoke (x86_64-linux) (push) Successful in 6s
windows / cargo clippy (Windows cross) (push) Successful in 20s
windows / package felis (x86_64-pc-windows-msvc) (push) Successful in 1m58s
2026-09-06 16:33:55 +09:00
Compare
natsukium deleted branch fix/proto-dims-presence-146 2026-09-06 16:47:08 +09:00
Sign in to join this conversation.
No description provided.