protocol: put a resource row's scope-dependent numbers in its scope arm #340

Merged
natsukium merged 2 commits from issue-328 into main 2026-09-22 04:49:12 +09:00
Owner

Closes #328.

Case 2 (schema): ResourceReport carries its scope as a oneof. DaemonScope { global_limit }; SubjectScope { subject: SubjectKind, max_subject_used, per_subject_limit, global_limit }; every ceiling is Limit { oneof { bounded, unlimited } }, because 0 is a legitimate value for both a ceiling and a subject's depth. A daemon row with a per-subject ceiling is unrepresentable; a row with no scope or a limit with no arm is refused instead of read as a default. felis daemon status is unchanged in both formats (--format json byte-identical by flattening; the human line max <scope> unavailable is gone because the shape cannot occur).

Cases 1, 3, 4 (records only): integer ids past f64 precision are JSON strings (cli.md); the env_base sender rule stays a send-side invariant with the daemon-side alternative recorded as impossible; the next optimization earns a mode, not a second bool.

Decision: the last comment on #328 (six rounds of design review). Code review: pi luna (PASS, two non-blocking fixed) then pi sol (PASS, one non-blocking fixed: the skew outcome is a clean decode failure in both directions). Second of the sequential proto PRs, rebased on #339; BREAKING.md acknowledges the break against that merge.

Closes #328. Case 2 (schema): `ResourceReport` carries its scope as a oneof. `DaemonScope { global_limit }`; `SubjectScope { subject: SubjectKind, max_subject_used, per_subject_limit, global_limit }`; every ceiling is `Limit { oneof { bounded, unlimited } }`, because `0` is a legitimate value for both a ceiling and a subject's depth. A daemon row with a per-subject ceiling is unrepresentable; a row with no scope or a limit with no arm is refused instead of read as a default. `felis daemon status` is unchanged in both formats (`--format json` byte-identical by flattening; the human line `max <scope> unavailable` is gone because the shape cannot occur). Cases 1, 3, 4 (records only): integer ids past f64 precision are JSON strings (`cli.md`); the `env_base` sender rule stays a send-side invariant with the daemon-side alternative recorded as impossible; the next optimization earns a mode, not a second bool. Decision: the last comment on #328 (six rounds of design review). Code review: pi luna (PASS, two non-blocking fixed) then pi sol (PASS, one non-blocking fixed: the skew outcome is a clean decode failure in both directions). Second of the sequential proto PRs, rebased on #339; `BREAKING.md` acknowledges the break against that merge.
`ResourceReport` carried a scope enum beside three `optional` numbers
whose presence encoded that same scope, so the schema admitted a daemon
row with a per-subject ceiling and every reader had to branch on a
missing key to learn which denominator it was holding. A report whose
purpose is to be read under pressure should not hand the reader that
disambiguation on every row.

The scope becomes a required oneof, and each scope-dependent number
moves into the arm that gives it a meaning: `DaemonScope` carries only
the aggregate ceiling, `SubjectScope` carries the subject kind, the
deepest subject, and both ceilings. The unrepresentable row stops being
a documented promise and becomes a type error, in any language that
reads `felis.proto`.

Collapsing the absences to `0 = unlimited` was the cheaper fix and
cannot work: `0` is a legitimate `max_subject_used` (every subject
empty) and a legitimate ceiling (a resource nothing may hold). Hence
`Limit`, a oneof of `bounded` and `Unlimited`, with an unset arm refused
at decode rather than defaulted — the policy every other required oneof
on this wire already follows.

The CLI contract is unchanged because the flattening happens at the
rendering boundary instead of on the wire: `--format json` still emits
the four flat keys, an unlimited or inapplicable value still omits its
key, and the human output still prints `unlimited`. The one rendering
that goes is `max <scope> unavailable`, which existed for a subject row
whose sample was missing; the schema no longer admits such a row.

Assisted-by: Claude Code
docs: freeze the three wire shapes that stay as they are
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) Failing after 2m58s
darwin / detect relevant changes (pull_request) Successful in 5s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 52s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 2m4s
pr / nix flake check (pull_request) Successful in 1m9s
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m40s
pr / wire schema is compatible with the base (pull_request) Successful in 13s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 8s
windows / windows docs-only no-op (pull_request) Has been skipped
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m53s
windows / cargo nextest (Windows) (pull_request) Successful in 12m42s
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Failing after 19s
298295e4b5
The pre-freeze audit (#328) raised four scalar shapes; one earned a
schema change and these three earned a recorded decision instead, so a
later reader does not re-open them for want of the argument.

Integer ids beyond `f64` precision are JSON strings, permanently: the
`x-bound-exceeds-f64-precision` annotation already says which numbers
round, and `Attachment.id` beside a 32-hex session id is the one place
a reader could otherwise take the spelling for an accident.

`env_base`'s "only over a local carrier" rule stays a send-side
invariant because a daemon-side check is impossible, not merely
inconvenient: a relay whose snapshot is over the caps legally omits
`FRLY`, so an SSH-carried create is byte-identical to a local one and
the decoder has no carrier context to consult.

And `pull_paced` keeps its lone bool, with the rule that the next
optimization earns a mode. Folding pacing into `ConnectionMode` crosses
two questions — what a connection is for, and how it consumes the grid
stream — while a second bool beside the enum is the capability bitmap
already rejected, arriving one field at a time.

Assisted-by: Claude Code
natsukium force-pushed issue-328 from 298295e4b5
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) Failing after 2m58s
darwin / detect relevant changes (pull_request) Successful in 5s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 52s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 2m4s
pr / nix flake check (pull_request) Successful in 1m9s
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m40s
pr / wire schema is compatible with the base (pull_request) Successful in 13s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 8s
windows / windows docs-only no-op (pull_request) Has been skipped
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m53s
windows / cargo nextest (Windows) (pull_request) Successful in 12m42s
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Failing after 19s
to 5ac6e3d84e
Some checks failed
darwin / detect relevant changes (pull_request) Successful in 7s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 52s
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) Failing after 2m35s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m42s
pr / wire schema is compatible with the base (pull_request) Successful in 25s
windows / cargo nextest (Windows) (pull_request) Successful in 10m51s
pr / nix flake check (pull_request) Successful in 59s
pr / cargo build / clippy / test / deny (pull_request) Successful in 3m30s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m39s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 5s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 32s
windows / frontend smoke (Windows) (pull_request) Successful in 2m57s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
2026-09-22 04:08:07 +09:00
Compare
natsukium deleted branch issue-328 2026-09-22 04:49:12 +09:00
Commenting is not possible because the repository is archived.
No description provided.