Make goal 4 ask for intuitive templates, and decide a selected row is fixed, not drawn

This commit is contained in:
2026-09-27 15:00:16 +02:00
committed by Lillem4n
parent 538114a926
commit fb7a30e505
5 changed files with 39 additions and 22 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ In [docs/decisions.md](docs/decisions.md):
- A selector is bracketed, and a dot inside it is literal - A selector is bracketed, and a dot inside it is literal
- `parent` names the link column and the table alike - `parent` names the link column and the table alike
- After a row, a path names a column or a linked table - After a row, a path names a column or a linked table
- Every reference path into one family selects the same rows, per render and group - A selected row is fixed, not drawn, and an unnamed read beside it is refused
- A parent row with no child row is a load error - A parent row with no child row is a load error
- The choice-of-rows fence guards a data file's root, and requires string fields - The choice-of-rows fence guards a data file's root, and requires string fields
- A table is a record of string columns - A table is a record of string columns
+6 -3
View File
@@ -823,9 +823,12 @@ the Development section below, and who ships a register the four above then draw
3. **Valid by construction** — every value passes the check its real consumer 3. **Valid by construction** — every value passes the check its real consumer
applies; facts that belong together come from one draw, within a value and applies; facts that belong together come from one draw, within a value and
across categories. across categories.
4. **Text means what it says** — a format renders as written; only `{…}` varies, 4. **Templates are intuitive, and text means what it says** — a template does what
random characters included (`{digits(3)}`). One spelling per result; the wrong its writer expects on first reading. A format renders as written; only `{…}`
one is a load error naming the right one. varies, random characters included (`{digits(3)}`), and a selected row, `[SE]`,
is that row wherever it is written. Where a reader could expect two outputs, the
template is a load error naming a spelling for each. One spelling per result; the
wrong one is a load error naming the right one.
5. **Every mistake is a load error** — `New` rejects the data and `NewTemplate` 5. **Every mistake is a load error** — `New` rejects the data and `NewTemplate`
the inline template; on a loaded generator `Fake` fails only for an unknown the inline template; on a loaded generator `Fake` fails only for an unknown
path, `FakeStruct` only for a non-struct argument or a type its tags do not path, `FakeStruct` only for a non-struct argument or a type its tags do not
+16 -9
View File
@@ -359,14 +359,22 @@ folder or another column name would be asking for a second spelling.
today; admitting it later is additive, since a refused spelling gains a meaning and no today; admitting it later is additive, since a refused spelling gains a meaning and no
accepted one changes, so the door stays open for the address records the plan describes. accepted one changes, so the door stays open for the address records the plan describes.
## Every reference path into one family selects the same rows, per render and group ## A selected row is fixed, not drawn, and an unnamed read beside it is refused
A selector pins rows for the render, and a read that draws freely before it could pin a Decided 2026-09-27 by the maintainer, for goal 4; valid while a selector names exactly one row.
row the selector contradicts, so accepting both would make the result depend on which
token rendered first. Requiring one selection per family per group is checkable at load - Selected rows never conflict: `{/misc.territory[SE].capital} / {/misc.territory[FI].capital}`
with no data lookup beyond the selectors themselves, and the error names the rewrite. renders `Stockholm / Helsinki`, and so may two items of one choice.
Two selectors naming one row by key and by name compare equal, since the load check - What a path draws beneath a selected row is one draw per selection, shared by every
resolves them. path through it: `{/geo.SE.region[12].locality.name}` twice names one locality.
- A read naming no row of a table whose family a selection in the render and group
names is refused, naming the selected path and a `drawGroup`: beside
`{/misc.territory[SE].capital}`, a writer reading `{/misc.territory.currency}` expects
`SEK`, and `Stockholm pays in EUR` would pass review. It stays refused beside two
selections, since adding `[FI]` to that template would otherwise make it load.
Checked against three simulated template writers from the Audience, who agreed on each
case but the last, where two expected a render and all three accepted the refusal.
## A parent row with no child row is a load error ## A parent row with no child row is a load error
@@ -409,8 +417,7 @@ row, and whatever they reach, do meet, and so does the format beside them. Two r
meet unless they sit under different rows of one pinned table; pinning a row pins its meet unless they sit under different rows of one pinned table; pinning a row pins its
ancestors too, so rows under different parents are alternatives as well. A table read ancestors too, so rows under different parents are alternatives as well. A table read
whole draws its row apart from every pin, so the rows of one whole read are whole draws its row apart from every pin, so the rows of one whole read are
alternatives only to each other. A choice's items are not alternatives: two items alternatives only to each other.
selecting different rows of one family are refused.
## A table never reaches its own family, by any route ## A table never reaches its own family, by any route
-1
View File
@@ -84,7 +84,6 @@ func (r *tableRead) selected(t *table) (tableSel, bool) {
// read one consistent draw: a table rendered whole beside a path into the family, // read one consistent draw: a table rendered whole beside a path into the family,
// a table one read draws that another pins, and two reads pinning different rows. // a table one read draws that another pins, and two reads pinning different rows.
// The reads come sorted by group and path, so which pair is reported does not vary. // The reads come sorted by group and path, so which pair is reported does not vary.
// docs/decisions.md#every-reference-path-into-one-family-selects-the-same-rows-per-render-and-group
func checkFamilies(reads []pathRead) error { func checkFamilies(reads []pathRead) error {
for i, r := range reads { for i, r := range reads {
if r.tr == nil { if r.tr == nil {
+16 -8
View File
@@ -47,11 +47,9 @@ This round, in order:
where `NewTemplate` and `NewRecordTemplate` do not, and `List` claims to be every where `NewTemplate` and `NewRecordTemplate` do not, and `List` claims to be every
path `Fake` accepts, which a selector is too. Delete the dead `table.descends` and path `Fake` accepts, which a selector is too. Delete the dead `table.descends` and
the redundant `string(key)` in `checkNoRepeatedItem`. the redundant `string(key)` in `checkNoRepeatedItem`.
- Keep only the standing choice in the decision "The rows of a table are alternatives", - Delete the decision "The rows of a table are alternatives": it defines `alternatives`,
a choice's items refused as alternatives, and give it the goal it serves: the rest which its comment in `family.go` already does, and the README's "which pairs are
defines `alternatives`, which its comment in `family.go` already does, and the README's weighed against each other" sends a reader to the log for that definition.
"which pairs are weighed against each other" sends a reader to the log for that
definition.
- Run the nine-seat panel again and file what it names here. - Run the nine-seat panel again and file what it names here.
### Data ### Data
@@ -59,6 +57,8 @@ This round, in order:
Shape: T = table, t = template, c = choice. Read every factual list from a register Shape: T = table, t = template, c = choice. Read every factual list from a register
by a `data-import/` script, as README goal 12 asks. by a `data-import/` script, as README goal 12 asks.
- Strip the whitespace `data-import/territory.py` copies from its register:
`misc.territory[CW].capital` renders ` Willemstad`, with a leading space.
- Place xlsx cells by their `r` reference in `data-import/xlsx.py`: a sheet omitting an - Place xlsx cells by their `r` reference in `data-import/xlsx.py`: a sheet omitting an
empty cell shifts every later column left. empty cell shifts every later column left.
- Spell `misc.creditcard`'s digit runs `{digits(n)}`, and refuse a repeat over a lone - Spell `misc.creditcard`'s digit runs `{digits(n)}`, and refuse a repeat over a lone
@@ -171,6 +171,13 @@ by a `data-import/` script, as README goal 12 asks.
### Library and CLI ### Library and CLI
- Render selected rows as the decision "A selected row is fixed, not drawn, and an
unnamed read beside it is refused" states: `New` refuses
`{/misc.territory[SE].capital} / {/misc.territory[FI].capital}` today, and a choice
whose items select different rows. Have the refusal of an unnamed read name a
selected path that loads: beside `{/geo.SE.locality[Lund].name}`,
`{/geo.SE.municipality.name}` names none today, and the one that holds selects from
the top, `{/geo.SE.municipality[Lund].name}`.
- Report the same error every load for a table with two bad options, and for a - Report the same error every load for a table with two bad options, and for a
folder with two unnamed rows files: `readTableOptions` and `loadDir` return on the folder with two unnamed rows files: `readTableOptions` and `loadDir` return on the
first in Go's map order. first in Go's map order.
@@ -192,9 +199,6 @@ by a `data-import/` script, as README goal 12 asks.
error names the category and the two columns but no row, so the author greps a error names the category and the two columns but no row, so the author greps a
register-sized file for the cell — and "name the fields you want instead" rewrites a register-sized file for the cell — and "name the fields you want instead" rewrites a
cell that is not the one to fix. cell that is not the one to fix.
- Have the family fence offer only a rewrite that works: where the drawn table's rows select
more than one row of the family, every `write {/terr[NO].name}` it offers fails the same
way, so only the `drawGroup` it also names is left.
- Spell a table one way across the family errors: `checkFamilyPair` names - Spell a table one way across the family errors: `checkFamilyPair` names
`drawn.category`, `pinRow` names `t.path` and `mustRow` panics with `t.category`, so one `drawn.category`, `pinRow` names `t.path` and `mustRow` panics with `t.category`, so one
table is `territory` and `misc.territory`, and the short spelling names no file where two table is `territory` and `misc.territory`, and the short spelling names no file where two
@@ -214,6 +218,10 @@ by a `data-import/` script, as README goal 12 asks.
### Open questions to settle ### Open questions to settle
- Decide whether a path steps from a row up to its parent, so
`{/geo.SE.locality[Lund].municipality.name}` reads Lund's municipality: three simulated
template writers all expected it to, per goal 4, and today it fails with "municipality
is a column, and a cell holds no fields". Adding it only accepts a refused spelling.
- Decide whether a goal 5 violation joins the comprehension gate's exception list: the - Decide whether a goal 5 violation joins the comprehension gate's exception list: the
`{/sel}|{/sel}` panic waits behind the whole round at 5.9, and `{/sel}|{/sel}` panic waits behind the whole round at 5.9, and
`{/geo.SE.locality}|{/geo.SE.municipality}` mispairs the shipped data meanwhile, though a `{/geo.SE.locality}|{/geo.SE.municipality}` mispairs the shipped data meanwhile, though a