Reshape todo.md into the scored table, planning the draw restructure first

This commit is contained in:
2026-10-03 00:52:57 +02:00
committed by Lillem4n
parent 83c2a4758c
commit c3d47e5b21
2 changed files with 357 additions and 332 deletions
+20
View File
@@ -114,6 +114,8 @@ pins its level, and a fresh draw of a pinned level could show another row. A bui
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.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## Reference sigils follow the filesystem
2026-09-02, Lilleman auf Larv.
@@ -291,6 +293,8 @@ rather than two copies of it. Only references share: a sibling field is local to
expansion, so a `first` column does not silently bind to a `first` in the column next to
it.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## A draw group name is local to its category
2026-09-16, Lilleman auf Larv.
@@ -300,6 +304,8 @@ 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.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## The expansion hold and the render's draws are two fences
2026-09-16, Lilleman auf Larv.
@@ -310,6 +316,8 @@ rows agree across a render and its draw groups. Their scopes differ and only the
tracks rows and pins, so one walk would carry both scopes and tell them apart at every
step.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## A render's draws make their maps on the first read
2026-09-29, Lilleman auf Larv.
@@ -321,6 +329,8 @@ cost the cheapest render two heap allocations and 240→390 ns, measured 2026-09
Lazily, a render reading a reference path pays those two allocations, a record or struct
included, and one reading none pays nothing; goal 11 holds either way.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## A category never references itself, and a record's fences run at load
2026-09-16, Lilleman auf Larv.
@@ -473,6 +483,8 @@ exactly one row.
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.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## A link column after a row steps up to the parent row, and a path may not end on it
Decided 2026-09-27 by the maintainer, for goal 4; valid while a link column holds the
@@ -501,6 +513,8 @@ Checked against three simulated template writers from the Audience: all three re
parent row where a segment followed, and they split between the code and the name where
the path ended.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 2 ends it.
## A table selects by one key column, a code, never a free-form name
Decided 2026-09-27 by the maintainer, for goal 4's one spelling per result; valid while
@@ -567,6 +581,8 @@ A `repeat` iteration and a `drawGroup` each draw apart on purpose, but a row tha
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.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## A cell may read a reference, and each row its own
2026-09-29, Lilleman auf Larv.
@@ -576,6 +592,8 @@ Goal 3: facts that belong together come from one draw, so a shop row's `phone` c
format, the same for every row, cannot say that. A restructure for goal 2 keeps the fences
that prove such cells safe. Valid while a table's rows need generators of their own.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## Tables carrying token cells stay small
2026-09-17, Lilleman auf Larv.
@@ -586,6 +604,8 @@ its rows: about a second at four thousand rows. No shipped table carries such ce
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.
Works against goal 4's every-`{…}`-is-a-new-pick aims until `todo.md` item 4 ends it.
## A path is walked once without drawing before it is walked for real
2026-09-17, Lilleman auf Larv.
+337 -332
View File
@@ -1,149 +1,261 @@
# Plan
# todo
Ordered as the releases that ship it.
## Scoring
## v0.1.0
`Score = -R - S/4 + 2*A + 2*G*W`
### Comprehension, until the panel scores 7.0
`Bar = 9`
Nothing else ships while the score is under 7.0 — no feature, no category, no data — bar
what `AGENTS.md` excepts, per README goal 2. Nine rounds of the nine-seat findings held
the score between 5.7 and 6.1, so this round is a restructure the maintainer approved on
2026-09-29: a PR per item, then the nine-seat run again. The run is the nine-seat one,
since the four-seat scoring run names too little to steer an architectural change.
`Next ID = 62`
Scores, newest last: 5.9 on 2026-09-20 (Navigation 7.0, Locality 5.2, Shape 5.9,
Self-sufficiency 5.7); 5.8 on 2026-09-22 (Navigation 6.7, Locality 5.0, Shape 5.9,
Self-sufficiency 5.6); 5.7 on 2026-09-26 (Navigation 6.7, Locality 5.0, Shape 5.8,
Self-sufficiency 5.6), every seat capped by Locality, and all nine naming
`drawWalk.walk` among the hardest; 5.7 on 2026-09-28 (Navigation 6.7, Locality 5.1,
Shape 5.8, Self-sufficiency 5.7), every seat capped by Locality, all nine naming
`drawWalk.walk` and the four path walks among the hardest; 5.8 on 2026-09-29
(Navigation 6.8, Locality 5.3, Shape 5.8, Self-sufficiency 5.7), seven seats capped
by Locality, all nine naming `drawWalk.walk` hardest and eight the unit they would
least modify; 5.7 on 2026-09-29 again (Navigation 6.6, Locality 5.2, Shape 5.9,
Self-sufficiency 5.8), four seats capped by Locality, six of the seven unit seats
ranking `readSurvey.walk` hardest and all seven the unit they would least modify; 6.1
on 2026-09-29 a third time (Navigation 7.0, Locality 5.6, Shape 6.3, Self-sufficiency
6.1), every seat's lowest dimension Locality, all seven unit seats ranking the
`readSurvey` walk or check hardest and the unit they would least modify; 5.8 on
2026-09-29 a fourth time (Navigation 6.7, Locality 5.0, Shape 5.7, Self-sufficiency
5.7), every seat's lowest dimension Locality, alone or tied, all seven unit seats
ranking `readSurvey.walk` and `edge` hardest and the unit they would least modify; 5.8
on 2026-09-29 a fifth time (Navigation 6.9, Locality 5.2, Shape 6.1, Self-sufficiency
5.8), every seat's lowest dimension Locality, alone or tied, all seven unit seats ranking
`readSurvey.walk` hardest and the unit they would least modify; 5.7 on 2026-09-30, the
checkpoint (Navigation 6.8, Locality 5.2, Shape 5.9, Self-sufficiency 5.7), every seat's
lowest dimension Locality, alone or tied, all seven unit seats ranking `drawsApart` or
`readFold.reads` hardest and five `drawsApart` the unit they would least modify. The
maintainer chose on 2026-09-30 to go on with the path steps regardless; 5.7 on
2026-09-30 after them (Navigation 7.0, Locality 5.1, Shape 5.9, Self-sufficiency 5.7),
every seat's lowest dimension Locality, alone or tied, six of the seven unit seats
ranking `drawsApart` hardest and all seven the unit they would least modify.
| Goal | W |
|---|---|
| 1 | 1.00 |
| 1.1 | 0.97 |
| 2 | 0.94 |
| 2.1 | 0.91 |
| 2.2 | 0.88 |
| 2.3 | 0.85 |
| 2.4 | 0.82 |
| 3 | 0.79 |
| 3.1 | 0.76 |
| 4 | 0.74 |
| 4.1 | 0.71 |
| 4.2 | 0.68 |
| 4.3 | 0.65 |
| 4.4 | 0.62 |
| 4.5 | 0.59 |
| 4.6 | 0.56 |
| 4.7 | 0.53 |
| 4.8 | 0.50 |
| 5 | 0.47 |
| 6 | 0.44 |
| 6.1 | 0.41 |
| 6.2 | 0.38 |
| 7 | 0.35 |
| 8 | 0.32 |
| 9 | 0.29 |
| 10 | 0.26 |
| 10.1 | 0.24 |
| 10.2 | 0.21 |
| 10.3 | 0.18 |
| 11 | 0.15 |
| 12 | 0.12 |
| 12.1 | 0.09 |
| 13 | 0.06 |
| 14 | 0.03 |
This round, in order:
## Items
- **Take the maintainer's call on the items below**: the approved restructure is done and
left the score at 5.7, so the round stops here until they decide.
- **Split `drawsApart` into one named check per refusal, and show at `conflict` why pairs
find what a replay would**: all seven unit seats would least modify it, holding four
refusals over `landsRow`, `familyRoot`, `drawnOf` and `selected` at once, and two opened
`TestPairsFindWhatAWholeReplayFinds` to believe the pairs claim.
- **Walk a path in one function**: `probePath` copies `pathCheck.walk`'s loop and
`routeSteps` call, differing at a choice and in skipping `enter`, and seven seats and
both architects named the pair.
- **Name what `branches` and `pinBelow` hold and drop**: six seats ranked
`readFold.reads` second, decoding `pins` against `wholePins` through `doc.go` and
finding `pinBelow`'s silent `continue` only by tracing.
- **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 and both
architects found format compilation under `linkTemplate`.
- **Rename `holdfence.go`'s `cover`, and say at `heldNodes` why a path hold and an
operand hold answer for different nodes**: `arm.cover` means something else, and six
seats held both walks beside `renderEdges` to see why they differ.
- **Move `binding`, `bind` and `checkNodeFences` out of `data.go` into a file of their
own, stating there that `checkOwnFamily` runs before the draw surveys**: the greenfield
architect found the pipeline last, and the order is stated only at `rowReads`.
- **Move `table.route`, `selector`, `step` and `drawStep` from `path.go` to `table.go`**:
the inherited architect's 3am trace crossed four files for one draw.
- **Test that every node kind reaches each switch over node kinds**: ten switches, from
`render` to `columnItems`, must agree, and only `render` and `renderEdges` say so.
- **Rename the load-time read fold apart from the render's reads**: `reads.go`, `pathRead`
and `readFold` share a word with `readField` and `readValue`, and `graph.go` holds
`renderEdges`, the edge model every fence walks.
- Run the nine-seat panel again and file what it names here.
| ID | Release | Exempt | Item | R | S | A | G | Goals | Score |
|---|---|---|---|---|---|---|---|---|---|
| 51 | 0.1.0 | | **Ship prebuilt binaries, a container image, Homebrew and Scoop, so the CLI needs no Go.** | 3 | 6 | 8 | 9 | 1.1, 6 | 29.0 |
| 1 | 0.1.0 | | **Bind a pick to a name with `{x as n}`, read it with `{n}` and `{n.path}`, and refuse a name bound twice.** | 4 | 6 | 8 | 9 | 4.3, 3.1 | 24.2 |
| 3 | 0.1.0 | | **Rewrite the shipped categories so facts that belong together come from one named pick.** | 5 | 5 | 8 | 9 | 3.1 | 23.4 |
| 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 | 4.2, 3.1 | 22.2 |
| 24 | 0.1.0 | defect | **Draw `en_US.phone`'s `exch` as a NANP central office code, and assert the rule in `TestShippedUSPhone`.** | 2 | 2 | 6 | 8 | 3 | 22.1 |
| 50 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.1 |
| 5 | 0.1.0 | | **Run the nine-seat comprehension panel after the draw restructure, and file what it names.** | 1 | 3 | 3 | 9 | 2 | 21.2 |
| 25 | 0.1.0 | defect | **Give a `sv_SE.personnummer` over 100 the `+` separator Skatteverket spells, or stop drawing birthdates that reach 100.** | 2 | 2 | 5 | 8 | 3 | 20.1 |
| 32 | 0.1.0 | | **Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.** | 2 | 4 | 6 | 7 | 3 | 20.1 |
| 13 | 0.1.0 | defect | **Strip the whitespace `data-import/territory.py` copies from its register.** | 1 | 1 | 5 | 7 | 3 | 19.8 |
| 6 | 0.1.0 | question | **Decide how the comprehension gate treats panel noise: identical code scored 5.7 and 5.4.** | 1 | 1 | 3 | 8 | 2 | 19.8 |
| 2 | 0.1.0 | decision | **Step up from a row to the row it links to with `..`: `{/city[Oslo]..country.name}` renders `Norway`, and `city[Oslo].country` stays the cell `NO`.** | 4 | 5 | 7 | 8 | 4.4 | 18.7 |
| 23 | 0.1.0 | defect | **Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does not hold.** | 1 | 1 | 4 | 7 | 3 | 17.8 |
| 27 | 0.1.0 | | **Accept a middle name, and draw a `personnummer` whose sex matches a selected sex, by name.** | 4 | 4 | 6 | 7 | 3.1 | 17.6 |
| 47 | 0.1.0 | | **Let a table column carry a `datatype`, so `--format json` writes `"safe": true` and `--format sql` a boolean.** | 4 | 5 | 6 | 6 | 3 | 16.2 |
| 19 | 0.1.0 | | **Give the address records one column set across countries: `region` and `municipality` as columns on `geo.SE.address` too.** | 4 | 4 | 6 | 6 | 4 | 15.9 |
| 48 | 0.1.0 | defect | **Read the tag through `git/ref/tags/{tag}` in `publish_release.py`.** | 2 | 1 | 3 | 6 | 1.1 | 15.4 |
| 46 | 0.1.0 | | **Name the file a link error comes from beside its category path, `sub.x (/d/a/sub/x.json): …`.** | 1 | 2 | 5 | 7 | 5 | 15.1 |
| 34 | 0.1.0 | | **Report every mistake a load finds in one error, as the decision "A load reports every mistake at once" states.** | 5 | 6 | 7 | 8 | 5 | 15.0 |
| 20 | 0.1.0 | defect | **Weight `sv_SE.sex` and `en_US.sex` by bearers, as the README's Data section says they are.** | 2 | 2 | 4 | 6 | 3 | 15.0 |
| 38 | 0.1.0 | | **Key every table by one key column, as the decision "A table selects by one key column, a code, never a free-form name" states.** | 6 | 7 | 7 | 8 | 4.7 | 14.7 |
| 18 | 0.1.0 | | **Give every shipped category building-block columns, and one record shape across the national ids; re-pin the shape.** | 5 | 6 | 6 | 6 | 4 | 14.4 |
| 43 | 0.1.0 | defect | **Refuse a `Fake` path into a level carrying a `repeat`, as the README's Correlated fields section states.** | 2 | 2 | 4 | 6 | 4 | 14.4 |
| 49 | 0.1.0 | | **Document `NewRecordTemplate`, `ErrNoColumns`, `ErrNoData` and `MaxRepeat` in the README's Library section.** | 1 | 2 | 6 | 7 | 10 | 14.1 |
| 28 | 0.1.0 | | **Let a path go from a selected row into the template beside its family: `geo.US.locality[1714000].address`.** | 4 | 4 | 5 | 6 | 4 | 13.9 |
| 30 | 0.1.0 | | **Add `misc.browser` as the parent of `misc.useragent`, so `misc.browser[Chrome].useragent` resolves.** | 3 | 3 | 4 | 6 | 3.1 | 13.4 |
| 7 | 0.1.0 | | **Walk a path in one function: fold `probePath` into `pathCheck.walk`.** | 4 | 4 | 3 | 7 | 2.2 | 13.3 |
| 14 | 0.1.0 | defect | **Place xlsx cells by their `r` reference in `data-import/xlsx.py`.** | 2 | 2 | 3 | 6 | 3 | 13.0 |
| 11 | 0.1.0 | | **Test that every node kind reaches each switch over node kinds.** | 1 | 3 | 2 | 6 | 2.2 | 12.8 |
| 16 | 0.1.0 | | **Add the remaining `misc` tables and templates, one row of the table below per chunk.** | 3 | 9 | 8 | 7 | 13, 12 | 12.4 |
| 12 | 0.1.0 | | **Test that every `geo` tree holds the five table names and the `address` columns that port across countries.** | 1 | 2 | 3 | 5 | 3 | 12.4 |
| 45 | 0.1.0 | | **Reword `prove`'s refusal of a typed column reading a row, `{/region}`.** | 1 | 2 | 4 | 6 | 5 | 12.1 |
| 17 | 0.1.0 | | **Add the remaining locale categories: company, phone, finance, vehicle, words.** | 3 | 8 | 8 | 7 | 13 | 11.8 |
| 9 | 0.1.0 | | **Move `binding`, `bind` and `checkNodeFences` out of `data.go` into a file of their own.** | 1 | 2 | 2 | 5 | 2.1 | 11.6 |
| 10 | 0.1.0 | | **Move `table.route`, `selector`, `step` and `drawStep` from `path.go` to `table.go`.** | 1 | 2 | 2 | 5 | 2.1 | 11.6 |
| 29 | 0.1.0 | | **Rename `misc.territory.country` to `sovereign`.** | 3 | 2 | 4 | 7 | 4.8 | 11.5 |
| 39 | 0.1.0 | | **Refuse a struct tag's `datatype` naming the Go type that sets it before proving its values, as the README's Library section promises.** | 2 | 2 | 4 | 6 | 5 | 11.1 |
| 26 | 0.1.0 | | **Merge `en_US.ip` and `sv_SE.ip`, today byte-identical, into one `misc.ip`.** | 3 | 2 | 4 | 6 | 4.7 | 10.9 |
| 15 | 0.1.0 | | **Spell `misc.creditcard`'s digit runs `{digits(n)}`, and refuse a repeat over a lone `{digits(1)}` at `New`, naming that spelling.** | 3 | 3 | 4 | 6 | 4.7 | 10.6 |
| 22 | 0.1.0 | | **Give `url` and `email` a path that draws only domains nobody can register, keeping the wide set as the default.** | 3 | 4 | 7 | 8 | 14 | 10.5 |
| 36 | 0.1.0 | defect | **Compare `calcParser.binary`'s operator as a rune.** | 1 | 1 | 3 | 6 | 5 | 10.4 |
| 41 | 0.1.0 | | **Name the node a selector follows in `stepInto`'s refusal.** | 1 | 1 | 3 | 6 | 5 | 10.4 |
| 44 | 0.1.0 | | **Report a CRLF rows file holding only its header as having no rows.** | 1 | 1 | 3 | 6 | 5 | 10.4 |
| 31 | 0.1.0 | | **Group `data/misc` into folders where a group name makes a path easier to guess.** | 4 | 5 | 4 | 5 | 4 | 10.2 |
| 37 | 0.1.0 | | **Stop the `columnKinds` suggestion naming a field's own kind or a narrower one.** | 1 | 2 | 3 | 6 | 5 | 10.1 |
| 42 | 0.1.0 | | **Report the same error every load for a table with two bad options, and for a folder with two unnamed rows files.** | 2 | 2 | 3 | 7 | 5, 8 | 10.1 |
| 33 | 0.1.0 | | **Add `{base64url(n)}` for JWT shapes.** | 1 | 2 | 4 | 5 | 7 | 10.0 |
| 21 | 0.1.0 | | **List `title` in both locales, and `sv_SE`'s `birth-number`, in the README's Data list of what each locale carries.** | 1 | 1 | 4 | 6 | 10 | 9.9 |
| 8 | 0.1.0 | | **Fill `arm` in one place.** | 4 | 4 | 2 | 6 | 2.2 | 9.6 |
| 35 | 0.1.0 | | **Rename `newRand` to `newGeneratorState` and `Generator.rand` to `state`.** | 1 | 1 | 1 | 5 | 2.3 | 9.2 |
| 40 | 0.1.0 | | **Call `templateError` a compile failure in its doc.** | 1 | 1 | 1 | 5 | 2.3 | 9.2 |
| 55 | 0.2.0 | | **Pair a street with its exact postnummer.** | 3 | 6 | 5 | 8 | 3.1 | 17.7 |
| 54 | 0.2.0 | | **Add `{btc()}` and `{eth()}`.** | 2 | 4 | 4 | 6 | 3 | 14.5 |
| 52 | 0.2.0 | | **Promise in `Generator`'s godoc that its renders run one at a time.** | 1 | 1 | 5 | 5 | 10 | 11.3 |
| 58 | 0.2.0 | | **Cut the README's Layout block to the lines that say what a file name cannot.** | 1 | 1 | 2 | 5 | 2.4 | 10.9 |
| 53 | 0.2.0 | | **Add the locales nb_NO, da_DK, fi_FI, de_DE, en_GB, nl_NL, fr_FR and es_ES, and their `geo/` trees.** | 4 | 9 | 8 | 7 | 13 | 10.6 |
| 59 | 0.2.0 | | **Cut `AGENTS.md` to the rules only it states, and explain every term its comprehension rule uses.** | 1 | 2 | 1 | 6 | 2.4 | 10.3 |
| 57 | 0.2.0 | | **Hold every change `AGENTS.md` says owes a `CHANGELOG.md` entry to one in CI.** | 2 | 3 | 4 | 5 | 4.8 | 10.2 |
| 56 | 0.2.0 | | **Give every entry in `docs/decisions.md` the goal it serves, its date, who made it and the premise it rests on.** | 1 | 4 | 1 | 6 | 2.4 | 9.8 |
| 60 | 1.0.0 | | **Announce v1.0.0 where a developer choosing a fake-data tool already reads, with a README first screen for someone deciding in a minute.** | 1 | 3 | 7 | 9 | 1.1 | 29.7 |
| 61 | 1.0.0 | | **Publish a homepage with an in-browser generator, the library compiled to WebAssembly.** | 3 | 6 | 6 | 6 | 1.1 | 19.1 |
### Data
## Details
Shape: T = table, t = template, c = choice. Read every factual list from a register
by a `data-import/` script, as README goal 12 asks.
### 51. Ship prebuilt binaries, a container image, Homebrew and Scoop, so the CLI needs no Go.
- Fetch NVDB through `source.fetch` in `data-import/geo-se.py`: `nvdb_segments` calls
urllib itself, with no retry and no User-Agent.
- Test that every `geo` tree holds the five table names and the `address` columns the
README says port across countries: nothing checks it, and a new country breaks it silently.
- 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
empty cell shifts every later column left.
- Spell `misc.creditcard`'s digit runs `{digits(n)}`, and refuse a repeat over a lone
`{digits(1)}` at `New`, naming that spelling: both render the same run.
- Add the remaining `misc` tables and templates, one row of the table below per chunk.
- Read `misc.emoji` from the Unicode `emoji-test.txt` register, and `misc.car` from an
international make and model source; vPIC and Mobility Sweden are national, so they
build `en_US.car` and `sv_SE.car`.
- Add the remaining locale categories: company, phone, finance, vehicle, words.
- Give every shipped category building-block columns, `misc.uuid` → `variant` among
them, and one record shape across the national ids; re-pin the shape.
- Give the address records one column set across countries: `region` and
`municipality` as columns on `geo.SE.address` too.
- Fill the 398 Swedish localities weighted 200 from SCB småorter.
- Weight `sv_SE.sex` and `en_US.sex` by bearers, as the README's Data section says they
are: neither names a `weight`, so a bare `sex` draws evenly.
- List `title` in both locales, and `sv_SE`'s `birth-number`, in the README's Data list
of what each locale carries, which names neither.
- Give `url` and `email` a path that draws only domains nobody can register, keeping
the wide set as the default, per goal 14: 17 of the 40 distinct ones shipped today
sit on `.se`, `.nu`, `.io` and `.co`, which anyone may register, and only RFC 2606's
`example.com`, `.net`, `.org`, `.test`, `.example`, `.invalid` and `.localhost`
provably reach nothing.
- Audit the rest of the shipped set against goal 14 and give each a never-reaching
path where it lacks one: `phone` draws live PTS and NANP ranges — PTS's five
fiction series are the Swedish inert set, sourced in
[research-sources-se.md](docs/research/research-sources-se.md), and the NANP one
still wants a source — and `bankgiro`, `plusgiro` and `routing` draw live prefixes.
- Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does
not hold: ISO 3166 reserves it for Western Sahara and the root zone has never been
delegated it, so no resolver answers for it and goal 3 promises otherwise.
- Draw `en_US.phone`'s `exch` as a NANP central office code: `{int(100,999)}` renders
a leading 1 in about an eighth of draws, which libphonenumber rejects, and
`TestShippedUSPhone` proves the shape rather than the rule. Assert the rule with it.
- Give a `sv_SE.personnummer` over 100 the `+` separator Skatteverket spells, or stop
drawing birthdates that reach 100: the format hard-codes `-`, and the 1930 floor
makes the oldest draws invalid from 2031.
- Merge `en_US.ip` and `sv_SE.ip`, today byte-identical, into one `misc.ip`, per goal 4's
one spelling per result.
- Accept a middle name, and draw a shipped `personnummer` inside a *selected* sex;
both want a draw group sharing its family's pins. Stop the conflict error naming a
rewrite that returns a different value where the read it conflicts with sits inside
another category, and report a struct column's draw conflict with the path spelling
a tag takes.
- Let `geo.US.locality[1714000].address` descend from a selected row into the template
beside the family: require the path step to reach a sibling category, and seed every
draw group of the render from the outer selector's pins.
- Read `email.local`'s and `username`'s handles from the shipped name tables, which
goal 12 asks for and the hand-written list they use today does not meet. Keep the
handle shape: `username` shortens a surname to `ahl` or `sjo`, and the US one draws
unisex given names, neither of which falls out of the tables unaided.
- Draw `email.local` from `username`, so the two share one handle list.
- Rename `misc.territory.country` to `sovereign`, per goal 4: it holds a code, and
`country` collides with the `misc.country` path it replaced.
- Add `misc.browser` as the parent of `misc.useragent`, so
`misc.browser[Chrome].useragent` resolves, per goal 3.
- Group `data/misc` into folders where a group name makes a path easier to guess, per
goal 4.
GoReleaser attaches them to the release the tag workflow publishes. A checkout build prints `devel` for `--version`; the binaries carry the stamped tag.
`misc` (locale-neutral)
### 1. Bind a pick to a name with `{x as n}`, read it with `{n}` and `{n.path}`, and refuse a name bound twice.
- A binding prints nothing: `{/person as p}{p}` renders `Erik Lind`, and `{p.last}` beside it `Lind`.
- A name is one pick of everything under it, drawn once: `{/country as k} {k.city.name} ({k.city.population})` is one city of `k`'s country.
- A name is visible in the template that binds it and everything that template renders, its sibling fields and a record's columns included, so one field may bind and another read.
- A name bound inside a `repeat` level picks again on every iteration; one bound outside it keeps its pick on every line.
- Refused at load: a name bound twice in one scope, and a name that is also a field or an option of the template, since options and fields share one namespace.
- Lands before item 4 changes what an unnamed `{…}` does, so the shipped data can move to names first (item 3).
### 3. Rewrite the shipped categories so facts that belong together come from one named pick.
- `sv_SE.person` and `en_US.person` bind the first name and read sex and title through it: `"first": "{.first-name as n}{n.name}"`, `"sex": "{n..sex.name}"`, a title from `{n..sex.title.name}`.
- `geo.SE.address` and `geo.US.address` bind the street and read locality, postal code and region through it; the locale `address` categories bind `/geo.XX.address` once.
- Needs items 1 and 2. Every shipped value must still pass its consumer's check before and after item 4.
- Re-pin seeded output in its own commit.
### 4. Draw every `{…}` afresh, keep a pick only by name, and delete `drawGroup` with the fences that held picks implicitly.
- Needs items 1–3. After it, `{/person} ({/person.first})` renders two people, `{w} {uppercase(w)}` renders `b G`, 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` and the decisions that reverse with them. The remaining load 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 References, Draw group, Correlated fields, Linked tables and "One draw, one spelling" sections; the Linked tables section says which pick's weights govern a family, since the bound row decides.
- Re-pin seeded output and `testdata/shipped_shape.txt` in their own commits; the CHANGELOG names the grammar change.
### 24. Draw `en_US.phone`'s `exch` as a NANP central office code, and assert the rule in `TestShippedUSPhone`.
`{int(100,999)}` renders a leading 1 in about an eighth of draws, which libphonenumber rejects, and the test proves the shape rather than the rule.
### 50. 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.
### 5. Run the nine-seat comprehension panel after the draw restructure, and file what it names.
Needs item 4, and runs as item 6 decides.
### 25. Give a `sv_SE.personnummer` over 100 the `+` separator Skatteverket spells, or stop drawing birthdates that reach 100.
The format hard-codes `-`, and the 1930 floor makes the oldest draws invalid from 2031.
### 32. Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.
`isin`: Luhn over letters expanded to digits. `aba`: 3-7-1 weights. `vin`: position 9 over the whole; a sample taking the WMI, since the check sits mid-string.
### 13. Strip the whitespace `data-import/territory.py` copies from its register.
`misc.territory[CW].capital` renders ` Willemstad`, with a leading space.
### 6. Decide how the comprehension gate treats panel noise: identical code scored 5.7 and 5.4.
Two nine-seat runs on `5cb919e`, same briefs, same model, 2026-09-30:
| Seat | Run 1 | Run 2 |
|---|---|---|
| Junior A | 5 | 5 |
| Junior B | 4 | 5 |
| Mid A | 6 | 5 |
| Mid B | 6 | 6 |
| Senior domain | 6 | 5 |
| Senior maintainability | 6 | 6 |
| Senior extender | 6 | 5 |
| Architect greenfield | 6 | 6 |
| Architect inherited | 6 | 6 |
| Mean (Navigation / Locality / Shape / Self-sufficiency) | 5.7 (7.0 / 5.1 / 5.9 / 5.7) | 5.4 (6.3 / 4.8 / 5.8 / 5.3) |
Both runs named the same hardest units, `drawsApart` and `readFold.reads`. Every score since 2026-09-20 (5.7–6.1) sits inside this spread, and `AGENTS.md`'s "no merge lowers the last score" can block or pass a chunk by chance.
### 2. Step up from a row to the row it links to with `..`: `{/city[Oslo]..country.name}` renders `Norway`, and `city[Oslo].country` stays the cell `NO`.
- `..country` steps up to the nearest ancestor table named `country`, from a selected row, a drawn row or a name bound to a row: `{c..country.name}`.
- After `..`, `.` steps down again: `{c..country.city.name}` is a city of `c`'s country, drawn afresh.
- `.country` after a row keeps reading the link column's cell, so the README's `city[Oslo].country` example stays true.
- A `Fake` path and `--list` take `..` too; `List` keeps advertising direct descents only.
- Replaces the decision "A link column after a row steps up to the parent row, and a path may not end on it".
### 23. Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does not hold.
ISO 3166 reserves it for Western Sahara and the root zone has never been delegated it, so no resolver answers for it.
### 27. Accept a middle name, and draw a `personnummer` whose sex matches a selected sex, by name.
Needs item 4. Report a struct column's draw conflict with the path spelling a tag takes.
### 47. Let a table column carry a `datatype`, so `--format json` writes `"safe": true` and `--format sql` a boolean.
Today only a JSON field takes one, so `misc.httpmethod`'s booleans are typed in Go and text everywhere else. Typing a shipped column changes what `json` and `sql` write, so the capability is a minor and applying it to `misc.httpmethod` is a major.
### 48. Read the tag through `git/ref/tags/{tag}` in `publish_release.py`.
GitHub answers `tags/{tag}` with 404 whatever exists, so the burnt-version guard never fires and a release lands on a tag already pointing at another commit.
### 46. Name the file a link error comes from beside its category path, `sub.x (/d/a/sub/x.json): …`.
With several `--data-path` layers the author has to work out which directory won, while a parse error on the same file names it.
### 34. Report every mistake a load finds in one error, as the decision "A load reports every mistake at once" states.
`New` and `NewTemplate` stop at the first, so data holding two mistakes takes two runs to fix. Split it into items before starting, one of them the shape a Go caller iterates and the CLI prints, and the order of an inline template's mistakes, which name no path; reword the README's "`New` refuses a mistake in the data" to every mistake.
### 20. Weight `sv_SE.sex` and `en_US.sex` by bearers, as the README's Data section says they are.
Neither names a `weight`, so a bare `sex` draws evenly.
### 38. Key every table by one key column, as the decision "A table selects by one key column, a code, never a free-form name" states.
`geo.SE.municipality[Lund]` and `misc.territory[Sweden]` select a row today. Steps:
- Have a selector that misses name the key of every row whose cell spells it, matched without case, else say the table selects by its key: `misc.territory[Sweden]` and `misc.territory[se]` name `[SE]`, `misc.protocol[tcp]` its key, and `misc.loglevel[Error]` `[err]`.
- Delete the `name` option and the load checks it carries, and have `New` refuse a table still carrying `name`, naming `key`.
- Key `misc.loglevel` on `keyword`, and `geo.SE.locality` on the integer `data-import/geo-se.py` assigns as the decision states, linking `street` and `postal-code` by it.
- Drop `key` from `last-name` and `sv_SE.title`, and re-pin `testdata/shipped_shape.txt`.
- Leave no selection by name in README.md, CHANGELOG.md, docs/ or `--help`.
### 18. Give every shipped category building-block columns, and one record shape across the national ids; re-pin the shape.
`misc.uuid` → `variant` among them.
### 43. Refuse a `Fake` path into a level carrying a `repeat`, as the README's Correlated fields section states.
`probePath` skips `pathCheck.enter`, so `Fake("x.a")` under `"repeat":3` renders one draw.
### 28. Let a path go from a selected row into the template beside its family: `geo.US.locality[1714000].address`.
Require the path step to reach a sibling category.
### 7. Walk a path in one function: fold `probePath` into `pathCheck.walk`.
`probePath` copies `pathCheck.walk`'s loop and `routeSteps` call, differing at a choice and in skipping `enter`; seven seats and both architects named the pair. After item 4.
### 14. Place xlsx cells by their `r` reference in `data-import/xlsx.py`.
A sheet omitting an empty cell shifts every later column left.
### 11. Test that every node kind reaches each switch over node kinds.
Ten switches, from `render` to `columnItems`, must agree, and only `render` and `renderEdges` say so. `tableReadOf` switches over `stepKind` with no default, so a new step kind passes it silently.
### 16. Add the remaining `misc` tables and templates, one row of the table below per chunk.
Read every factual list from a register by a `data-import/` script. Shape: T = table, t = template, c = choice.
| Category | Shape | Source | Licence |
|---|---|---|---|
@@ -164,6 +276,18 @@ by a `data-import/` script, as README goal 12 asks.
| `lorem`, `hacker`, `hipster`, `catchphrase`, `buzzword`, `quote` | T/c | lorem ipsum, LLM-written | — |
| `direction`, `continent`, `ulid` | c/t | — | — |
### 12. Test that every `geo` tree holds the five table names and the `address` columns that port across countries.
Nothing checks it, and a new country breaks it silently.
### 45. Reword `prove`'s refusal of a typed column reading a row, `{/region}`.
Say the format is the table's own (`region's format "{name}"`), name the read to write, `{/region.<column>}` with its columns, and stop calling a one-column format, `{code}`, composed text.
### 17. Add the remaining locale categories: company, phone, finance, vehicle, words.
Shape: T = table, t = template, c = choice.
`sv_SE` ([source research](docs/research/research-sources-se.md))
| Category | Shape | Source | Licence |
@@ -194,213 +318,94 @@ by a `data-import/` script, as README goal 12 asks.
| `word`, `sentence`, `paragraph` | T+t | Moby POS or WordNet | public domain / WordNet |
| month and weekday names, `holiday` | t+T | CLDR en | Unicode |
### Builtins the data cannot express
### 9. Move `binding`, `bind` and `checkNodeFences` out of `data.go` into a file of their own.
- Add `{isin()}` (Luhn over letters expanded to digits), `{cusip()}`, `{aba()}`
(3-7-1 weights) and `{vin()}` (position 9 over the whole; a sample taking the WMI,
since the check sits mid-string).
- Add `{base64url(n)}` for JWT shapes.
- Add a `unix` layout to `date()` once something needs it.
The greenfield architect found the fence pipeline last. After item 4, which shrinks it.
### Library and CLI
### 10. Move `table.route`, `selector`, `step` and `drawStep` from `path.go` to `table.go`.
- Report every mistake a load finds in one error, as the decision "A load reports every
mistake at once" states: `New` and `NewTemplate` stop at the first, so data holding
two mistakes takes two runs to fix. Split it into sub-items before starting, one of
them the shape a Go caller iterates and the CLI prints, and the order of an
inline template's mistakes, which name no path; reword the README's
"`New` refuses a mistake in the data" to every mistake.
- Rename `newRand` to `newGeneratorState` and `Generator.rand` to `state`: both name
half of a `generatorState`, hiding its `{seq()}` counters, and `New`'s local `rng`
shadows the `rng` interface.
- Compare `calcParser.binary`'s operator as a rune: `byte(p.rs[p.pos])` reads U+012B
`ī` as `+`, so `{calc(a ī b)}` compiles as `a + b`.
- Stop the `columnKinds` suggestion naming a field's own kind or a narrower one: an
`int64` or `float64` field unproven in range is told to become itself, and a `uint64`
one to become `int64`.
- Say in the README's Linked tables that the first table a render reads decides whose
weights govern the family: `geo.SE.address` reads `street` first, so it draws by
street segments and the region, kommun and postort populations never apply.
- Parse `defaultTable`'s path with the library's grammar: the CLI counts brackets of its
own, so a change to the selector grammar desyncs the default `--table` silently.
- Step up from a row to its parent as the decision "A link column after a row steps up
to the parent row, and a path may not end on it" states, refusing a step back down:
`city[Oslo].country.name` is a load error today, and `city[Oslo].country` renders the
cell `NO`, as `locality.municipality` does, which `List` stops advertising. Replace the
README's `city[Oslo].country` example with
`fejkdata -d ./mydata 'city[Oslo].country.name'`, rendering `Norway`.
- Key every table as the decision "A table selects by one key column, a code, never a
free-form name" states: `geo.SE.municipality[Lund]` and `misc.territory[Sweden]` select
a row today.
- Have a selector that misses name the key of every row whose cell spells it,
matched without case, else say the table selects by its key:
`misc.territory[Sweden]` and `misc.territory[se]` name `[SE]`, `misc.protocol[tcp]`
its key, and `misc.loglevel[Error]` `[err]`.
- Delete the `name` option and the load checks it carries, a name spelling another
row's key and a name repeating inside its parent, and have `New` refuse a table
still carrying `name`, naming `key`.
- Key `misc.loglevel` on `keyword`, and `geo.SE.locality` on the integer
`data-import/geo-se.py` assigns as the decision states, linking `street` and
`postal-code` by it.
- Drop `key` from `last-name` and `sv_SE.title`, and re-pin `testdata/shipped_shape.txt`.
- Leave no selection by name in README.md, CHANGELOG.md, docs/ or `--help`: no
selector spelling a free-form name, `geo.SE.locality[Lund]`, `misc.currency[Euro]`, `first-name[Kim]`
and the rest, no "key or name", "by name" or "either" wording about selectors, and no
`name` option in an example.
- 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 the
rewrite through each selection: beside `{/geo.US.locality[1714000].name}`,
`{/geo.US.municipality.name}` names none today, where
`{/geo.US.locality[1714000].municipality.name}` holds once the step up above lands.
Scope the README's "A reference path into a category is held" to a path that
selects no row.
- Refuse a struct tag's `datatype` naming the Go type that sets it before proving its
values, as the README's Library section promises: `datatype: boolean` over
`{int(1,2)}` on an `int` field answers `prints an integer, not a boolean`.
- Name the spelling to use in `checkNameHeld`'s path refusal and `checkNoOverlap`'s, as
the README's hold section promises: both answer "name the fields you want instead",
and `{n} reaches "p" by a second route` names neither the read the route ends on,
`{/row.p.last}`, nor the spelling to write, `{p.last}`. Where an operand route's value
is the held name, offer it as the operand: `lowercase operand "y" renders
"/misc.territory"` ends at "stop", where `{lowercase(/misc.territory)}` loads.
- Split the README's linked-tables paragraph opening "A path descends from a row", 23
lines holding five rules: move its load errors that are no overlap to the table
section, and drop its bare `{/city}` refusal, which the hold section states.
- Offer `move {Y} into a field with a drawGroup` in `overlapError` where both reads share
one field: `"a":"{/cat} {/cat.p.first}"` ends at "name the fields you want instead",
while the same pair in a format offers the move, which loads there too.
- Call `templateError` a compile failure: its doc says render failure, and it wraps
what `NewTemplate` and `NewRecordTemplate` refuse.
- Name the node a selector follows in `stepInto`'s refusal: `sv_SE.person[1].first`
answers `1 is not a table`.
- 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
first in Go's map order.
- Refuse a category naming a hidden rows file, `"rows": ".x.tsv"`, as a rows file that
is not there: `loadDir` indexes hidden TSVs and skips them in two places, so one loads
where its comment says a hidden file is never data.
- Refuse a `Fake` path into a level carrying a `repeat`, as the README's Correlated fields section states:
`probePath` skips `pathCheck.enter`, so `Fake("x.a")` under `"repeat":3` renders one draw.
- Print `--help`'s repeat range from `fejkdata.MaxRepeat`: the usage spells
`1..1048576` a second time.
- Report a CRLF rows file holding only its header as having no rows: `parseRows`
answers "line 2 is empty".
- Reword `prove`'s refusal of a typed column reading a row, `{/region}`: say the
format is the table's own (`region's format "{name}"`), name the read to write,
`{/region.<column>}` with its columns, and stop calling a one-column format,
`{code}`, composed text.
- Keep one copy of the selector characters in `data-import/`: `httpmethod.py` and
`protocol.py` each copy `inSelector`, and `tsv.py`'s `write` enforces neither.
- Refuse two whole reads of one table family in one render, which goal 5 promises is a
load error: `{/sel}|{/sel}` panics out of `Fake` where the two draws land on rows whose
cells select different rows of another table, and
`{/geo.SE.locality}|{/geo.SE.municipality}` renders a locality outside the
municipality beside it. Each read draws its own row, so cells of two rows render
together, where the fence counts them alternatives and compares neither. Have the error
name the path spelling that holds, `{/geo.SE.locality.name}|{/geo.SE.municipality.name}`,
as the README's rule on a rejected spelling asks.
- Name the file a link error comes from beside its category path, `sub.x
(/d/a/sub/x.json): …`: with several `--data-path` layers the author has to work out
which directory won, while a parse error on the same file names it.
- Name the row a refused read sits in: where both reads come from TSV cells the overlap
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
cell that is not the one to fix.
- Spell a table one way across the family errors: `drawsApart` names
`drawn.segment`, `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
folders hold that name.
- Let a table column carry a `datatype`, so `--format json` writes
`"safe": true` and `--format sql` a boolean rather than the text `'true'`. Today only
a JSON field takes one, so `misc.httpmethod`'s booleans are typed in Go and text
everywhere else. Typing a shipped column changes what `json` and `sql` write, so the
capability is a minor and applying it to `misc.httpmethod` is a major.
The inherited architect's 3am trace crossed four files for one draw.
### Release
### 29. Rename `misc.territory.country` to `sovereign`.
- Read the tag through `git/ref/tags/{tag}` in `publish_release.py`: GitHub answers
`tags/{tag}` with 404 whatever exists, so the burnt-version guard never fires and a
release lands on a tag already pointing at another commit.
- Document `NewRecordTemplate`, `ErrNoColumns`, `ErrNoData` and `MaxRepeat` in the
README's Library section, which names none of them.
- Build on a manual run of `test.yml`: `workflow_dispatch` leaves `github.event.before`
empty, so the diff compares `HEAD` with itself and skips `docker build`.
- 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.
- Ship prebuilt binaries so the CLI needs no Go: GoReleaser attaching them to the Gitea
release the tag workflow publishes, a container image, Homebrew and Scoop. A checkout
build prints `devel` for `--version`; the binaries carry the stamped tag.
It holds a code, and `country` collides with the `misc.country` path it replaced.
## v0.2.0
### 39. Refuse a struct tag's `datatype` naming the Go type that sets it before proving its values, as the README's Library section promises.
- **Promise in `Generator`'s godoc that its renders run one at a time**, so a caller
wanting parallel throughput makes one generator per goroutine.
- Ship the full registers as packs, each a Go module with its own `embed.FS` and a zip
for `--data-path`: every US place of 10,000 and more streets per locality, built by
`--min-population` and `--streets-per-locality`. The default embed stays under ~1 MB
per country and `New` under ~50 ms.
- Add the locales nb_NO, da_DK, fi_FI, de_DE, en_GB, nl_NL, fr_FR and es_ES, with
person, address, phone, national id, company and date names each, and the `geo/`
trees NO, DK, FI, NL, FR, AU, CA, ES, GB and DE their `address` reads by reference;
AU and CA get a tree with no locale of their own.
- Add `{btc()}` and `{eth()}`, which need sha256 and keccak for Base58Check and EIP-55.
- Pair a street with its exact postnummer, which needs an application to Lantmäteriet;
today a street goes to the nearest postal code centroid. It rewrites shipped rows, so
it is a major once 1.0 is cut and a minor before that.
`datatype: boolean` over `{int(1,2)}` on an `int` field answers `prints an integer, not a boolean`.
## v0.3.0
### 15. Spell `misc.creditcard`'s digit runs `{digits(n)}`, and refuse a repeat over a lone `{digits(1)}` at `New`, naming that spelling.
Names no items yet, so planning it is a chunk of its own: it takes the additive work
that lands after v0.2.0, and it has to add something, since a minor only adds. Its job
is to be the release with no breaking change that v1.0.0 waits on — v0.2.0 cannot be,
because pairing a street with its exact postnummer rewrites shipped rows.
Both render the same run.
## v1.0.0
### 22. Give `url` and `email` a path that draws only domains nobody can register, keeping the wide set as the default.
- Cut it once the shipped data is in its record shape and one full minor has shipped
with no breaking change, per the README's Versioning table: v0.1.0 settles the
record shape and v0.3.0 is the quiet minor.
- Announce it where a developer choosing a fake-data tool already reads, which is
goal 1's second half and waits for the tag: complete, stable, installable without
Go. Write the README's first screen for someone deciding in a minute.
17 of the 40 distinct ones shipped today sit on `.se`, `.nu`, `.io` and `.co`, which anyone may register, and only RFC 2606's `example.com`, `.net`, `.org`, `.test`, `.example`, `.invalid` and `.localhost` provably reach nothing.
## Not release-bound
### 36. Compare `calcParser.binary`'s operator as a rune.
- Give every entry in `docs/decisions.md` the goal it serves, its date, who made it and
the premise it rests on; propose a goal for an entry that serves none.
- Hold every change the charter says owes a `CHANGELOG.md` entry to one in CI: the
check covers `data` and `testdata/shipped_shape.txt` alone, where `AGENTS.md` adds a
flag, an exit code, an exported name, a fence, a builtin and the lowest Go.
- Measure the allocation gate over the shipped `geo` trees too: `pinSet.inline [8]` is sized
for them, and `perf_test.go` checks a synthetic five-deep tree only.
- Name goal 2's four criteria with the axes the panel scores, so a recorded score maps
back to the clause it came from: the goal spells them out in prose while `todo.md`
records `Navigation`, `Locality`, `Shape` and `Self-sufficiency`.
- Cut the README's Layout block to the lines that say what a file name cannot:
`calc.go`, `doc.go` and `cmd/fejkdata/` restate their names.
- Cut `AGENTS.md` to the rules only it states: the round procedure stands word for word
in `todo.md` and the `gh` rule opens with the README's GitHub decision, so each is a
second copy that drifts. Split the comprehension rule while there, which packs the
gate, its exceptions, the round and the at-or-above-7.0 regime into one line.
- Scope `AGENTS.md`'s `Hard tabs.` to the Go source, or drop it: `gofmt` already gates
Go at `Dockerfile:16`, and the shipped JSON under `data/` is two-space, so the rule as
written is one no Go file can break and every data file does.
- Make `gitea.larvit.se/larvit/fejkdata` a pull mirror of GitHub:
Gitea converts a repository to a mirror only by re-creating it, so until that runs
the copy there is a second owner of one history and goes stale from this commit.
- Stop `/cache` collecting root-owned files, which today every command documented
without `--user` leaves for the next `--user` one to trip over, and which the README
answers with a chown a reader has to repeat. Put `--user` on all of them, or give the
`x-go` anchor a `user:` — that one needs a mechanism, since Compose interpolates from
the process environment and neither `UID` nor `GID` is exported there, so
`user: "${UID}:${GID}"` resolves to `":"`.
- Spell the latest supported Go once: `Dockerfile`'s `ARG GO_VERSION` and
`compose.yaml`'s `golang:${GO_VERSION:-…}` pin one version twice, and a bump that
moved only the first left `docker compose run --rm fmt` formatting to a `gofmt` the
gate rejects. A `renovate.json` custom manager reads the second today; drop it with
the duplication.
- Publish a homepage with an in-browser generator: the library compiled to WebAssembly,
so visitors generate as much data as they like in their own browser.
`byte(p.rs[p.pos])` reads U+012B `ī` as `+`, so `{calc(a ī b)}` compiles as `a + b`.
### 41. Name the node a selector follows in `stepInto`'s refusal.
`sv_SE.person[1].first` answers `1 is not a table`.
### 44. Report a CRLF rows file holding only its header as having no rows.
`parseRows` answers "line 2 is empty".
### 37. Stop the `columnKinds` suggestion naming a field's own kind or a narrower one.
An `int64` or `float64` field unproven in range is told to become itself, and a `uint64` one to become `int64`.
### 42. 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 first in Go's map order.
### 8. 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. After item 4.
### 35. Rename `newRand` to `newGeneratorState` and `Generator.rand` to `state`.
Both name half of a `generatorState`, hiding its `{seq()}` counters, and `New`'s local `rng` shadows the `rng` interface.
### 40. Call `templateError` a compile failure in its doc.
Its doc says render failure, and it wraps what `NewTemplate` and `NewRecordTemplate` refuse.
### 55. Pair a street with its exact postnummer.
Needs an application to Lantmäteriet; today a street goes to the nearest postal code centroid. It rewrites shipped rows, so it is a major once 1.0 is cut and a minor before that.
### 54. Add `{btc()}` and `{eth()}`.
They need sha256 and keccak for Base58Check and EIP-55.
### 52. Promise in `Generator`'s godoc that its renders run one at a time.
So a caller wanting parallel throughput makes one generator per goroutine.
### 58. Cut the README's Layout block to the lines that say what a file name cannot.
`calc.go`, `doc.go` and `cmd/fejkdata/` restate their names.
### 53. Add the locales nb_NO, da_DK, fi_FI, de_DE, en_GB, nl_NL, fr_FR and es_ES, and their `geo/` trees.
Person, address, phone, national id, company and date names each, and the `geo/` trees NO, DK, FI, NL, FR, AU, CA, ES, GB and DE their `address` reads by reference; AU and CA get a tree with no locale of their own.
### 59. Cut `AGENTS.md` to the rules only it states, and explain every term its comprehension rule uses.
The round procedure stands in `todo.md` and the `gh` rule opens with the README's GitHub decision, so each is a second copy that drifts. The comprehension rule packs the gate, its exceptions, the round and the at-or-above-7.0 regime into one line, and uses "depth 1", "nine-seat", "four-seat" and "answers it in one run" without saying what they mean.
### 57. Hold every change `AGENTS.md` says owes a `CHANGELOG.md` entry to one in CI.
The check covers `data` and `testdata/shipped_shape.txt` alone, where `AGENTS.md` adds a flag, an exit code, an exported name, a fence, a builtin and the lowest Go.
### 56. Give every entry in `docs/decisions.md` the goal it serves, its date, who made it and the premise it rests on.
Propose a goal for an entry that serves none.
### 60. Announce v1.0.0 where a developer choosing a fake-data tool already reads, with a README first screen for someone deciding in a minute.
The human cuts v1.0.0 once the shipped data is in its record shape and one full minor has shipped with no breaking change, per the README's Versioning table; v0.2.0 rewrites shipped rows, so a v0.3.0 with no breaking change comes first.