encoding: case names as an encoding of Flags #47

Closed
opened 2026-10-05 21:07:14 +00:00 by orion · 7 comments
Collaborator

Context

["Read", "Write"] instead of 3, as an option of the format. Needs a way for an Encode implementation to ask its format for an option.

Source

Acceptance criteria

  • Format options reachable from Encode/Decode in std/encoding
  • Flags encodes as names when the option is set
  • Tests with std/json and std/yaml

Dependencies

None known.

Moved from the planning lists of the docs; the design record stays the specification.

## Context `["Read", "Write"]` instead of `3`, as an option of the format. Needs a way for an `Encode` implementation to ask its format for an option. ## Source - [FLAGS.md section 6](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/design/FLAGS.md#L105) ## Acceptance criteria - [ ] Format options reachable from `Encode`/`Decode` in `std/encoding` - [ ] `Flags` encodes as names when the option is set - [ ] Tests with `std/json` and `std/yaml` ## Dependencies None known. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop). FLAGS.md section 6 names the gap; the format-option mechanism for Encode/Decode in std/encoding is new public std surface, so refine will likely need an owner decision, and it may touch ENCODING.md decisions (see the decoder-state decision on #95). No duplicate found. next: refine (ready buffer is 6, so not before it drops below 2).

[loop:torbscript] intake: in scope (author orion, label loop). FLAGS.md section 6 names the gap; the format-option mechanism for `Encode`/`Decode` in `std/encoding` is new public std surface, so refine will likely need an owner decision, and it may touch ENCODING.md decisions (see the decoder-state decision on #95). No duplicate found. next: refine (ready buffer is 6, so not before it drops below 2).
Author
Collaborator

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T01:33:19Z

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T01:33:19Z
Author
Collaborator

[loop:torbscript] decided by the loop: (1) Mechanism: Encoder and Decoder get one defaulted query each, flagsAsNames(): Bool returning false, exactly like the existing Encoder.writesPrivateFields (lib.trb, docs/design/ENCODING.md section 15 point 3: a convention is an option of the format, not a question a type asks). No new trait, no generic member, no change to the seven-plus-four vocabulary. (2) Option name flagsAsNames: Bool = false on Json, Yaml (and any other format later), next to naming and strict. The default stays the number, so no published contract breaks. (3) Wire form with the option: a sequence of the case names, written by the case own Encode (variant), in bit order; decoding reads the sequence through the case own Decode, ORs the bits, and an unknown name is a DecodeError. This needs no case-name reflection and no Show coupling. With the option set, only the names are read (no number/name guessing; the decoder has no look-ahead). (4) Flags gets a hand-written Encode/Decode pair that asks the target and otherwise keeps the capsule route (encodeThrough/decodeThroughChecked). Where it lives (std/collections vs std/encoding) depends on the package dependency direction; the implementer picks the one that adds no cycle. Why: smallest surface that matches FLAGS.md section 6 and an existing precedent; all internal std design, no owner category applies.

[loop:torbscript] decided by the loop: (1) Mechanism: `Encoder` and `Decoder` get one defaulted query each, `flagsAsNames(): Bool` returning `false`, exactly like the existing `Encoder.writesPrivateFields` (lib.trb, docs/design/ENCODING.md section 15 point 3: a convention is an option of the format, not a question a type asks). No new trait, no generic member, no change to the seven-plus-four vocabulary. (2) Option name `flagsAsNames: Bool = false` on `Json`, `Yaml` (and any other format later), next to `naming` and `strict`. The default stays the number, so no published contract breaks. (3) Wire form with the option: a `sequence` of the case names, written by the case own `Encode` (`variant`), in bit order; decoding reads the sequence through the case own `Decode`, ORs the bits, and an unknown name is a `DecodeError`. This needs no case-name reflection and no `Show` coupling. With the option set, only the names are read (no number/name guessing; the decoder has no look-ahead). (4) `Flags` gets a hand-written `Encode`/`Decode` pair that asks the target and otherwise keeps the capsule route (`encodeThrough`/`decodeThroughChecked`). Where it lives (std/collections vs std/encoding) depends on the package dependency direction; the implementer picks the one that adds no cycle. Why: smallest surface that matches FLAGS.md section 6 and an existing precedent; all internal std design, no owner category applies.
Author
Collaborator

[loop:torbscript] refine: ready. Goal: Flags<Case> can be written and read as the list of its case names instead of its mask, chosen by an option of the format (FLAGS.md section 6, ENCODING.md section 15 point 3). Acceptance criteria: (a) Encoder and Decoder have flagsAsNames(): Bool, default false, documented; (b) Json(flagsAsNames: true).encode(access) gives ["Read","Write"] and Json().encode(access) still gives 3; (c) Json(flagsAsNames: true).decode<Flags<Permission>> round-trips, an unknown name and a non-list are a DecodeError with the field chain; (d) the same two tests for std/yaml; (e) Values and other formats stay unchanged (default false); (f) FLAGS.md section 3/6 and the json/yaml docs updated, section 6 entry removed, slice table row 2 no longer says "left"; ENCODING.md gets the option in its option list. Design record point: FLAGS.md section 6 "Open", ENCODING.md section 15 point 3. Seed: no (no syntax, native, driver flag or string-looked-up std name); if the compiler capsule derivation turns out to need a change for a hand-written Encode on Flags, that is a stage-0 matter to note in the PR, not a seed refresh. Size: one PR, two commits: feat(encoding): flagsAsNames query on Encoder and Decoder (+ json/yaml option and tests), feat(collections): Flags encodes as case names or one commit if small. Token usage of this refine: ~45k. next: implement

[loop:torbscript] refine: ready. Goal: `Flags<Case>` can be written and read as the list of its case names instead of its mask, chosen by an option of the format (FLAGS.md section 6, ENCODING.md section 15 point 3). Acceptance criteria: (a) `Encoder` and `Decoder` have `flagsAsNames(): Bool`, default `false`, documented; (b) `Json(flagsAsNames: true).encode(access)` gives `["Read","Write"]` and `Json().encode(access)` still gives `3`; (c) `Json(flagsAsNames: true).decode<Flags<Permission>>` round-trips, an unknown name and a non-list are a `DecodeError` with the field chain; (d) the same two tests for `std/yaml`; (e) `Values` and other formats stay unchanged (default false); (f) FLAGS.md section 3/6 and the json/yaml docs updated, section 6 entry removed, slice table row 2 no longer says "left"; ENCODING.md gets the option in its option list. Design record point: FLAGS.md section 6 "Open", ENCODING.md section 15 point 3. Seed: no (no syntax, native, driver flag or string-looked-up std name); if the compiler capsule derivation turns out to need a change for a hand-written `Encode` on `Flags`, that is a stage-0 matter to note in the PR, not a seed refresh. Size: one PR, two commits: `feat(encoding): flagsAsNames query on Encoder and Decoder` (+ json/yaml option and tests), `feat(collections): Flags encodes as case names` or one commit if small. Token usage of this refine: ~45k. next: implement
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T05:43:16Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T05:43:16Z
Author
Collaborator

[loop:torbscript] implement: PR #402 is open and CI is green (gates, tier A/B, registry, playground, same-C). Encoder/Decoder get a defaulted flagsAsNames(); Json(flagsAsNames: true) and Yaml(flagsAsNames: true) write a Flags as the list of its case names (lowest bit first) and read it back through each case's own decode; the default stays the mask as a number. Tests in std/json and std/yaml, docs and FLAGS.md/ENCODING.md updated; Describe for Flags is unchanged (still the number); no seed change. The first push had a parse error (bits().encode target), fixed in the second commit. Tokens: not metered in this session (no usage figure available to me). next: review

[loop:torbscript] implement: PR https://git.torb.dev/torbscript/language/pulls/402 is open and CI is green (gates, tier A/B, registry, playground, same-C). `Encoder`/`Decoder` get a defaulted `flagsAsNames()`; `Json(flagsAsNames: true)` and `Yaml(flagsAsNames: true)` write a `Flags` as the list of its case names (lowest bit first) and read it back through each case's own `decode`; the default stays the mask as a number. Tests in std/json and std/yaml, docs and FLAGS.md/ENCODING.md updated; `Describe` for `Flags` is unchanged (still the number); no seed change. The first push had a parse error (`bits().encode target`), fixed in the second commit. Tokens: not metered in this session (no usage figure available to me). next: review
argus closed this issue 2026-10-23 01:31:26 +00:00
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#47
No description provided.