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 309 additions and 312 deletions
Showing only changes of commit c7f9885821 - Show all commits
+1 -1
View File
@@ -9,7 +9,7 @@
- CodeRabbit skips every pull request until the repository has 10 GitHub stars: check
its status once when a PR is marked ready, answer a review if one is there, and never
wait for one.
- README goal 2 is gated at 7.0 on the `comprehension-panel` skill, and below it
- README goal 3 is gated at 7.0 on the `comprehension-panel` skill, and below it
nothing else merges — no feature, no category, no data — bar a security fix, a
dependency bump, a repair of behaviour the README documents, and the infrastructure
the round itself runs on. `todo.md` carries the round: a PR per item it plans, the nine-seat
+1 -1
View File
@@ -14,7 +14,7 @@ Every shipped dataset, its source, its licence and the attribution it asks for.
| `sv_SE/first-name.tsv`, `last-name.tsv` | [SCB](https://www.scb.se/) names with at least two bearers, 31 December 2022 | CC0 1.0 | "Källa: SCB" | `data-import/names-se.py` |
| `en_US/first-name.tsv` | [SSA](https://www.ssa.gov/oact/babynames/) baby names, births 1930 to 2020, through [hackerb9/ssa-baby-names](https://github.com/hackerb9/ssa-baby-names) | public domain | none required | `data-import/names-us.py` |
| `en_US/last-name.tsv` | Census Bureau surnames occurring 100 or more times, 2010 | public domain | none required | `data-import/names-us.py` |
| `sv_SE/sex.tsv`, `sv_SE/birth-number.tsv`, `sv_SE/title.tsv`, `en_US/sex.tsv`, `en_US/title.tsv`, `misc/car.tsv` | curated; Skatteverket's test birth numbers are facts, and `car.tsv` waits for an international source (goal 12) | — | — | — |
| `sv_SE/sex.tsv`, `sv_SE/birth-number.tsv`, `sv_SE/title.tsv`, `en_US/sex.tsv`, `en_US/title.tsv`, `misc/car.tsv` | curated; Skatteverket's test birth numbers are facts, and `car.tsv` waits for an international source (goal 13) | — | — | — |
| `misc/currency.tsv` | [datasets/currency-codes](https://github.com/datasets/currency-codes); symbols from [Unicode CLDR](https://github.com/unicode-org/cldr) `en.xml` and `root.xml` | PDDL 1.0; [Unicode License v3](https://www.unicode.org/license.txt) | CLDR: "Copyright © 1991-2025 Unicode, Inc. Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the United States and other countries." | `data-import/currency.py` |
| `misc/httpmethod.tsv` | [IANA HTTP Method Registry](https://www.iana.org/assignments/http-methods/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/httpmethod.py` |
| `misc/httpstatus.tsv` | [IANA HTTP Status Code Registry](https://www.iana.org/assignments/http-status-codes/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/httpstatus.py` |
+22 -18
View File
@@ -176,9 +176,9 @@ zone), `useragent` and `uuid` (v4). Many carry sub-fields — `misc.currency.sym
Where a script under [`data-import/`](data-import) reads a source, the facts come from
that source, and the script drops a row only by a rule it states; elsewhere a small hand-written
set ships, and [`todo.md`](todo.md) carries the step that replaces it (goal 12). Copy
set ships, and [`todo.md`](todo.md) carries the step that replaces it (goal 13). Copy
that states no fact, such as `word` and `sentence`, stays hand-written. Where a category
has no path that draws only values reaching nothing real (goal 14), write a fixed value such
has no path that draws only values reaching nothing real (goal 15), write a fixed value such
as `example.com` into the template; `todo.md` plans the path.
`misc.httpmethod`, `misc.protocol` and `misc.port` are IANA's registries.
@@ -767,7 +767,7 @@ token {w} is repeated, and uppercase operand "w" holds "w" to one draw per expan
### Performance
The target is about a microsecond per value (goal 11).
The target is about a microsecond per value (goal 12).
Each file is parsed, validated and weight-indexed once, in `New`, bar a table's name and
parent columns, which are mapped on the first `Fake` that selects by name or descends
@@ -834,16 +834,20 @@ Where the project is heading; the sections before Audience document what ships t
1. **The best fake-data tool there is, and the one developers reach for.**
1. You can install it the way you install your other tools, and people hear about
it where developers read.
2. **The code is easy to understand.**
2. **Enough data ships built in to fill a real test database, with no download.**
1. It covers the ten most-spoken languages and the Nordic countries.
2. Each locale covers the common categories: names, addresses, phone numbers, ids,
companies and dates.
3. **The code is easy to understand.**
1. You can go from a bug to the code behind it without asking anyone.
2. You can change one part without keeping the rest in your head.
3. Every name says what the thing really is.
4. Each file makes sense on its own, without another document open beside it.
3. **Every value is valid.** It passes the same check the real system would apply,
4. **Every value is valid.** It passes the same check the real system would apply,
such as the checksum in a Swedish personal number.
1. Values that belong together come from the same random pick. You choose which
ones by giving that pick a name.
4. **Templates are easy to write and read.** A template is the text you write to
5. **Templates are easy to write and read.** A template is the text you write to
describe a value, such as `{first} {last}`.
1. Everything outside `{…}` prints exactly as written.
2. Each `{…}` makes a new random pick.
@@ -857,25 +861,25 @@ Where the project is heading; the sections before Audience document what ships t
shows the right one.
8. When a clearer name turns up, it replaces the old one, even if that needs a new
major version.
5. **Mistakes are caught when the data loads, and the error says plainly what is
6. **Mistakes are caught when the data loads, and the error says plainly what is
wrong.**
6. **One command gets you a value.**
7. **One command gets you a value.**
1. Flags work like in other command-line tools, and can go anywhere on the line.
2. Your first template of your own needs no escaping and no options.
7. **Data is JSON files. Code is only for what JSON can't express.**
8. **The same seed always gives the same output.**
9. **It depends on nothing but Go's standard library.**
10. **The docs are a map of the template language.**
8. **Data is JSON files. Code is only for what JSON can't express.**
9. **The same seed always gives the same output.**
10. **It depends on nothing but Go's standard library.**
11. **The docs are a map of the template language.**
1. Every feature has its own heading.
2. Every example runs as a test and shows its output.
3. Each rule is stated in one place.
11. **It is fast enough that you never notice it.**
12. **Every fact comes from an official or open source,** such as a government
12. **It is fast enough that you never notice it.**
13. **Every fact comes from an official or open source,** such as a government
register.
1. A table holds every row its source holds, and nothing added by hand.
13. **The most common data comes first,** such as names, addresses, phone numbers and
14. **The most common data comes first,** such as names, addresses, phone numbers and
dates.
14. **Values look real by default. You can also ask for values that can never reach
15. **Values look real by default. You can also ask for values that can never reach
anything real,** such as a domain nobody can register.
## Layout
@@ -959,8 +963,8 @@ docker build --build-arg GO_VERSION=1.22.12 --target portable . # lowest support
GO_VERSION=1.22.12 docker compose run --rm test # the same tests, without the image build
```
Goal 2 is scored by a panel of simulated readers, and [`AGENTS.md`](AGENTS.md) says
what the score asks of a pull request. Goal 4 is checked against simulated template
Goal 3 is scored by a panel of simulated readers, and [`AGENTS.md`](AGENTS.md) says
what the score asks of a pull request. Goal 5 is checked against simulated template
writers from the Audience.
A change to the shipped data re-pins [`testdata/shipped_shape.txt`](testdata/shipped_shape.txt)
+31 -31
View File
@@ -4,7 +4,7 @@
2026-09-20, Lilleman auf Larv.
Goal 1 wants the usage the tool earns and goal 2 expects extenders who did not write it;
Goal 1 wants the usage the tool earns and goal 3 expects extenders who did not write it;
both need a stranger to file an issue and open a pull request. Gitea has no anonymous
issue, and no cross-host pull request at all, so a contributor would need an account and
a fork on a personal instance. Valid while the project wants contribution from outside:
@@ -17,7 +17,7 @@ the workflow decided nothing, being portable already — `github.api_url` and
2026-10-03, decided by the maintainer. Technical principle: one owner per value
(`~/.claude/principles/technical-principles.md`). GitHub owns the history, and
`gitea.larvit.se/larvit/fejkdata` stays as a read-only pull mirror of it, so it is never
a second owner. Until `todo.md` item 70 converts it, the Gitea copy goes stale. Valid
a second owner. Until `todo.md` item 68 converts it, the Gitea copy goes stale. Valid
while the maintainer keeps a Gitea copy.
## The vocabulary sits below `doc.go`'s package clause, not in the package doc
@@ -27,7 +27,7 @@ while the maintainer keeps a Gitea copy.
Goal 1 is what a developer choosing a library reads first, and the package doc is that
page: the glossary would fill two thirds of it with the render path's units, mostly
unexported, and Go doc comments have no code markup, so every name would render its
backticks literally. Below the clause it reaches goal 2's reader — who opens the file —
backticks literally. Below the clause it reaches goal 3's reader — who opens the file —
and nothing else. Valid while the glossary's subject is the render path; one written to
teach the exported API belongs above the clause.
@@ -118,14 +118,14 @@ still lives in `data/` as JSON; `--data-path` layers over it.
2026-09-02, Lilleman auf Larv.
Goal 4: `{/p} {/p}` is two draws, as `{word} {word}` is, while every `{/p.first}` in
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.
Goal 4.2 overrides this; `todo.md` item 4 replaces it.
Goal 5.2 overrides this; `todo.md` item 4 replaces it.
## Reference sigils follow the filesystem
@@ -161,12 +161,12 @@ this repo's own are.
2026-09-16, Lilleman auf Larv.
Goal 5. A script branches on the exit code and reads the named path or spelling, so
Goal 6. A script branches on the exit code and reads the named path or spelling, so
those hold; wording improves in a minor.
## A load reports every mistake at once
2026-09-30, Lilleman auf Larv. Goal 5. An author learns everything wrong with the data
2026-09-30, Lilleman auf Larv. Goal 6. An author learns everything wrong with the data
in one load, not one mistake per run: `New` and `NewTemplate` report every mistake in one
error, ordered by the path each names. A mistake following only from another, such as a
reference into a category that did not compile, is left out, so each one names something
@@ -174,7 +174,7 @@ to fix. Valid while a load checks the whole set before its first render.
## Formats are checked after all data is loaded, so their errors name the category
2026-09-28, larv-review; approved 2026-09-28 by lilleman. Goal 2. Loading reads every
2026-09-28, larv-review; approved 2026-09-28 by lilleman. Goal 3. Loading reads every
file first, then connects references across them. A format holding a reference can only
be checked once it is connected, so every format is checked then, and all checks run at
one moment. An error from those checks names the category path you would type (`sub.x`),
@@ -196,7 +196,7 @@ convention of a minor is not followed.
literal's values out to a lone long key, so no source satisfies both it and 1.22's. A
consumer on the lowest supported Go depends on the code compiling and its tests passing
there, which is the `portable` stage, and never runs `gofmt` over this source, so the
latest toolchain alone defines the one canonical form goal 2 asks a reader to meet.
latest toolchain alone defines the one canonical form goal 3 asks a reader to meet.
Valid while the lowest supported Go is not the latest.
## The changelog heading is the one spelling of a release; CI cuts the tag
@@ -304,7 +304,7 @@ 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.
Goal 4.2 overrides this; `todo.md` item 4 replaces it.
Goal 5.2 overrides this; `todo.md` item 4 replaces it.
## A draw group name is local to its category
@@ -321,7 +321,7 @@ across categories must agree.
2026-09-16, Lilleman auf Larv.
Goal 2: one proves a sibling path or an operand is reached only by its readers within
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
@@ -333,12 +333,12 @@ step.
2026-09-29, Lilleman auf Larv.
Every render starts from empty draws, its draw groups included, since goal 2 prices
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 11 holds either way.
included, and one reading none pays nothing; goal 12 holds either way.
`todo.md` item 4 deletes what this decides.
@@ -347,7 +347,7 @@ included, and one reading none pays nothing; goal 11 holds either way.
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 3 asks facts that belong
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
@@ -413,7 +413,7 @@ writes as the load check proved it; a `Value any` would hand every caller a type
2026-09-29, Lilleman auf Larv.
Goal 2 wants a name to reach one unit: `drawMemo` is what a draw is kept in, `readValue` what
Goal 3 wants a name to reach one unit: `drawMemo` is what a draw is kept in, `readValue` what
a read drew.
## One name, one meaning
@@ -423,8 +423,8 @@ definition covers both: the same kind of value, counted or addressed the same wa
reader landing on the wrong one concludes nothing false, as `Fake` renders one value on
`Generator`, `Template` and `RecordTemplate`. `TestNoFunctionSpellsAMethod` keeps a bare function off a method's
name, since a search returns both and a call site shows no receiver; review judges the
rest. Serves goal 2: names that tell the truth, and reaching the unit behind a symptom
without asking a person. Decided 2026-09-25 by Lilleman; valid while goal 2 counts reading
rest. Serves goal 3: names that tell the truth, and reaching the unit behind a symptom
without asking a person. Decided 2026-09-25 by Lilleman; valid while goal 3 counts reading
cost.
## The package stays flat
@@ -478,7 +478,7 @@ accepted one changes, so the door stays open for the address records the plan de
## A selected row is fixed, not drawn, and an unnamed read beside it is refused
Decided 2026-09-27 by the maintainer, for goals 3 and 4; valid while a selector names
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}`
@@ -496,11 +496,11 @@ 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.
Goal 4.2 overrides its shared draw beneath a selection and its refusal of an unnamed read; `todo.md` item 4 replaces them.
Goal 5.2 overrides its shared draw beneath a selection and its refusal of an unnamed read; `todo.md` item 4 replaces them.
## 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
Decided 2026-09-27 by the maintainer, for goal 5; valid while a link column holds the
parent's key. It extends [After a row, a path names a column or a linked
table](#after-a-row-a-path-names-a-column-or-a-linked-table) from child tables to the
parent. A row is selected or drawn alike.
@@ -526,11 +526,11 @@ 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.
Goal 4.4 overrides this; `todo.md` item 2 replaces it.
Goal 5.4 overrides this; `todo.md` item 2 replaces 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
Decided 2026-09-27 by the maintainer, for goal 5's one spelling per result; valid while
the key is the code a user writes, as a database keys a table. `country[NO]` loads, and
`country[Norway]` is refused naming `[NO]`, found by the row whose cell spells the
selector. The key is the friendliest code the register holds, `misc.territory[SE]` and
@@ -600,9 +600,9 @@ own-family fence walks through both rather than stopping where the draw fences d
2026-09-29, Lilleman auf Larv.
Goal 3: facts that belong together come from one draw, so a shop row's `phone` cell reads
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 2 keeps the fences
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.
@@ -623,7 +623,7 @@ is until a real data set needs the indexed form.
2026-09-17, Lilleman auf Larv.
Goals 8 and 11: a path that fails moves no seeded stream, at the cost of one draw-free
Goals 9 and 12: a path that fails moves no seeded stream, at the cost of one draw-free
walk per call, compiling its steps into a stack buffer of 16. Valid while that walk
costs little against the draw it guards.
@@ -765,7 +765,7 @@ naming the row.
2026-09-20, Lilleman auf Larv.
`misc.httpmethod`, `misc.port`, `misc.httpstatus`, `misc.mimetype` and `misc.tld` weigh
every row alike, so GET is a ninth of the methods drawn. Goal 12 keeps an authored fact
every row alike, so GET is a ninth of the methods drawn. Goal 13 keeps an authored fact
out of a sourced table, and no register publishes how often a method, a port or a TLD is
used, so a weight here would be invented. Where one exists it is read, as
`misc.timezone` reads GeoNames populations and `sv_SE.first-name` SCB bearers.
@@ -775,7 +775,7 @@ used, so a weight here would be invented. Where one exists it is read, as
2026-09-24, Lilleman auf Larv.
The weight is the population GeoNames records in the zone's cities of 15,000 or more,
floored at 15,000, which goal 14 asks for: over 300 seeded draws of
floored at 15,000, which goal 15 asks for: over 300 seeded draws of
`misc.territory[US].timezone`, the four zones most Americans live in took 278 where an
even weight gave them 44, and `America/Indiana/Petersburg`, a town of 2,400, fell from 17
to 0. Valid while a draw weighted this way lands where people live.
@@ -807,7 +807,7 @@ claim fejkdata has no business making.
2026-09-27, Lilleman auf Larv.
A flat list of names carries neither the code a PRI encodes nor a selector reaching it,
and goal 3 draws the two as one fact. The canonical spelling losing to the one its
and goal 4 draws the two as one fact. The canonical spelling losing to the one its
domain writes, above, settles the rest: a configuration writes `info`, so RFC 5424's
`Informational` stays the `severity` column.
@@ -817,14 +817,14 @@ domain writes, above, settles the rest: a configuration writes `info`, so RFC 54
The register spells a TLD `.se` and `misc.territory.tld` already ships it so, which a
bare key would make two spellings of one fact; `{/misc.tld}` also composes onto a host
with no separator. `misc.tld[se]` misses for it, which `todo.md` item 45 ends.
with no separator. `misc.tld[se]` misses for it, which `todo.md` item 44 ends.
## `misc.tld` is a table of its own, and `misc.territory.tld` stays a column
2026-09-20, Lilleman auf Larv.
A `parent` demands a child for every parent row, so linking them would drop every root
zone row naming no territory, which is most of them, and goal 12 holds a sourced table
zone row naming no territory, which is most of them, and goal 13 holds a sourced table
whole. The loader refuses the link outright anyway: `tld` is a column of
`misc.territory`, and a table may not be named like a column of its ancestor.
@@ -880,7 +880,7 @@ the direct chain is the one a reader can predict from the tables' parents.
2026-09-30, larv-review on the comprehension round the maintainer approved on 2026-09-29;
replaces "The path walks are separate loops".
Goals 2 and 11: `drawSteps` draws every path from its steps, compiled at link by
Goals 3 and 12: `drawSteps` draws every path from its steps, compiled at link by
`pathCheck` or, for a caller's path, by `probePath` into a stack buffer. The check and
the probe stay two loops: on 2026-09-29 one loop over all three walks, switching on a
mode field, leaked the check's leaves and errors with the draw's pins, arm and memo,
+1 -1
View File
@@ -90,7 +90,7 @@ Recommendation: embed SCB 2022 xlsx-derived lists (top-N per sheet with counts)
## 3. Phone
- Source: PTS "The Swedish numbering plan for telephony according to ITU-T E.164", 2024-01-08 (verified, text extracted): https://pts.se/globalassets/globala-block/nummertillstand/the-swedish-numbering-plan-for-telephony-according-to-itu---2024-01-08.pdf. Swedish "nrplansammanstallning-2026-02-04.pdf" and https://nummer.pts.se/NbrPlanSearch are behind a Radware bot check (both curl and Playwright blocked). No licence stated by PTS anywhere found; pts.se says contact pts@pts.se for data.
- Reserved for fiction, PTS "Telefonnummer till böcker och filmer" (verified 2026-09-21; curl and Playwright are Radware-blocked, a rendering fetcher gets through): https://www.pts.se/internet-och-telefoni/telefonnummer-och-adressering/telefonnummer-till-bocker-och-filmer/ — mobile 070-1740605–1740699, geographic 08-46500400–46500499 (Stockholm), 031-3900600–3900699 (Göteborg), 040-6280400–6280499 (Malmö), 0980-319200–319299 (Kiruna). 495 numbers PTS holds back so they reach nobody, which is goal 14's never-reaching path for `sv_SE.phone`. Same licence status as the numbering plan: none stated, facts only, cite PTS. The mobile block fits the shipped `{a} {b} {c}` grouping (`174 06 05`–`99`), as do 031 and 040; Stockholm's 8 subscriber digits and Kiruna's 6 need a format of their own, and 0980 is not among the 20 shipped area codes.
- Reserved for fiction, PTS "Telefonnummer till böcker och filmer" (verified 2026-09-21; curl and Playwright are Radware-blocked, a rendering fetcher gets through): https://www.pts.se/internet-och-telefoni/telefonnummer-och-adressering/telefonnummer-till-bocker-och-filmer/ — mobile 070-1740605–1740699, geographic 08-46500400–46500499 (Stockholm), 031-3900600–3900699 (Göteborg), 040-6280400–6280499 (Malmö), 0980-319200–319299 (Kiruna). 495 numbers PTS holds back so they reach nobody, which is goal 15's never-reaching path for `sv_SE.phone`. Same licence status as the numbering plan: none stated, facts only, cite PTS. The mobile block fits the shipped `{a} {b} {c}` grouping (`174 06 05`–`99`), as do 031 and 040; Stockholm's 8 subscriber digits and Kiruna's 6 need a format of their own, and 0980 is not among the 20 shipped area codes.
- 264 geographic area codes (PDF text yields 263 "Area code for" rows; one likely split across a page break — flag). Area code = trunk "0" + NDC of 1–3 digits (08 Stockholm, 031 Göteborg, 040 Malmö, 0480 Kalmar). Geographic N(S)N (NDC + subscriber) max 9 digits, min 7 (2-digit NDC) or 8 (3-digit NDC); Stockholm min 7. Each row in the PDF gives NDC, max/min length, "Area code for <name>", so a `{ndc, name}` list can be built from it.
- Mobile: NDC 70, 72, 73, 76, 79 — N(S)N exactly 9 digits, i.e. 07X-XXX XX XX (10 digits with trunk 0). Others: 71 mobile broadband/M2M (13 digits), 74 paging, 75 personal numbering, 77 shared cost, 20 freephone, 10 location-independent (10 AXX XX XX, A=1–8), 378 M2M fixed (10 digits).
- Formatting (Språkrådet/Isof frågelådan, via search excerpts): hyphen after area code, subscriber digits grouped 2–3 from the left: `08-668 01 50`, `031-123 45 67`, `0480-123 45`, `070-123 45 67` (also `0701-23 45 67` accepted). International: `+46 70 123 45 67` (drop trunk 0).
+253 -260
View File
@@ -6,7 +6,7 @@
`Bar = 9`
`Next ID = 73`
`Next ID = 71`
| Goal | W |
|---|---|
@@ -15,244 +15,116 @@
| 2 | 0.95 |
| 2.1 | 0.95 |
| 2.2 | 0.95 |
| 2.3 | 0.95 |
| 2.4 | 0.95 |
| 3 | 0.90 |
| 3.1 | 0.90 |
| 3.2 | 0.90 |
| 3.3 | 0.90 |
| 3.4 | 0.90 |
| 4 | 0.85 |
| 4.1 | 0.85 |
| 4.2 | 0.85 |
| 4.3 | 0.85 |
| 4.4 | 0.85 |
| 4.5 | 0.85 |
| 4.6 | 0.85 |
| 4.7 | 0.85 |
| 4.8 | 0.85 |
| 5 | 0.80 |
| 5.1 | 0.80 |
| 5.2 | 0.80 |
| 5.3 | 0.80 |
| 5.4 | 0.80 |
| 5.5 | 0.80 |
| 5.6 | 0.80 |
| 5.7 | 0.80 |
| 5.8 | 0.80 |
| 6 | 0.75 |
| 6.1 | 0.75 |
| 6.2 | 0.75 |
| 7 | 0.70 |
| 7.1 | 0.70 |
| 7.2 | 0.70 |
| 8 | 0.65 |
| 9 | 0.60 |
| 10 | 0.55 |
| 10.1 | 0.55 |
| 10.2 | 0.55 |
| 10.3 | 0.55 |
| 11 | 0.50 |
| 11.1 | 0.50 |
| 11.2 | 0.50 |
| 11.3 | 0.50 |
| 12 | 0.45 |
| 12.1 | 0.45 |
| 13 | 0.40 |
| 13.1 | 0.40 |
| 14 | 0.35 |
| 15 | 0.30 |
## Items
| ID | Release | Exempt | Item | R | S | A | G | Goals | Score |
|---|---|---|---|---|---|---|---|---|---|
| 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 | 26.7 |
| 3 | 0.1.0 | | **Rewrite the shipped categories so facts that belong together come from one named pick.** | 5 | 5 | 8 | 9 | 3.1 | 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 | 4.2, 3.1 | 25.0 |
| 25 | 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 | 23.9 |
| 59 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.5 |
| 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 | 22.4 |
| 26 | 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 | 21.9 |
| 35 | 0.1.0 | | **Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.** | 2 | 4 | 6 | 7 | 3 | 21.6 |
| 12 | 0.1.0 | defect | **Strip the whitespace `data-import/territory.py` copies from its register.** | 1 | 1 | 5 | 7 | 3 | 21.4 |
| 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.3 |
| 37 | 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 | 20.3 |
| 60 | 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, 6 | 29.2 |
| 6 | 0.2.0 | question | **Decide how the comprehension gate treats panel noise: identical code scored 5.7 and 5.4.** | 1 | 1 | 3 | 8 | 2 | 19.9 |
| 42 | 0.2.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 | 19.9 |
| 54 | 0.2.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 | 19.7 |
| 28 | 0.2.0 | | **Accept a middle name, and draw a `personnummer` whose sex matches a sex selected through a named pick.** | 4 | 4 | 6 | 7 | 3.1 | 19.6 |
| 24 | 0.2.0 | defect | **Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does not hold.** | 1 | 1 | 4 | 7 | 3 | 19.4 |
| 57 | 0.2.0 | | **Document `NewRecordTemplate`, `ErrNoColumns`, `ErrNoData` and `MaxRepeat` in the README's Library section.** | 1 | 2 | 6 | 7 | 10 | 18.2 |
| 55 | 0.2.0 | | **Let a table column carry a `datatype`, so `--format json` writes `"safe": true` and `--format sql` a boolean.** | 4 | 5 | 6 | 6 | 3 | 17.6 |
| 18 | 0.2.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 | 17.2 |
| 15 | 0.2.0 | | **Add the remaining `misc` tables and templates, one row of its detail's table per chunk.** | 3 | 9 | 8 | 7 | 13, 12 | 17.1 |
| 16 | 0.2.0 | | **Add the remaining locale categories: company, phone, finance, vehicle, words.** | 3 | 8 | 8 | 7 | 13 | 16.6 |
| 32 | 0.2.0 | | **Rename `misc.territory.country` to `sovereign`.** | 3 | 2 | 4 | 7 | 4.8 | 16.4 |
| 45 | 0.2.0 | decision | **Have `misc.tld[se]` select the row keyed `.se`, or have its miss name `[.se]`.** | 1 | 1 | 4 | 6 | 5 | 16.4 |
| 20 | 0.2.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 | 16.3 |
| 52 | 0.2.0 | | **Reword `prove`'s refusal of a typed column reading a row, `{/region}`.** | 1 | 2 | 4 | 6 | 5 | 16.1 |
| 56 | 0.2.0 | defect | **Read the tag through `git/ref/tags/{tag}` in `publish_release.py`.** | 2 | 1 | 3 | 6 | 1.1 | 15.8 |
| 17 | 0.2.0 | | **Give every shipped category a column for each part its format composes, and one record shape across the national ids; re-pin the shape.** | 5 | 6 | 6 | 6 | 4 | 15.7 |
| 49 | 0.2.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 | 15.7 |
| 22 | 0.2.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 | 15.6 |
| 29 | 0.2.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 | 15.2 |
| 43 | 0.2.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 | 15.1 |
| 33 | 0.2.0 | | **Add `misc.browser` as the parent of `misc.useragent`, so `misc.browser[Chrome].useragent` resolves.** | 3 | 3 | 4 | 6 | 3.1 | 15.1 |
| 47 | 0.2.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 | 14.7 |
| 27 | 0.2.0 | | **Merge `en_US.ip` and `sv_SE.ip`, today byte-identical, into one `misc.ip`.** | 3 | 2 | 4 | 6 | 4.7 | 14.7 |
| 14 | 0.2.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 | 14.4 |
| 39 | 0.2.0 | defect | **Compare `calcParser.binary`'s operator as a rune.** | 1 | 1 | 3 | 6 | 5 | 14.4 |
| 46 | 0.2.0 | | **Name the node a selector follows in `stepInto`'s refusal.** | 1 | 1 | 3 | 6 | 5 | 14.4 |
| 51 | 0.2.0 | | **Report a CRLF rows file holding only its header as having no rows.** | 1 | 1 | 3 | 6 | 5 | 14.4 |
| 13 | 0.2.0 | defect | **Place xlsx cells by their `r` reference in `data-import/xlsx.py`.** | 2 | 2 | 3 | 6 | 3 | 14.3 |
| 40 | 0.2.0 | | **Stop the `columnKinds` suggestion naming a field's own kind or a narrower one.** | 1 | 2 | 3 | 6 | 5 | 14.1 |
| 53 | 0.2.0 | | **Spell a table one way across the errors that name it.** | 1 | 2 | 3 | 6 | 5 | 14.1 |
| 10 | 0.3.0 | | **Test that every node kind reaches each switch over node kinds.** | 1 | 3 | 2 | 6 | 2.2 | 13.6 |
| 11 | 0.3.0 | | **Test that every `geo` tree holds the five table names and the `address` columns that port across countries.** | 1 | 2 | 3 | 5 | 3 | 13.5 |
| 36 | 0.3.0 | | **Add `{base64url(n)}` for JWT shapes.** | 1 | 2 | 4 | 5 | 7 | 13.5 |
| 21 | 0.3.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 | 13.4 |
| 23 | 0.3.0 | | **Give every other shipped category that could reach something real a path that never does.** | 3 | 6 | 6 | 7 | 14 | 12.4 |
| 8 | 0.3.0 | | **Move `binding`, `bind` and `checkNodeFences` out of `data.go` into a file of their own.** | 1 | 2 | 2 | 5 | 2.1 | 12.0 |
| 9 | 0.3.0 | | **Move `table.route`, `selector`, `step` and `drawStep` from `path.go` to `table.go`.** | 1 | 2 | 2 | 5 | 2.1 | 12.0 |
| 31 | 0.3.0 | | **Draw `email.local` from `username`, so the two share one handle list.** | 2 | 2 | 3 | 5 | 4.7 | 12.0 |
| 41 | 0.3.0 | | **Parse `defaultTable`'s path with the library's grammar.** | 2 | 2 | 3 | 5 | 4.7 | 12.0 |
| 34 | 0.3.0 | | **Group `data/misc` into folders where a group name makes a path easier to guess.** | 4 | 5 | 4 | 5 | 4 | 11.2 |
| 48 | 0.3.0 | defect | **Refuse a category naming a hidden rows file, `"rows": ".x.tsv"`, as a rows file that is not there.** | 1 | 1 | 2 | 5 | 5 | 10.8 |
| 19 | 0.3.0 | | **Fill the 398 Swedish localities weighted 200 from SCB småorter.** | 2 | 4 | 4 | 6 | 12.1 | 10.4 |
| 7 | 0.3.0 | | **Fill `arm` in one place.** | 4 | 4 | 2 | 6 | 2.2 | 10.4 |
| 38 | 0.3.0 | | **Rename `newRand` to `newGeneratorState` and `Generator.rand` to `state`.** | 1 | 1 | 1 | 5 | 2.3 | 10.2 |
| 44 | 0.3.0 | | **Call `templateError` a compile failure in its doc.** | 1 | 1 | 1 | 5 | 2.3 | 10.2 |
| 30 | 0.3.0 | | **Read `email.local`'s and `username`'s handles from the shipped name tables.** | 3 | 4 | 4 | 6 | 12 | 9.4 |
| 50 | 0.3.0 | | **Print `--help`'s repeat range from `fejkdata.MaxRepeat`.** | 1 | 1 | 2 | 6 | 10.3 | 9.4 |
| 58 | 0.3.0 | defect | **Build on a manual run of `test.yml`.** | 1 | 1 | 1 | 4 | 1 | 8.8 |
| 65 | 0.4.0 | | **Pair a street with its exact postnummer.** | 3 | 6 | 5 | 8 | 3.1 | 19.9 |
| 64 | 0.4.0 | | **Add `{btc()}` and `{eth()}`.** | 2 | 4 | 4 | 6 | 3 | 15.8 |
| 63 | 0.4.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 | 15.4 |
| 61 | 0.4.0 | | **Promise in `Generator`'s godoc that its renders run one at a time.** | 1 | 1 | 5 | 5 | 10 | 14.2 |
| 67 | 0.4.0 | | **Hold every change `AGENTS.md` says owes a `CHANGELOG.md` entry to one in CI.** | 2 | 3 | 4 | 5 | 4.8 | 13.8 |
| 62 | 0.4.0 | | **Ship the full registers as packs, each a Go module with its own `embed.FS` and a zip for `--data-path`.** | 5 | 8 | 7 | 6 | 13, 11 | 13.0 |
| 68 | 0.4.0 | | **Cut the README's Layout block to the lines that say what a file name cannot.** | 1 | 1 | 2 | 5 | 2.4 | 12.2 |
| 69 | 0.4.0 | | **Cut `AGENTS.md` to the rules only it states, and explain every term its comprehension rule uses.** | 1 | 2 | 1 | 6 | 2.4 | 11.9 |
| 66 | 0.4.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 | 11.4 |
| 70 | 0.4.0 | decision | **Make `gitea.larvit.se/larvit/fejkdata` a pull mirror of GitHub.** | 2 | 1 | 1 | 3 | 1.1 | 5.8 |
| 71 | 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 | 30.2 |
| 72 | 1.0.0 | | **Publish a homepage with an in-browser generator, the library compiled to WebAssembly.** | 3 | 6 | 6 | 6 | 1.1 | 19.5 |
| 16 | 0.1.0 | | **Add the remaining locale categories: company, phone, finance, vehicle, words.** | 3 | 8 | 8 | 9 | 2.2, 14 | 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, 14, 13 | 25.9 |
| 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 | 5.3, 4.1 | 25.8 |
| 3 | 0.1.0 | | **Rewrite the shipped categories so facts that belong together come from one named pick.** | 5 | 5 | 8 | 9 | 4.1 | 25.0 |
| 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 |
| 25 | 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 | 4 | 23.1 |
| 57 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.5 |
| 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 | 5.4 | 21.6 |
| 26 | 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 | 4 | 21.1 |
| 34 | 0.1.0 | | **Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.** | 2 | 4 | 6 | 7 | 4 | 20.9 |
| 12 | 0.1.0 | defect | **Strip the whitespace `data-import/territory.py` copies from its register.** | 1 | 1 | 5 | 7 | 4 | 20.6 |
| 5 | 0.1.0 | | **Run the nine-seat comprehension panel after the draw restructure, and file what it names.** | 1 | 3 | 3 | 9 | 3 | 20.4 |
| 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 | 3 | 19.1 |
| 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 |
| 36 | 0.2.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 | 6 | 19.5 |
| 41 | 0.2.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 | 5.7 | 19.1 |
| 52 | 0.2.0 | | **Name the file a link error comes from beside its category path, `sub.x (/d/a/sub/x.json): …`.** | 1 | 2 | 5 | 7 | 6 | 19.0 |
| 28 | 0.2.0 | | **Accept a middle name, and draw a `personnummer` whose sex matches a sex selected through a named pick.** | 4 | 4 | 6 | 7 | 4.1 | 18.9 |
| 24 | 0.2.0 | defect | **Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does not hold.** | 1 | 1 | 4 | 7 | 4 | 18.6 |
| 55 | 0.2.0 | | **Document `NewRecordTemplate`, `ErrNoColumns`, `ErrNoData` and `MaxRepeat` in the README's Library section.** | 1 | 2 | 6 | 7 | 11 | 17.5 |
| 53 | 0.2.0 | | **Let a table column carry a `datatype`, so `--format json` writes `"safe": true` and `--format sql` a boolean.** | 4 | 5 | 6 | 6 | 4 | 16.9 |
| 18 | 0.2.0 | | **Give the address records one column set across countries: `region` and `municipality` as columns on `geo.SE.address` too.** | 4 | 4 | 6 | 6 | 5 | 16.6 |
| 44 | 0.2.0 | decision | **Have `misc.tld[se]` select the row keyed `.se`, or have its miss name `[.se]`.** | 1 | 1 | 4 | 6 | 6 | 15.8 |
| 54 | 0.2.0 | defect | **Read the tag through `git/ref/tags/{tag}` in `publish_release.py`.** | 2 | 1 | 3 | 6 | 1.1 | 15.8 |
| 31 | 0.2.0 | | **Rename `misc.territory.country` to `sovereign`.** | 3 | 2 | 4 | 7 | 5.8 | 15.7 |
| 20 | 0.2.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 | 4 | 15.7 |
| 50 | 0.2.0 | | **Reword `prove`'s refusal of a typed column reading a row, `{/region}`.** | 1 | 2 | 4 | 6 | 6 | 15.5 |
| 17 | 0.2.0 | | **Give every shipped category a column for each part its format composes, and one record shape across the national ids; re-pin the shape.** | 5 | 6 | 6 | 6 | 5 | 15.1 |
| 48 | 0.2.0 | defect | **Refuse a `Fake` path into a level carrying a `repeat`, as the README's Correlated fields section states.** | 2 | 2 | 4 | 6 | 5 | 15.1 |
| 22 | 0.2.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 | 15 | 14.8 |
| 29 | 0.2.0 | | **Let a path go from a selected row into the template beside its family: `geo.US.locality[1714000].address`.** | 4 | 4 | 5 | 6 | 5 | 14.6 |
| 42 | 0.2.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 | 6 | 14.5 |
| 32 | 0.2.0 | | **Add `misc.browser` as the parent of `misc.useragent`, so `misc.browser[Chrome].useragent` resolves.** | 3 | 3 | 4 | 6 | 4.1 | 14.4 |
| 27 | 0.2.0 | | **Merge `en_US.ip` and `sv_SE.ip`, today byte-identical, into one `misc.ip`.** | 3 | 2 | 4 | 6 | 5.7 | 14.1 |
| 46 | 0.2.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 | 6, 9 | 14.0 |
| 14 | 0.3.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 | 5.7 | 13.9 |
| 38 | 0.3.0 | defect | **Compare `calcParser.binary`'s operator as a rune.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
| 45 | 0.3.0 | | **Name the node a selector follows in `stepInto`'s refusal.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
| 49 | 0.3.0 | | **Report a CRLF rows file holding only its header as having no rows.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
| 13 | 0.3.0 | defect | **Place xlsx cells by their `r` reference in `data-import/xlsx.py`.** | 2 | 2 | 3 | 6 | 4 | 13.7 |
| 39 | 0.3.0 | | **Stop the `columnKinds` suggestion naming a field's own kind or a narrower one.** | 1 | 2 | 3 | 6 | 6 | 13.5 |
| 51 | 0.3.0 | | **Spell a table one way across the errors that name it.** | 1 | 2 | 3 | 6 | 6 | 13.5 |
| 10 | 0.3.0 | | **Test that every node kind reaches each switch over node kinds.** | 1 | 3 | 2 | 6 | 3.2 | 13.1 |
| 11 | 0.3.0 | | **Test that every `geo` tree holds the five table names and the `address` columns that port across countries.** | 1 | 2 | 3 | 5 | 4 | 13.0 |
| 35 | 0.3.0 | | **Add `{base64url(n)}` for JWT shapes.** | 1 | 2 | 4 | 5 | 8 | 13.0 |
| 21 | 0.3.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 | 11 | 12.8 |
| 23 | 0.3.0 | | **Give every other shipped category that could reach something real a path that never does.** | 3 | 6 | 6 | 7 | 15 | 11.7 |
| 8 | 0.3.0 | | **Move `binding`, `bind` and `checkNodeFences` out of `data.go` into a file of their own.** | 1 | 2 | 2 | 5 | 3.1 | 11.5 |
| 9 | 0.3.0 | | **Move `table.route`, `selector`, `step` and `drawStep` from `path.go` to `table.go`.** | 1 | 2 | 2 | 5 | 3.1 | 11.5 |
| 30 | 0.3.0 | | **Draw `email.local` from `username`, so the two share one handle list.** | 2 | 2 | 3 | 5 | 5.7 | 11.5 |
| 40 | 0.3.0 | | **Parse `defaultTable`'s path with the library's grammar.** | 2 | 2 | 3 | 5 | 5.7 | 11.5 |
| 33 | 0.3.0 | | **Group `data/misc` into folders where a group name makes a path easier to guess.** | 4 | 5 | 4 | 5 | 5 | 10.8 |
| 47 | 0.3.0 | defect | **Refuse a category naming a hidden rows file, `"rows": ".x.tsv"`, as a rows file that is not there.** | 1 | 1 | 2 | 5 | 6 | 10.2 |
| 7 | 0.3.0 | | **Fill `arm` in one place.** | 4 | 4 | 2 | 6 | 3.2 | 9.8 |
| 19 | 0.3.0 | | **Fill the 398 Swedish localities weighted 200 from SCB småorter.** | 2 | 4 | 4 | 6 | 13.1 | 9.8 |
| 37 | 0.3.0 | | **Rename `newRand` to `newGeneratorState` and `Generator.rand` to `state`.** | 1 | 1 | 1 | 5 | 3.3 | 9.8 |
| 43 | 0.3.0 | | **Call `templateError` a compile failure in its doc.** | 1 | 1 | 1 | 5 | 3.3 | 9.8 |
| 56 | 0.3.0 | defect | **Build on a manual run of `test.yml`.** | 1 | 1 | 1 | 4 | 1 | 8.8 |
| 61 | 0.4.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 |
| 63 | 0.4.0 | | **Pair a street with its exact postnummer.** | 3 | 6 | 5 | 8 | 4.1 | 19.1 |
| 62 | 0.4.0 | | **Add `{btc()}` and `{eth()}`.** | 2 | 4 | 4 | 6 | 4 | 15.2 |
| 59 | 0.4.0 | | **Promise in `Generator`'s godoc that its renders run one at a time.** | 1 | 1 | 5 | 5 | 11 | 13.8 |
| 65 | 0.4.0 | | **Hold every change `AGENTS.md` says owes a `CHANGELOG.md` entry to one in CI.** | 2 | 3 | 4 | 5 | 5.8 | 13.2 |
| 60 | 0.4.0 | | **Ship the full registers as packs, each a Go module with its own `embed.FS` and a zip for `--data-path`.** | 5 | 8 | 7 | 6 | 14, 12 | 12.4 |
| 66 | 0.4.0 | | **Cut the README's Layout block to the lines that say what a file name cannot.** | 1 | 1 | 2 | 5 | 3.4 | 11.8 |
| 67 | 0.4.0 | | **Cut `AGENTS.md` to the rules only it states, and explain every term its comprehension rule uses.** | 1 | 2 | 1 | 6 | 3.4 | 11.3 |
| 64 | 0.4.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 | 3.4 | 10.8 |
| 68 | 0.4.0 | decision | **Make `gitea.larvit.se/larvit/fejkdata` a pull mirror of GitHub.** | 2 | 1 | 1 | 3 | 1.1 | 5.8 |
| 69 | 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 | 30.2 |
| 70 | 1.0.0 | | **Publish a homepage with an in-browser generator, the library compiled to WebAssembly.** | 3 | 6 | 6 | 6 | 1.1 | 19.5 |
## Details
### 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 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.
### 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` 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 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.
### 25. 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.
### 59. 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.
### 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`.
- `..` goes up one level, to the parent table, and the name after it is that parent's: `{c..country.name}`, and from a locality `{l..municipality..region.name}`. Naming any other table is a load error naming the parent.
- It steps up from a selected row, a drawn row or a name bound to a row.
- 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.
### 26. 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.
### 35. 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.
### 12. Strip the whitespace `data-import/territory.py` copies from its register.
`misc.territory[CW].capital` renders ` Willemstad`, with a leading space.
### 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.
### 37. 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.
### 60. 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.
### 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`. Run 1 is the recorded score; run 2 reran the same code to measure noise. A spread of 0.3 is close to the whole 5.7–6.1 range the recorded scores span since 2026-09-20, so `AGENTS.md`'s "no merge lowers the last score" can block or pass a chunk by chance. Until this item is decided, that rule compares against 5.7, from 2026-09-30.
### 42. 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`.
### 54. 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.
### 28. Accept a middle name, and draw a `personnummer` whose sex matches a sex selected through a named pick.
Needs item 4.
### 24. 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.
### 55. 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.
### 15. Add the remaining `misc` tables and templates, one row of its detail's table per chunk.
Shape: T = table, t = template, c = choice.
| Category | Shape | Source | Licence |
|---|---|---|---|
| `ip` v4 documentation ranges, private, CIDR; `ipv6` `2001:db8::/32`; `mac` locally administered | t | RFC 5737, 1918, 9637 | facts |
| `creditcard` per network: IIN, length, CVV, expiry | T+t | network rules, `{luhn()}` | facts |
| `bic`, `isin`, `cusip` | t | structure rules | facts |
| `ean`, `upc`, `gtin`, `isbn`, `issn`, `imei`, `asin`, `sku` | t | check-digit rules, GS1 demo prefix 952 | facts |
| `product` name, adjective, material, department, `garmentsize` | T/c | LLM-written lists | — |
| `airport` IATA, ICAO, territory, municipality; `airline`; `aircraft`; `flight`, `seat`, `pnr` | T+t | OurAirports, Wikidata | public domain, CC0 |
| `element`, `unit`, `planet` | T | PubChem, BIPM, NASA | public domain, CC BY 4.0 |
| `animal` by class, `plant` | T | Wikidata | CC0 |
| `bloodtype` weighted (global) | c | AABB | facts |
| `color` named, hex, rgb, hsl, cmyk | T+t | CSS Color 4, XKCD | W3C, CC0 |
| `programminglanguage`, `os`, `browser`, `database` column/type/engine, `fileext` | T/c | Linguist, curated | MIT |
| `commit` message, branch, `sha` | t | LLM-written, `{hex(40)}` | — |
| `password`, `hash`, `jwt`, `slug`, `hostname`, `avatar`, `placeholderimage` | t | shapes | — |
| `book` title, author; `genre`; `instrument`; `dish`, `ingredient` | T | Gutenberg catalog, MusicBrainz, USDA | CC0 |
| `lorem`, `hacker`, `hipster`, `catchphrase`, `buzzword`, `quote` | T/c | lorem ipsum, LLM-written | — |
| `direction`, `continent`, `ulid` | c/t | — | — |
### 16. Add the remaining locale categories: company, phone, finance, vehicle, words.
Shape: T = table, t = template, c = choice.
@@ -287,31 +159,160 @@ Shape: T = table, t = template, c = choice.
| `word`, `sentence`, `paragraph` | T+t | Moby POS or WordNet | public domain / WordNet |
| month and weekday names, `holiday` | t+T | CLDR en | Unicode |
### 32. Rename `misc.territory.country` to `sovereign`.
### 15. Add the remaining `misc` tables and templates, one row of its detail's table per chunk.
It holds a code, and `country` collides with the `misc.country` path it replaced.
Shape: T = table, t = template, c = choice.
### 45. Have `misc.tld[se]` select the row keyed `.se`, or have its miss name `[.se]`.
| Category | Shape | Source | Licence |
|---|---|---|---|
| `ip` v4 documentation ranges, private, CIDR; `ipv6` `2001:db8::/32`; `mac` locally administered | t | RFC 5737, 1918, 9637 | facts |
| `creditcard` per network: IIN, length, CVV, expiry | T+t | network rules, `{luhn()}` | facts |
| `bic`, `isin`, `cusip` | t | structure rules | facts |
| `ean`, `upc`, `gtin`, `isbn`, `issn`, `imei`, `asin`, `sku` | t | check-digit rules, GS1 demo prefix 952 | facts |
| `product` name, adjective, material, department, `garmentsize` | T/c | LLM-written lists | — |
| `airport` IATA, ICAO, territory, municipality; `airline`; `aircraft`; `flight`, `seat`, `pnr` | T+t | OurAirports, Wikidata | public domain, CC0 |
| `element`, `unit`, `planet` | T | PubChem, BIPM, NASA | public domain, CC BY 4.0 |
| `animal` by class, `plant` | T | Wikidata | CC0 |
| `bloodtype` weighted (global) | c | AABB | facts |
| `color` named, hex, rgb, hsl, cmyk | T+t | CSS Color 4, XKCD | W3C, CC0 |
| `programminglanguage`, `os`, `browser`, `database` column/type/engine, `fileext` | T/c | Linguist, curated | MIT |
| `commit` message, branch, `sha` | t | LLM-written, `{hex(40)}` | — |
| `password`, `hash`, `jwt`, `slug`, `hostname`, `avatar`, `placeholderimage` | t | shapes | — |
| `book` title, author; `genre`; `instrument`; `dish`, `ingredient` | T | Gutenberg catalog, MusicBrainz, USDA | CC0 |
| `lorem`, `hacker`, `hipster`, `catchphrase`, `buzzword`, `quote` | T/c | lorem ipsum, LLM-written | — |
| `direction`, `continent`, `ulid` | c/t | — | — |
### 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 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.
### 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` 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 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.
### 25. 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.
### 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.
### 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`.
- `..` goes up one level, to the parent table, and the name after it is that parent's: `{c..country.name}`, and from a locality `{l..municipality..region.name}`. Naming any other table is a load error naming the parent.
- It steps up from a selected row, a drawn row or a name bound to a row.
- 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.
### 26. 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.
### 34. 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.
### 12. Strip the whitespace `data-import/territory.py` copies from its register.
`misc.territory[CW].capital` renders ` Willemstad`, with a leading space.
### 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.
### 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`. Run 1 is the recorded score; run 2 reran the same code to measure noise. A spread of 0.3 is close to the whole 5.7–6.1 range the recorded scores span since 2026-09-20, so `AGENTS.md`'s "no merge lowers the last score" can block or pass a chunk by chance. Until this item is decided, that rule compares against 5.7, from 2026-09-30.
### 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.
### 36. 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.
### 41. 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`.
### 52. 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.
### 28. Accept a middle name, and draw a `personnummer` whose sex matches a sex selected through a named pick.
Needs item 4.
### 24. 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.
### 53. 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.
### 44. Have `misc.tld[se]` select the row keyed `.se`, or have its miss name `[.se]`.
The decision "`misc.tld` keys carry the leading dot, where other tables key on a bare code" leaves `misc.tld[se]` missing today.
### 54. 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.
### 31. Rename `misc.territory.country` to `sovereign`.
It holds a code, and `country` collides with the `misc.country` path it replaced.
### 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.
### 52. Reword `prove`'s refusal of a typed column reading a row, `{/region}`.
### 50. 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.
### 56. 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.
### 17. Give every shipped category a column for each part its format composes, and one record shape across the national ids; re-pin the shape.
`misc.uuid` gains `variant`, for one.
### 49. Refuse a `Fake` path into a level carrying a `repeat`, as the README's Correlated fields section states.
### 48. 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.
@@ -323,11 +324,11 @@ GitHub answers `tags/{tag}` with 404 whatever exists, so the burnt-version guard
Require the path step to reach a sibling category.
### 43. Refuse a struct tag's `datatype` naming the Go type that sets it before proving its values, as the README's Library section promises.
### 42. 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`.
### 47. Report the same error every load for a table with two bad options, and for a folder with two unnamed rows files.
### 46. 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.
@@ -335,15 +336,15 @@ Require the path step to reach a sibling category.
Both render the same run.
### 39. Compare `calcParser.binary`'s operator as a rune.
### 38. 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`.
### 46. Name the node a selector follows in `stepInto`'s refusal.
### 45. Name the node a selector follows in `stepInto`'s refusal.
`sv_SE.person[1].first` answers `1 is not a table`.
### 51. Report a CRLF rows file holding only its header as having no rows.
### 49. Report a CRLF rows file holding only its header as having no rows.
`parseRows` answers "line 2 is empty".
@@ -351,11 +352,11 @@ Both render the same run.
A sheet omitting an empty cell shifts every later column left.
### 40. Stop the `columnKinds` suggestion naming a field's own kind or a narrower one.
### 39. 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`.
### 53. Spell a table one way across the errors that name it.
### 51. Spell a table one way across the errors that name it.
`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.
@@ -379,11 +380,11 @@ The greenfield architect found the fence pipeline last. Needs item 4, which shri
The inherited architect's 3am trace crossed four files for one draw.
### 41. Parse `defaultTable`'s path with the library's grammar.
### 40. 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.
### 48. Refuse a category naming a hidden rows file, `"rows": ".x.tsv"`, as a rows file that is not there.
### 47. 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.
@@ -391,66 +392,58 @@ The CLI counts brackets of its own, so a change to the selector grammar desyncs
`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.
### 38. Rename `newRand` to `newGeneratorState` and `Generator.rand` to `state`.
### 37. 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.
### 44. Call `templateError` a compile failure in its doc.
### 43. Call `templateError` a compile failure in its doc.
Its doc says render failure, and it wraps what `NewTemplate` and `NewRecordTemplate` refuse.
### 30. Read `email.local`'s and `username`'s handles from the shipped name tables.
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.
### 50. Print `--help`'s repeat range from `fejkdata.MaxRepeat`.
The usage spells `1..1048576` a second time.
### 58. Build on a manual run of `test.yml`.
### 56. 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`.
### 65. Pair a street with its exact postnummer.
### 61. Add a locale, with its `geo/` tree, for each of the ten most-spoken languages and each Nordic country.
Pick the ten from a published ranking of languages by total speakers, such as Ethnologue's, each in the country where it has the most speakers, and record the ranking in the decision that names them. The Nordic ones are sv_SE (shipped), nb_NO, da_DK, fi_FI and is_IS. Each locale carries goal 2.2's categories, and its `address` reads its country's `geo/` tree by reference.
### 63. 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.
### 64. Add `{btc()}` and `{eth()}`.
### 62. Add `{btc()}` and `{eth()}`.
They need sha256 and keccak for Base58Check and EIP-55.
### 63. Add the locales nb_NO, da_DK, fi_FI, de_DE, en_GB, nl_NL, fr_FR and es_ES, and their `geo/` trees.
Each locale carries person, address, phone, national id, company and date names. Their `address` reads the `geo/` trees NO, DK, FI, NL, FR, AU, CA, ES, GB and DE by reference; AU and CA get a tree with no locale.
### 61. Promise in `Generator`'s godoc that its renders run one at a time.
### 59. Promise in `Generator`'s godoc that its renders run one at a time.
A caller wanting parallel throughput then makes one generator per goroutine.
### 67. Hold every change `AGENTS.md` says owes a `CHANGELOG.md` entry to one in CI.
### 65. 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.
### 62. Ship the full registers as packs, each a Go module with its own `embed.FS` and a zip for `--data-path`.
### 60. 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.
### 68. Cut the README's Layout block to the lines that say what a file name cannot.
### 66. 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.
### 69. Cut `AGENTS.md` to the rules only it states, and explain every term its comprehension rule uses.
### 67. Cut `AGENTS.md` to the rules only it states, and explain every term its comprehension rule uses.
The `gh` rule opens by restating the decision "GitHub is canonical, and the module path names it", 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.
### 66. Give every entry in `docs/decisions.md` the goal it serves, its date, who made it and the premise it rests on.
### 64. 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.
### 70. Make `gitea.larvit.se/larvit/fejkdata` a pull mirror of GitHub.
### 68. 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.
### 71. 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.
### 69. 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; pairing a street with its exact postnummer rewrites shipped rows, so a minor with no breaking change follows it first.