Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
6 changed files with 145 additions and 334 deletions
Showing only changes of commit a45edf8fb5 - Show all commits
+5 -11
View File
@@ -43,7 +43,7 @@ In [docs/decisions.md](docs/decisions.md):
- `FakeTemplate` and `NewTemplate` both stay
- The shipped data is embedded, not discovered
- With only the shipped set, a category loads on the first call reaching it; beside a `--data-path`, every category loads in `New`
- A bare reference draws each time; a reference path is held
- Every `{…}` draws afresh, and only a name keeps a pick
- Reference sigils follow the filesystem
- A change to what exists is a major; a minor only adds
- An error is a contract by what it names, not its bytes
@@ -60,15 +60,11 @@ In [docs/decisions.md](docs/decisions.md):
- Samples say what they emit, transforms what they do
- A record is a template seen as columns; a Go struct is the one second schema
- A struct's records follow Go's field access, and compile on first use
- A render shares one reference draw per category, per group
- A draw group name is local to its category
- The expansion hold and the render's draws are two fences
- A render's draws make their maps on the first read
- A category never references itself, and a record's fences run at load
- A record's column set is fixed before the first draw
- Null is a `null` item, not a rate
- A typed column holds one value, not composed text
- A column of one reference alone is the column it reads
- A column of one reference or name read alone is the column it reads
- A typed column's calc is refused unless proven
- `Column` carries text, not a Go value
- A unit takes the stem of what it is, and a file the stem of the units it holds
@@ -78,17 +74,15 @@ In [docs/decisions.md](docs/decisions.md):
- A selector is bracketed, and a dot inside it is literal
- `parent` names the link column and the table alike
- After a row, a path names a column or a linked table
- A selected row is fixed, not drawn, and an unnamed read beside it is refused
- `..` steps up to the parent row, and a step down after it draws once per render and draw group, or per pick
- A selected row is fixed, not drawn
- `..` steps up to the parent row, and a step down after it draws afresh, or once per pick
- A table selects by one key column, a code, never a free-form name
- 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
- A table is a record of string columns
- The key map is built at load, the rest on first draw
- Two categories may name one TSV
- A table never reaches its own family, by any route
- A cell may read a reference, and each row its own
- Tables carrying token cells stay small
- A path is walked once without drawing before it is walked for real
- A country's postal codes and streets are siblings under its locality
- A locale's `address` reads its country's `geo` tree, so a locale folder is no data set on its own
@@ -118,4 +112,4 @@ In [docs/decisions.md](docs/decisions.md):
- `List` advertises direct descents only
- A path draws through its compiled steps
- A name lives in the category binding it, or in the repeat binding it, and is drawn on its first read
- comprehension floor: every dimension and the overall at 7.0 or above; items 4 and 90 ship with no scoring run, and item 5 scores them
- comprehension floor: every dimension and the overall at 7.0 or above; the draw restructure and item 90 ship with no scoring run, and item 5 scores them
+13 -10
View File
@@ -17,19 +17,22 @@ replacement, and each removed path, column or flag.
in the category binding it, and a `repeat` binding it picks again on every iteration.
A name is a `{calc()}` operand: `{/misc.coordinate.lat as lat}{calc(lat * 60)}`. What
`New` refuses is listed under [Names](README.md#names).
- Every `{…}` draws afresh, and a name is the one way to keep a pick: `{p.a} {p.b}`,
`{w} {uppercase(w)}` and `{/person.first} {/person.last}` are two draws each, which
`{p as q}{q.a} {q.b}` and `{/person as n}{n.first} {n.last}` make one. `{x as n}`
binds a sibling field as well as a reference, and a struct's path tags into one
category read one pick of it. `drawGroup` is no longer an option, and `New` no longer
refuses a read beside a path into what it reads, or reads of one table family that
could disagree.
- First release: the CLI, the library and the shipped data set.
- Table categories: a category JSON naming a `rows` TSV beside it, with the options
`key`, `name`, `weight` and `parent`; a path selects a row by key or name,
`misc.territory[SE]`, and descends to a linked table by name; linked tables draw
consistently within one render and draw group. `rows` is an option, so no
template may carry a field of that name. A `name` without a `key` resolves
inside the table's `parent`. Refused at `New`: a `name` without a `key` or a
`parent`, a name repeating inside one parent row, a name spelling another row's key, a table named like a column of any
table above it, a table whose format or cell references its own family, and,
within one render and draw group, a path drawing a table another path selects a
row of, or two paths pinning different rows of one table. Two cells are weighed
against each other only where they can render together: never two rows of one table,
nor rows under different rows of a table a path pinned.
`misc.territory[SE]`, and descends to a linked table by name, one path drawing each
row inside the row above it. `rows` is an option, so no template may carry a field
of that name. A `name` without a `key` resolves inside the table's `parent`. Refused
at `New`: a `name` without a `key` or a `parent`, a name repeating inside one parent
row, a name spelling another row's key, and a table named like a column of any table
above it.
- `New` refuses a root choice of templates sharing one format and one set of string
fields, naming the rows TSV to write instead.
- `misc.territory`, `misc.currency`, `misc.language`, `misc.httpstatus` and
+69 -136
View File
@@ -131,22 +131,18 @@ valid identifier. PostgreSQL and SQLite take it as written; MySQL and MariaDB ne
string and a backslash as an escape.
A record written only to emit columns still needs a `format` — the grammar's one
required key — so `"format": ""` carries the fields with an inert format: it
renders nothing by `Fake`, and is compiled only so the tree's fences still run.
The columns are the point, and their facts stay together: a record is one render, so
columns that read a path into one category — `{/currency.code}` and
`{/currency.symbol}` — read one draw of it ([References](#references)), and a
[draw group](#draw-group) draws a column apart. That one draw is also why the columns of one
record may not overlap — `{/cat.a}`, or a bare `{/cat}`, beside
`{/cat.a.b}` is refused, naming the fields to write instead, as
[One draw, one spelling](#one-draw-one-spelling) refuses that pair inside a single
format. Both fences run at load, so a category that loads renders as either shape. A
category never references itself — `{/users.first}` or a bare `{/users}` inside `users`
describes a draw other than the fields beside it — so read a sibling as a path, and put
a value two fields share in its own category and reference that. A field hold, transform or
operand ties fields together within one column as always (see
[Correlated fields](#correlated-fields) and
[Decisions](docs/decisions.md#a-category-never-references-itself-and-a-records-fences-run-at-load)).
required key — so `"format": ""` carries the fields with an inert format: `Fake` renders
it as nothing.
A record is one render, so its columns read one pick of each [name](#names) its category
binds: `"code": "{/currency as c}{c.code}"` beside `"symbol": "{c.symbol}"` is one
currency. Every other `{…}` draws afresh, so `{/currency.code}` and `{/currency.symbol}`
in two columns may name two currencies.
A category never references itself: `{/users.first}` or a bare `{/users}` inside `users`
describes a draw other than the fields beside it. Read a sibling as a field, and put a
value two fields share in its own category, which each binds to a name
([Decisions](docs/decisions.md#a-category-never-references-itself-and-a-records-fences-run-at-load)).
## Data
@@ -341,9 +337,12 @@ a column of one record, its tag a path or an inline template — told apart by
[datatype](#datatype): a string, bool, integer or float kind, or a pointer to one,
which a [`null`](#null) item leaves nil. An integer stays within int64 whatever its
kind, and a value the kind cannot hold, such as `{int(0,300)}` in a `uint8`, is refused
naming a kind that holds it. The fields an embedded struct promotes are columns of the
naming a kind that holds it. Path tags reading into one category, two or more of them
and none selecting a row, read one pick of it, as a record's columns read one name:
`sv_SE.person.first` and `sv_SE.person.sex` describe one person, while an inline template
in a tag draws afresh. The fields an embedded struct promotes are columns of the
same record; a named struct field, or a pointer to one, fills from its own tags as a
record of its own, so its references draw apart from its parent's. `fake:"-"` leaves a
record of its own, so it picks apart from its parent. `fake:"-"` leaves a
struct field, embedded or named, or a pointer to one, unfilled. Untagged fields keep
their values, and so does a pointer back to a struct already being filled; a type
whose fields reach more than 1024 structs is refused, naming `fake:"-"` to cut it. The
@@ -372,8 +371,8 @@ Every character is literal except a `{…}` token:
| Token | Renders |
|-------|---------|
| `{name}` | the sibling field `name`, or the pick bound to the name ([Names](#names)) |
| `{ref as name}` | nothing: binds one pick of the reference `ref` to `name` ([Names](#names)) |
| `{name.field}` | `field` of one draw of `name` ([Correlated fields](#correlated-fields)) |
| `{x as name}` | nothing: binds one pick of `x`, a reference or a sibling field, to `name` ([Names](#names)) |
| `{name.field}` | `field` of a fresh draw of the sibling `name`, or of the pick bound to it ([Correlated fields](#correlated-fields)) |
| `{a\|b}` | one of the named fields, even odds |
| `{fn(args)}` | a builtin ([Functions](#functions)) |
| `{/path}`, `{.path}`, `{..path}` | a node reached from the data root, this file's folder, or the folder above ([References](#references)) |
@@ -556,32 +555,21 @@ Stockholm SE 990000
fejkdata -d ./mydata 'country[SE].city' # Stockholm or Göteborg
fejkdata -d ./mydata country.city.name # a country drawn, then a city inside it
fejkdata -d ./mydata 'city[Oslo].country' # NO — the link column's cell
fejkdata -d ./mydata '{/city.name}, {/country.name}' # Oslo, Norway — one consistent draw
fejkdata -d ./mydata '{/city as c}{c.name}, {c..country.name}' # Oslo, Norway — one city and its country
```
A path descends from a row to a linked table by name, at any depth, and `--list`
advertises each direct step. Within one render and [draw group](#draw-group), linked
tables agree: the first table a reference path reads pins its ancestors, and a
descendant read after it is drawn inside them, so `{/city.name}` and
`{/country.name}` are a city and its country whichever is read first, except a step
down after a `..` ([Step up](#step-up)). A selected row pins the render the same way,
so every reference path into one family of linked tables in one render and group
selects the same rows: one that selects none beside one that does is refused naming the spelling that does, `{/country[SE].city.name}`
beside `{/country[SE].name}`, and two selecting different rows are refused naming a
`drawGroup` to draw them apart in. A bare `{/city}` beside a path into its family is
refused too, since a bare reference draws each time, and so is a path that draws a
table another path in the group selects a row of, `{/city.name}` beside
`{/country[SE].name}`, which the first token rendered would otherwise decide. `New`
also refuses a link cell that is no key of the parent, a parent row no child links
to, a chain of parents that closes, a table named like a column of any table above
it, and a cell or format of a table that references a table of its own family, through
any template, a `repeat` or a `drawGroup` included, since a bare read of the referencing
table draws its row without pinning it, so the family would draw apart from that row:
read the family from a template beside it, or add the value as a column. A cell's
reads are the render's too, weighed against every read that renders with them. A
reference renders one row of a table, so reads in cells of different rows of it, or of
different rows of their ancestors, never render together, whether `[key]` selects the row
or the reference draws it.
advertises each direct step. One path reads one chain of rows: `country.city.name` draws a
country by its weights, then a city inside it by theirs. Two paths are two draws, so
`{/city.name}, {/country.name}` may name a city and another country. The reads of one
[name](#names) agree instead, and the table it binds decides whose weights govern:
`{/city as c}` draws a city by city weight and `{c..country.name}` is its country, while
`{/country as k}` draws a country by country weight and `{k.city.name}` a city inside it.
A name keeps every row its reads pin, so `{k.city.name}` and `{k.city.population}` describe
one city, and `{k.name}` its country.
`New` refuses a link cell that is no key of the parent, a parent row no child links to, a
chain of parents that closes, and a table named like a column of any table above it.
### Step up
@@ -600,9 +588,9 @@ level per `..`: `geo.SE.locality..municipality..region.name`. `.country` after a
still reads the link column's cell, `NO`.
After a `..`, `.` steps down again and draws afresh inside the row stepped up to, so
`city[Göteborg]..country.city` may draw Göteborg again. Paths stepping down from one
`..` read one draw, as one reference path does: `{/city..country.city.name}` and `{/city..country.city.population}`
describe one city, within a render and [draw group](#draw-group), or within a name's pick.
`city[Göteborg]..country.city` may draw Göteborg again. Under a name, the paths stepping
down from one `..` read one draw: `{c..country.city.name}` and
`{c..country.city.population}` describe one city.
A `..` that names anything but the parent table is an error naming the parent. A selector
after a `..` is an error naming the table to select from, since it could name a row outside
@@ -611,7 +599,7 @@ reference, `..` is the folder above ([References](#references)).
### Options and fields
`format`, `weight`, `repeat`, `separator`, `datatype` and `drawGroup` are the only options;
`format`, `weight`, `repeat`, `separator` and `datatype` are the only options;
**any other key is a field** (see [Decisions](docs/decisions.md#options-and-fields-share-one-namespace)), and `rows` makes a category
a [table](#table), so no template carries a field of that name. A [name](#names) shares the
namespace too. An object that does nothing a
@@ -680,18 +668,14 @@ literals, sibling fields and [names](#names), each rendered then read as a numbe
a second argument rounds to that many decimals. A hyphen is always subtraction, so a
hyphenated field or name can't be an operand.
```json
{ "format": "{net} x {qty} = {calc(net * qty, 2)}", "net": ["19.99", "5.00"], "qty": ["3", "7"] }
```
Renders e.g. `19.99 x 3 = 59.97`. A name reads its one pick, so the calc computes from the
value `{lat}` prints:
Each `{…}` draws afresh, so a calc computes from the values its format shows only where
both read one [name](#names):
```json
"{/misc.coordinate.lat as lat}{lat}° is {calc(lat * 60, 0)} arcminutes"
{ "format": "{net as n}{qty as q}{n} x {q} = {calc(n * q, 2)}", "net": ["19.99", "5.00"], "qty": ["3", "7"] }
```
Renders e.g. `5.338477° is 320 arcminutes`.
Renders e.g. `19.99 x 3 = 59.97`.
A result that rounds to zero prints unsigned — `0`, `0.00` — as `{float()}`'s does. An
operand that can never be a number (`"abc"`, or a choice of such) is rejected at load, as
@@ -703,13 +687,12 @@ fail, except in a [typed column](#datatype), which must prove neither happens.
`{lowercase(x)}`, `{uppercase(x)}` and `{ascii(x)}` rewrite the value of `x` — a
field, a path, a `..path` or a [name](#names) — and nest. `ascii` folds Latin letters (`Åsa Öberg`
→ `Asa Oberg`) and drops any other non-ASCII rune. `x` is held
([One draw, one spelling](#one-draw-one-spelling)), so an email built from a name
matches the name beside it:
→ `Asa Oberg`) and drops any other non-ASCII rune. `x` draws afresh like any `{…}`, so an
email matches the name beside it where both read one name:
```json
{ "format": "{p.first} {p.last} <{lowercase(ascii(p.first))}.{lowercase(ascii(p.last))}@example.com>",
"p": [
{ "format": "{person as p}{p.first} {p.last} <{lowercase(ascii(p.first))}.{lowercase(ascii(p.last))}@example.com>",
"person": [
{ "format": "{first} {last}", "first": "Åsa", "last": "Öberg" },
{ "format": "{first} {last}", "first": "Bo", "last": "Ek" }
] }
@@ -731,22 +714,18 @@ without naming `sv_SE`:
"Hej, {/en_US.person}!"
```
Renders e.g. `Hej, Pat Smith!`. A reference path into a category is held like a
[correlated](#correlated-fields) path, but for the whole render — one `Fake`, or one
record — rather than one format: `{.person.first} {.person.last}` name one
person, as do the same two references in sibling fields or a nested template, and
`{lowercase(.person.first)}` reads that same draw. Each `repeat` iteration is
a render of its own, in no group, so it draws anew, and a [draw group](#draw-group) holds a
draw apart. A bare reference names no field and makes its own picks each time —
`{/misc.uuid} {/misc.uuid}` is two draws — while the reference paths inside what it
renders still read the render's draws. Rejected at `New`: a path that is
Renders e.g. `Hej, Pat Smith!`. Each reference draws afresh, a path into a category
too: `{.person.first} {.person.last}` may name two people, and `{/misc.uuid} {/misc.uuid}`
is two draws. Bind the person to a [name](#names) to read one: `{.person as p}{p.first}
{p.last}`. Rejected at `New`: a path that is
unknown, names a folder, has no folder above, reads a field not every variant
of a choice carries, or names the category the reference sits in, and a reference
that leads back to its own value, directly, mutually or through a chain.
### Names
`{ref as n}` binds one pick of a reference to the name `n` and prints nothing. `{n}` then
`{x as n}` binds one pick of `x`, a reference or a sibling field, to the name `n` and prints
nothing. `{n}` then
prints that pick, and `{n.path}` reads a path through it, so every read of `n` describes one
row:
@@ -758,12 +737,11 @@ Renders e.g. `🇳🇴 Norway: capital Oslo, calling code +47`. A name keeps wha
lands on and every level it passes, so `{n}` and `{n.path}` agree, and so does a
[transform](#transforms) of `n.path`. Everything else under the name draws as it would
anywhere: if `n`'s category has the format `{w}-{w}`, it draws `w` twice, and `{n.w}` beside
`{n}` is refused, since it could read either draw.
`{n}` is refused, since it could read either draw. A field binds the same way, so
`{place as p}{p.postal-code} {p.locality}` reads one place ([Correlated fields](#correlated-fields)).
A name is a pick of its own: `{t.name}` and `{/misc.territory.name}` beside it are two draws. A
reference path the picked category reads is still held for the render as usual, so two names
bound to `sv_SE.person`, whose `last` reads `{.last-name.name}`, print the same last name unless
their reads sit in two [draw groups](#draw-group).
A name is a pick of its own: `{t.name}` and `{/misc.territory.name}` beside it are two draws,
and so are two names bound to one category.
A name is drawn on its first read, and lives in the category binding it: any field of the
category may read it, and a record's columns read one pick. Each render of the category picks
@@ -775,50 +753,25 @@ Refused at `New`, each error naming what to write instead:
- a name bound twice outside any `repeat`, twice in one `repeat`, or both inside a `repeat`
and outside it;
- a name that is a field or an option;
- a binding of anything but a reference;
- a binding of anything but a reference or a path into a field of the template binding it;
- a binding in a choice's item, which every other item would leave unbound;
- a binding nothing reads, or a binding of a whole category read once whole where it is
bound, which the bare reference spells: `{/word as w}{w}` is `{/word}`; a calc cannot
read a reference, so a calc's read is not refused;
- a binding nothing reads, or one read once outside a `repeat` nested where it is bound,
which the spelling it binds draws the same way: `{/word as w}{w}` is `{/word}`, and
`{/region as r}{r.name}` is `{/region.name}`; a calc reads neither a reference nor a
path, so there one read is not refused;
- a path through a name that selects a row;
- `{n}` beside `{n.w}` where `n`'s category renders `w` twice, as in `{w}-{w}`;
- reads of a name in two draw groups, or inside a `repeat` nested where the name is bound,
where what it names reads a reference path, which each draw group and iteration draws
apart;
- `{n}` beside `{n.w}` where `n`'s category reads `w` twice, as in `{w}-{w}` or
`{w.a} {w.b}`;
- a binding in a table's format or cell.
### Draw group
A template may carry `drawGroup` to hold its reference draws apart: every reference path
it renders, however deep short of a `repeat` or a nested `drawGroup`, reads the draw of
that group, and the templates of one category naming one group in one render read one
draw. A name is local to its category, so a category another one references never joins
its groups by name; the unnamed group spans them all.
```json
{ "format": "{payer} pays {payee}; signed {signature}",
"payer": { "format": "{/sv_SE.person.first} {/sv_SE.person.last}", "drawGroup": "payer" },
"payee": "{/sv_SE.person.first} {/sv_SE.person.last}",
"signature": { "format": "{/sv_SE.person.last}", "drawGroup": "payer" } }
```
Renders e.g. `Sara Eriksson pays Ebba Lind; signed Eriksson`: the signature reads the
payer's draw, while the payee is drawn apart. Rejected at load, each naming nothing: a
`drawGroup` of `""` (the default); one naming the draw group its template already draws
in; one on a template that renders no reference path — a bare reference to a
[table](#table) counts, since the group answers for the family it draws in — short of a
`repeat` or a nested `drawGroup`, on a `repeat` itself — each iteration renders in no
draw group — or on an inline template's root, which nothing references. So is a path
reading into a level that carries a `drawGroup`.
### Correlated fields
`{name.field}` reads a path into a sibling, which holds the sibling to one draw
([One draw, one spelling](#one-draw-one-spelling)), so several tokens read one
row — a locality and the postal code that really covers it:
`{name.field}` reads a path into a sibling, drawing it afresh as any `{…}` does. Several
tokens read one draw of it through a [name](#names): bind the sibling, and every read of
the name reads that one row — a locality and the postal code that really covers it:
```json
{ "format": "{street} {int(1,99)}\n{place.postal-code} {place.locality}",
{ "format": "{place as p}{street} {int(1,99)}\n{p.postal-code} {p.locality}",
"street": ["Kungsgatan", "Storgatan"],
"place": [
{ "format": "{locality}", "locality": "Stockholm", "postal-code": "1{digits(2)} {digits(2)}", "weight": 975 },
@@ -827,35 +780,17 @@ row — a locality and the postal code that really covers it:
```
Renders e.g. `Kungsgatan 35` / `176 99 Stockholm`, never a Stockholm code beside
Tranås; each row's `weight` says how often it appears. A path is held at every
level it passes through: `{p.geo.town.name} {p.geo.town.zip}` share the town.
Every variant of a choice on the path must carry the rest of it, so a row missing
a field is named at load:
Tranås; each row's `weight` says how often it appears. A name keeps every level its
reads pass: `{p.geo.town.name} {p.geo.town.zip}` share the town. Every variant of a choice
on the path must carry the rest of it, so a row missing a field is named at load:
```text
token {place.postal-code}: field "place": not every variant of this 2-way choice carries "postal-code"; all carry [locality]
token {p.postal-code}: not every variant of this 2-way choice carries "postal-code"; all carry [locality]
```
The sub-fields stay addressable — `Fake("address.place.locality")` renders, and
`List` advertises it. A path may not read into a level carrying a `repeat`.
### One draw, one spelling
A name any token reads as a path (`{p.first}`) or as an operand (`{calc(net * 2)}`,
`{uppercase(w)}`) is drawn **once per expansion**, a reference path (`{/cat.p.first}`)
**once per render** in its [draw group](#draw-group), and every other route to either — a bare
`{p}`, a second bare `{w}`, `{/cat.net}`, a nested template rendering `{/cat.p.last}`
beside `{p.first}`, a bare `{/cat}` beside `{/cat.p.first}`, at any
depth — is a load error naming the spelling to use. A name nothing reads that way is
drawn each time: `{word} {word}` differs. An expansion is one render of one format, so
each nested template draws its own names again; a render is one `Fake` or one record,
and each `repeat` iteration is an expansion and a render of its own.
```text
token {p} renders a level that {p.first} reads a path into; name the fields you want instead
token {w} is repeated, and uppercase operand "w" holds "w" to one draw per expansion; write {w} once
```
### Performance
The target is about a microsecond per value (goal 13).
@@ -865,9 +800,7 @@ With only the shipped set, `New` loads nothing: a category loads on the first `F
its whole table family. Beside `--data-path`, `WithDataPath` or `WithDataFS`, every
category loads in `New`. Each file is parsed, validated and weight-indexed once, bar a
table's name and parent columns, which are mapped on the first `Fake` that selects by
name or descends through it. Proving the draw fences adds a pass over the loaded tree per
fence, and walks what a render reads only where data holds a reference, so a set that
holds none pays for the passes alone. A `Fake` call then costs about what its output
name or descends through it. A `Fake` call then costs about what its output
costs: an unweighted pick is O(1) whatever the list's length, a weighted one
O(log n), and long formats, deep nesting and many tokens add cost in proportion to
the output.
+50 -152
View File
@@ -11,7 +11,6 @@ a fork on a personal instance. Valid while the project wants contribution from o
the workflow decided nothing, being portable already — `github.api_url` and
`secrets.GITHUB_TOKEN` resolve on either host, so only the names moved.
## The Gitea copy stays, as a pull mirror of GitHub
2026-10-03, decided by the maintainer. Technical principle: one owner per value
@@ -35,11 +34,9 @@ teach the exported API belongs above the clause.
2026-09-02, Lilleman auf Larv.
`format`, `weight`, `repeat`, `separator`, `datatype` and `drawGroup` are reserved;
every other key is a field. Nesting fields under a key, or prefixing options, would tax
every template to guard against a misspelt option.
`todo.md` item 4 deletes `drawGroup`, which this reserves.
`format`, `weight`, `repeat`, `separator` and `datatype` are reserved; every other key
is a field. Nesting fields under a key, or prefixing options, would tax every template to
guard against a misspelt option.
## `{a|b}` stays beside nested choices
@@ -136,18 +133,25 @@ Valid while CI proves the shipped set whole and each category alone.
Works against goal 2.3 until `todo.md` item 77 ends it.
## A bare reference draws each time; a reference path is held
## Every `{…}` draws afresh, and only a name keeps a pick
2026-09-02, Lilleman auf Larv.
2026-10-05, larv-review on the draw restructure the maintainer planned on 2026-10-03.
Serves goals 5.2 and 4.1: each `{…}` makes a new pick, and values that belong together come
from the one pick a name keeps.
Goal 5: `{/p} {/p}` is two draws, as `{word} {word}` is, while every `{/p.first}` in
one render and draw group reads one draw, and a bare `{/p}` beside them is a load error,
as `{p}` beside `{p.first}` is: a bare token is by contract an independent draw, a path
pins its level, and a fresh draw of a pinned level could show another row. A builtin's
operand holds what it reads for its expansion, references included, so
`{uppercase(/p)} {/p}` is one draw, and `{/p}` written twice beside it is a load error.
- `{p.a} {p.b}`, `{w} {uppercase(w)}`, `{/p.first} {/p.last}` and `{/p} {/p}` are two draws
each, and `{p as q}{q.a} {q.b}` is one. Nothing is held per format or per render, so no
fence guards a hold: the load checks on draws are names, paths through names, cycles and
the repeat cap.
- A name binds a reference or a path into a field of the template binding it, `{place as
p}`: an inline template, and a choice of rows inside one category, have no reference to
bind, and goal 4.1 holds there too.
- A name read once is refused where the spelling it binds stands in, and the error names
that spelling (goal 5.7): `{/x as n}{n.y}` is `{/x.y}`. A calc reads no reference or
path, and a read inside a nested repeat keeps one pick on every line, so neither is
refused.
Goal 5.2 overrides this; `todo.md` item 4 replaces it.
Valid while goal 5.2 has each `{…}` make a new pick.
## Reference sigils follow the filesystem
@@ -301,7 +305,9 @@ not agree on one among themselves; each must only hold that type.
The fields an embedded struct promotes are the struct's own — `e.First`, as
`encoding/json` and SQL mappers read them — so they are columns of its record and share
its draws; a tagged field that another field hides is refused, not dropped. A named
its picks: path tags into one category, two or more of them and none selecting a row, read
one pick of it, as a category's record columns read one pick of each name (goal 4.1), while
an inline template in a tag draws afresh as any `{…}` does; a tagged field that another field hides is refused, not dropped. A named
struct field is another entity and a record of its own. `fake:"-"` leaves a struct
field, embedded or named, unfilled, so no name may be `-`; a pointer back to a struct
already being filled is left alone, since filling it would never end. `New` cannot see a
@@ -309,74 +315,15 @@ caller's types, so the first `FakeStruct` for a type compiles its tags and the a
error included, is kept per type: a test's first call is its load, and no `NewStruct`
handle is needed, as the cache already compiles once.
## A render shares one reference draw per category, per group
2026-09-15, Lilleman auf Larv.
Every reference path into a category that selects no row, in one `Fake` or one record,
reads one draw of it, so a value's facts agree across its fields, nested templates and
columns alike — `{/currency.code}` in one field and `{/currency.symbol}` in another name
one currency, whichever view renders them. A `repeat` iteration is a render of its own, since
repeating asks for another entity, and a [draw group](../README.md#draw-group) names further
entities within one render, so a payer and a payee are two groups over one `person`
rather than two copies of it. Only references share: a sibling field is local to its own
expansion, so a `first` column does not silently bind to a `first` in the column next to
it.
Goal 5.2 overrides this; `todo.md` item 4 replaces it.
## A draw group name is local to its category
2026-09-16, Lilleman auf Larv.
A category's groups are its own entities, so a caller naming a group the same way never
joins them by accident, and renaming a group inside one file changes no render
elsewhere. The unnamed group still spans categories, since facts that belong together
across categories must agree.
`todo.md` item 4 deletes what this decides.
## The expansion hold and the render's draws are two fences
2026-09-16, Lilleman auf Larv.
Goal 3: one proves a sibling path or an operand is reached only by its readers within
an expansion, the other that reference paths, whole-table reads and a table family's
rows agree across a render and its draw groups. Their scopes differ and only the second
tracks rows and pins, so one walk would carry both scopes and tell them apart at every
step.
`todo.md` item 4 deletes what this decides.
## A render's draws make their maps on the first read
2026-09-29, Lilleman auf Larv.
Every render starts from empty draws, its draw groups included, since goal 3 prices
a reader meeting two spellings of one empty set. Making the maps where the draws are declared kept a
record's on its frame's stack, but a `Fake`'s draws live on the `Generator`, so there it
cost the cheapest render two heap allocations and 240→390 ns, measured 2026-09-28.
Lazily, a render reading a reference path pays those two allocations, a record or struct
included, and one reading none pays nothing; goal 13 holds either way.
`todo.md` item 4 deletes what this decides.
## A category never references itself, and a record's fences run at load
2026-09-16, Lilleman auf Larv.
A category is one unit: a reference back into it — `{/users.first}` inside `users` —
describes a draw other than the fields beside it, where goal 4 asks facts that belong
together to come from one draw, so `New` refuses it and the sibling path stays the one
spelling for a field of one's own. A value two fields share goes in its own category,
which both reference. That settled, a record's column fences run at `New` too, so a
category that loads renders as whichever shape is asked for, and a reference reaching
back into a category through another one is refused there as the overlap it is. Which
reads those fences weigh differs on purpose: a record-only template's inert format
renders nothing, so a `drawGroup` on it can never matter and is refused, while one on a
rendering format can matter to a caller that bare-references it.
`todo.md` item 4 deletes the `drawGroup` refusal this names.
describes a draw other than the fields beside it, so `New` refuses it and the sibling
field stays the one spelling for a field of one's own. A value two fields share goes in its
own category, which each binds to a name (goal 4.1). That settled, a record's column fences
run at `New` too, so a category that loads renders as a value and as a record alike.
## A record's column set is fixed before the first draw
@@ -404,12 +351,12 @@ Its bounds come from a literal or a call's arguments, so a load error names a re
value, a range check is one comparison, and `1{digits(2)}` is a second spelling of
`{int(100,199)}`.
## A column of one reference alone is the column it reads
## A column of one reference or name read alone is the column it reads
2026-09-15, Lilleman auf Larv.
`{/src.score}` renders exactly what `src.score` draws, so it takes that column's
datatype and null rather than restating them, and a `datatype` restating the one it
`{/src.score}` renders exactly what `src.score` draws, and so does `{s.score}` where `s` is
bound to `/src`, so it takes that column's datatype and null rather than restating them, and a `datatype` restating the one it
takes is a second spelling. Any other `datatype` still types the values — the one way to
type a column someone else wrote.
@@ -489,47 +436,28 @@ 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
accepted one changes, so the door stays open for the address records the plan describes.
## A selected row is fixed, not drawn, and an unnamed read beside it is refused
## A selected row is fixed, not drawn
Decided 2026-09-27 by the maintainer, for goals 4 and 5; valid while a selector names
exactly one row.
- Selected rows never conflict: `{/misc.territory[SE].capital} / {/misc.territory[FI].capital}`
renders `Stockholm / Helsinki`, and two items of one choice may select different rows.
- What a path draws beneath a selected row is one draw per selection, per render and
group, shared by every path through it: `{/geo.SE.municipality[1281].locality.name}`
twice names one locality.
- A read whose path selects no row is refused beside a selection in its family, in the
same render and group, naming the rewrite through each selection and a `drawGroup`:
beside `{/misc.territory[SE].capital}`, a writer reading `{/misc.territory.currency}`
expects `SEK`, so the error names `{/misc.territory[SE].currency}`, since
`Stockholm pays in EUR` would pass review. A second selection does not make the
unnamed read load.
Selected rows never conflict: `{/misc.territory[SE].capital} / {/misc.territory[FI].capital}`
renders `Stockholm / Helsinki`, and two items of one choice may select different rows.
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.
## `..` steps up to the parent row, and a step down after it draws afresh, or once per pick
Goal 5.2 overrides its shared draw beneath a selection and its refusal of an unnamed read; `todo.md` item 4 replaces them.
## `..` steps up to the parent row, and a step down after it draws once per render and draw group, or per pick
2026-10-05, larv-review in PR #158. Serves goal 5.4, which has `..` go up one level. A step
down draws afresh because goal 5.2 has each `{…}` make a new pick, which `todo.md` item 4
brings to every path.
2026-10-05, larv-review in PR #158. Serves goal 5.4, which has `..` go up one level, and goal
5.2, which has each `{…}` make a new pick.
- `..` names the parent table next, `city..country`, as a folder path names the folder it
enters. `.country` after a row stays the link column's cell.
- A step down after a `..` draws afresh inside the row stepped up to (`drawSteps`), and the
draw fences leave it out (`tableReadOf`), since it pins none of the render's rows: the one
exception to [A render shares one reference draw per category, per
group](#a-render-shares-one-reference-draw-per-category-per-group). Paths
stepping down from one `..` share that draw through their memo (`drawMemo.stepDownPins`),
as reference paths share a draw today, so two reads of one step down describe one row.
- A step down after a `..` draws afresh inside the row stepped up to (`drawSteps`). Under a
name, the paths stepping down from one `..` share that draw through the pick's memo
(`drawMemo.stepDownPins`), as every level a name's reads pass is shared, so two reads of
one step down describe one row.
- A route through another row is another path to the same data, not a second spelling:
`city[Oslo]..country.name` and `country[NO].name` both load.
Valid until item 4 makes every `{…}` draw afresh, which revises the shared step down.
## A table selects by one key column, a code, never a free-form name
Decided 2026-09-27 by the maintainer, for goal 5.5: a name may repeat across rows or change
@@ -589,38 +517,14 @@ rows twice, which is what a category over a register with two natural formats as
a TSV nothing names stays a load error, since that one is a file forgotten rather than
shared.
## A table never reaches its own family, by any route
2026-09-17, Lilleman auf Larv.
A `repeat` iteration and a `drawGroup` each draw apart on purpose, but a row that lists
three localities from other regions is the output the family exists to prevent, so the
own-family fence walks through both rather than stopping where the draw fences do.
`todo.md` item 4 deletes what this decides.
## A cell may read a reference, and each row its own
2026-09-29, Lilleman auf Larv.
Goal 4: facts that belong together come from one draw, so a shop row's `phone` cell reads
`{/sv_SE.phone}` on a Swedish shop and `{/en_US.phone}` on a US one, and the table's
format, the same for every row, cannot say that. A restructure for goal 3 keeps the fences
that prove such cells safe. Valid while a table's rows need generators of their own.
`todo.md` item 4 deletes the fences this keeps; a cell may still read a reference.
## Tables carrying token cells stay small
2026-09-17, Lilleman auf Larv.
The family fence compares the reads of every pair of rows that can render together, so a
table whose every row's cell selects a row of another table loads in time quadratic in
its rows: about a second at four thousand rows. No shipped table carries such cells, and
a register is a column set rather than a set of references, so the fence is left as it
is until a real data set needs the indexed form.
`todo.md` item 4 deletes what this decides.
format, the same for every row, cannot say that. Valid while a table's rows need
generators of their own.
## A path is walked once without drawing before it is walked for real
@@ -804,8 +708,8 @@ the register gives each one's state. A second table of the 195 sovereigns would
`DK` row beside the territory `DK` row, both carrying Denmark's capital, currency, flag
and TLD — two owners for one fact, drifting at the next import. Splitting the columns to
avoid that is worse: put `capital` on the territory alone and a country row can no
longer name Copenhagen. The column cannot be a `parent`, since a table never reaches its
own family; a test proves every value names a row instead. A territory the register
longer name Copenhagen. The column cannot be a `parent`, since a table is never its own
parent; a test proves every value names a row instead. A territory the register
records no state for stands alone, which is `EH` alone, and naming one for it would be a
claim fejkdata has no business making.
@@ -910,22 +814,16 @@ it.
The scope is lexical. A read entering a category, by a reference, through a name or from
`Fake`, sees no frame (one render of a scope's picks) opened above it, so a category renders
the same whoever references it. A reference path, a read through a name or `Fake` opens the
frames around where it lands from its own memo, so reads sharing a memo, `{n}` and `{n.path}`
alike, read one pick; a bare reference lands on a category's root, which opens fresh ones. A reference path's
memo lasts per render and draw group, a name's per pick, and `Fake("cat.field")`'s per render.
the same whoever references it. It opens the frames around where it lands: a read through a
name from its pick's memo, so `{n}` and `{n.path}` read one pick of each name inside, and a
reference or `Fake` fresh ones.
A pick keeps only the levels a read of the name addresses, so a template draws the same way
under a name as anywhere else (goal 5.2). A reference read under a pick draws in the draw group
of the read of the name, the group the draw fences survey it in, so two names bound to one
category share what it reads by reference path. Where what a name names reads a reference
path, its reads stay in one draw group and outside any repeat nested where it is bound: a reference path is held per
draw group and per iteration, so what the pick kept and what it renders afresh would come from
two draws of it. `todo.md` item 4 ends both: once every `{…}` draws afresh, nothing is held for
the render.
under a name as anywhere else (goal 5.2). A read of a name bound to a field stays in the
category's frames, since the field sits inside it.
Valid while names are read only inside the category binding them.
## comprehension floor: every dimension and the overall at 7.0 or above; items 4 and 90 ship with no scoring run, and item 5 scores them
## comprehension floor: every dimension and the overall at 7.0 or above; the draw restructure and item 90 ship with no scoring run, and item 5 scores them
2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat, and the hardest units are the draw fences item 4 deletes. Items 4 and 90 delete or move what the earlier chunks of the below-floor round touched, so a scoring run before `todo.md` item 90 merges would score code about to go; the calc change accepting a name as an operand shipped unscored on 2026-10-05 for that reason. Items 4 and 90 finish the round `AGENTS.md`'s comprehension rule lets merge below the floor, a restructure the maintainer approved. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until `todo.md` item 5 runs.
2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat, and the hardest units were the draw fences the restructure deleted. The draw restructure, shipped 2026-10-05, and `todo.md` item 90 delete or move what the earlier chunks of the below-floor round touched, so a scoring run before item 90 merges would score code about to go; the calc change accepting a name as an operand shipped unscored on 2026-10-05 for that reason. The two finish the round `AGENTS.md`'s comprehension rule lets merge below the floor, a restructure the maintainer approved. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until `todo.md` item 5 runs.
+1 -2
View File
@@ -240,8 +240,7 @@ func tagValue(sf reflect.StructField, tag string) (any, string, error) {
}
// bindShared has the fields whose paths read into one category, two or more of them and none
// selecting a row, read one pick of it through a name, as a record's columns read one pick:
// docs/decisions.md#a-structs-path-tags-into-one-category-read-one-pick-of-it
// selecting a row, read one pick of it through a name, as a record's columns read one pick.
func bindShared(root *folder, tags map[string]any, paths map[string]string) {
byCategory := map[string][]string{}
for _, field := range sortedNames(paths) {
+7 -23
View File
@@ -70,7 +70,6 @@
| 61 | 0.1.0 | | **Add a locale, with its `geo/` tree, for each of the ten most-spoken languages and each Nordic country.** | 4 | 9 | 9 | 10 | 2.1 | 30.8 |
| 16 | 0.1.0 | | **Add the remaining locale categories: company, phone, finance, vehicle, words.** | 3 | 8 | 8 | 9 | 2.2, 15 | 28.1 |
| 15 | 0.1.0 | | **Add the remaining `misc` tables and templates, one row of its detail's table per chunk.** | 3 | 9 | 8 | 8 | 2, 15, 14 | 25.9 |
| 4 | 0.1.0 | decision | **Draw every `{…}` afresh, keep a pick only by name, and delete `drawGroup` with the fences that held picks implicitly.** | 7 | 8 | 8 | 10 | 5.2, 4.1 | 24.0 |
| 57 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.5 |
| 34 | 0.1.0 | | **Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.** | 2 | 4 | 6 | 7 | 4 | 20.9 |
| 5 | 0.1.0 | decision | **Run the nine-seat comprehension panel after the draw restructure, and file what it names.** | 1 | 3 | 3 | 9 | 3 | 20.4 |
@@ -78,7 +77,6 @@
| 89 | 0.1.0 | | **Fail `New` without `WithSeed`, naming the option, and seed the CLI from the system.** | 3 | 3 | 6 | 7 | 10.1 | 16.0 |
| 90 | 0.1.0 | decision | **Restructure the code into folders named for what they hold: the public API stays in the root package, and the implementation moves into `internal/` packages.** | 6 | 9 | 3 | 9 | 3.2, 3.4 | 13.9 |
| 79 | 0.1.0 | decision | **Beside a `--data-path`, load in `New` the user's categories, the shipped ones they override or read, and the shipped ones whose reads reach an overridden one; load the rest on first reach.** | 6 | 6 | 7 | 8 | 13 | 12.9 |
| 87 | 0.1.0 | | **Split the README's last Records paragraph into one paragraph per rule, name the two shapes "either shape" means, and define or replace "a field hold".** | 1 | 2 | 4 | 6 | 12, 12.3 | 11.9 |
| 58 | 0.2.0 | | **Ship prebuilt binaries, a container image, and packages for Homebrew, Scoop and the biggest Linux package managers, so the CLI needs no Go.** | 3 | 7 | 8 | 9 | 1.1, 7 | 29.2 |
| 26 | 0.2.0 | defect | **Spell `sv_SE.personnummer` with the `+` Skatteverket uses from the year the holder turns 100, judged by a date the caller gives.** | 4 | 6 | 7 | 8 | 4 | 22.1 |
| 81 | 0.2.0 | decision | **Refuse at load every `calc` operand not proven numeric and every divisor not proven nonzero, in a string column too.** | 4 | 4 | 6 | 8 | 6.1, 4 | 20.6 |
@@ -225,16 +223,6 @@ Shape: T = table, t = template, c = choice.
| `lorem`, `hacker`, `hipster`, `catchphrase`, `buzzword`, `quote` | T/c | lorem ipsum, LLM-written | — |
| `direction`, `continent`, `ulid` | c/t | — | — |
### 4. Draw every `{…}` afresh, keep a pick only by name, and delete `drawGroup` with the fences that held picks implicitly.
- After it, `{/person} ({/person.first})` renders two people, as does `{/sv_SE.person as a}{/sv_SE.person as b}{a} & {b}`. `{w} {uppercase(w)}` renders `b G` where `w` is a choice of letters, and `{/city.name}, {/country[SE].name}` and `{/country[SE].name} / {/country[NO].name}` both load.
- Deletes the token-order rules (`drawsApart`, `readFold`, `branches`, the pairwise replay), the expansion hold fence, `drawGroup`, the own-family fence and the cell-reference fences, and revises the decisions that point here. The remaining draw checks are names, paths through names, cycles and the repeat cap.
- Fixes on the way: `{/sel}|{/sel}` panics out of `Fake` today where two draws land on rows whose cells select different rows of another table; it must render.
- Rewrites the README's Computation, References, Draw group, Correlated fields, Linked tables, Names and "One draw, one spelling" sections; the Linked tables section says which pick's weights govern a family, since the bound row decides.
- Keeps a name read once where no reference can stand, such as `{/score as s}{calc(s * 2)}` over a numeric category `score`, loading. Widens the refusal of a name read once to every binding, a path into it or a reference path bound included, since `{n.x}` read once is then `{/ref.x}`, and deletes the names fence over draw groups and nested repeats (`checkUses`, `readsHeld`).
- Say how a record's columns and a struct's tags read one pick, so the `first` and `sex` columns of one `sv_SE.person` record, or one struct's tags reading them, still describe one person.
- Re-pin seeded output and `testdata/shipped_shape.txt` in their own commits; the CHANGELOG names the grammar change.
### 57. Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.
Nothing has shipped, so "no longer paths", "where it used to fail" and "where it used to be an empty string" describe versions no reader can have installed.
@@ -245,11 +233,11 @@ Nothing has shipped, so "no longer paths", "where it used to fail" and "where it
### 5. Run the nine-seat comprehension panel after the draw restructure, and file what it names.
Needs items 4 and 90. The decision "comprehension floor: every dimension and the overall at 7.0 or above; items 4 and 90 ship with no scoring run, and item 5 scores them" suspends every scoring run and panel until then.
Needs item 90. The decision "comprehension floor: every dimension and the overall at 7.0 or above; the draw restructure and item 90 ship with no scoring run, and item 5 scores them" suspends every scoring run and panel until then.
### 91. Accept a path through a name as a `calc` operand, so `{/misc.coordinate as c}{c}: {calc(c.lat * 60)}` computes from the coordinate `{c}` prints.
Needs item 4. A calc operand parses as a bare identifier, so `{calc(c.lat * 60)}` fails with `unexpected ".lat * 60"`, and a second binding of `/misc.coordinate.lat` would draw another coordinate. Item 4 removes today's route, a field holding the reference path, which the expansion hold keeps for the render. Resolve the path through `linkName`'s leaves, as `operandNodes` does for a bare name.
A calc operand parses as a bare identifier, so `{calc(c.lat * 60)}` fails with `unexpected ".lat * 60"`, and a second binding of `/misc.coordinate.lat` would draw another coordinate. Resolve the path through `linkName`'s leaves, as `operandNodes` does for a bare name.
### 89. Fail `New` without `WithSeed`, naming the option, and seed the CLI from the system.
@@ -257,7 +245,7 @@ Goal 10.1 has the caller supply the seed, and today `New` without `WithSeed` see
### 90. Restructure the code into folders named for what they hold: the public API stays in the root package, and the implementation moves into `internal/` packages.
Needs item 4, and comes before item 5's panel. Packages under `internal/` leave the API in one package: no other module can import them. A package exports only what another package uses. It takes in two file moves the nine-seat panel asked for: `binding`, `bind` and `checkNodeFences` out of `data.go`, and `table.route`, `selector`, `step` and `drawStep` out of `path.go`. The systems-architect proposes the folders and their names, and the maintainer approves them before code moves.
Comes before item 5's panel. Packages under `internal/` leave the API in one package: no other module can import them. A package exports only what another package uses. It takes in two file moves the nine-seat panel asked for: `binding`, `bind` and `checkNodeFences` out of `data.go`, and `table.route`, `selector`, `step` and `drawStep` out of `path.go`. The systems-architect proposes the folders and their names, and the maintainer approves them before code moves.
### 79. Beside a `--data-path`, load in `New` the user's categories, the shipped ones they override or read, and the shipped ones whose reads reach an overridden one; load the rest on first reach.
@@ -265,10 +253,6 @@ Needs item 77, which makes an override fail unless it says it replaces.
The decision "With only the shipped set, a category loads on the first call reaching it; beside a `--data-path`, every category loads in `New`" parses the whole shipped set on every `--data-path` run, about 45 ms today and seconds once items 61 and 78 land. Goal 6 needs only the user's categories, the shipped ones they override, the shipped ones they read, and the shipped ones whose reads reach an overridden category, so the index would carry each category's reads.
### 87. Split the README's last Records paragraph into one paragraph per rule, name the two shapes "either shape" means, and define or replace "a field hold".
Needs item 4, which rewrites the draw-group rule this paragraph states. The last paragraph under Records, from "A record written only to emit columns" to the Decisions link, holds seven rules, so "put a value two fields share in its own category" is hard to find. "Renders as either shape" names no shape nearby, and the README defines no "field hold".
### 58. Ship prebuilt binaries, a container image, and packages for Homebrew, Scoop and the biggest Linux package managers, so the CLI needs no Go.
GoReleaser attaches the binaries to the release the tag workflow publishes, and builds deb and rpm packages, an Alpine APK and an AUR package beside Homebrew and Scoop. A checkout build prints `devel` for `--version`; the binaries carry the stamped tag. v0.1.0 ships first, so it can be tested before it is packaged.
@@ -310,7 +294,7 @@ With several `--data-path` layers the author has to work out which directory won
### 28. Accept a middle name, and draw a `personnummer` whose sex matches a sex selected through a named pick.
Needs item 4.
Today `personnummer` draws its own sex, so a person bound to `{/sv_SE.sex[f] as s}` needs the template the README's Data section shows to agree with a number.
### 24. Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does not hold.
@@ -342,7 +326,7 @@ Neither names a `weight`, so a bare `sex` draws evenly.
### 80. Walk a path through one per-step function that both `pathCheck.walk` and `probePath` call.
The decision "A path draws through its compiled steps" keeps two loops over one rule for passing a choice, a table or a field, and they have drifted: `walk` refuses a `repeat` or `drawGroup` level through `enter`, `probePath` does not (item 48). Technical principle: DRY.
The decision "A path draws through its compiled steps" keeps two loops over one rule for passing a choice, a table or a field, and they have drifted: `walk` refuses a `repeat` level through `enter`, `probePath` does not (item 48). Technical principle: DRY.
### 50. Reword `prove`'s refusal of a typed column reading a row, `{/region}`.
@@ -402,7 +386,7 @@ An `int64` or `float64` field unproven in range is told to become itself, and a
### 10. Test that every node kind reaches each switch over node kinds.
Needs item 4; recount the switches then. Today ten, from `render` to `columnItems`, must agree, and only `render` and `renderEdges` say so.
Today nine, from `render` to `columnItems`, must agree, and only `render` and `renderEdges` say so.
### 11. Test that every `geo` tree holds the five table names and the `address` columns that port across countries.
@@ -434,7 +418,7 @@ Both phone formats carry the same 21-code `area` list, so an edit to one copy ca
### 7. Fill `arm` in one place.
`splitArm` runs before the link and again after it, and `compileArm` finishes it from `reference.go`, so five seats traced three phases. Needs item 4.
`splitArm` runs before the link and again after it, and `compileArm` finishes it from `reference.go`, so five seats traced three phases.
### 37. Rename `newRand` to `newGeneratorState` and `Generator.rand` to `state`.