Answer prose round 1: plainer decision titles and body, comments that say what the code cannot, and claims New no longer makes

This commit is contained in:
2026-10-03 17:57:04 +02:00
committed by Lillem4n
parent a9eb337ec7
commit 53431ab908
9 changed files with 56 additions and 52 deletions
+2 -2
View File
@@ -43,7 +43,7 @@ In [docs/decisions.md](docs/decisions.md):
- A padded JSON argument is rejected, not trimmed
- `FakeTemplate` and `NewTemplate` both stay
- The shipped data is embedded, not discovered
- The shipped set alone loads a category on the first call reaching it; beside a `--data-path` every category loads in `New`
- With only the shipped set, a category loads on the first call reaching it; beside a `--data-path`, every category loads in `New`
- A bare reference draws each time; a reference path is held
- Reference sigils follow the filesystem
- A change to what exists is a major; a minor only adds
@@ -94,7 +94,7 @@ In [docs/decisions.md](docs/decisions.md):
- Tables carrying token cells stay small
- A path is walked once without drawing before it is walked for real
- A country's postal codes and streets are siblings under its locality
- A locale's `address` reads its country's `geo` tree, so a locale folder never loads alone
- A locale's `address` reads its country's `geo` tree, so a locale folder is no data set on its own
- The default embed holds every Swedish postort the import can place and give a street-delivery code and a street, and the US places of 25,000 or more
- A locale's `address` restates its country record's format
- A postort's kommun comes from its name, its tätort or its codes, never from distance
+7 -6
View File
@@ -499,7 +499,7 @@ table, `{/misc.currency[SEK].symbol}` on one row and `{/misc.currency[EUR].symbo
on the next, since only one row renders. `New` proves the header, the options and every
cell token, and refuses a TSV no category names, a key that is empty or repeats, a
weight that is not a positive number, and a key or name holding `[`, `]`, `{`, `}`,
`"` or `|`, which a selector cannot spell; `New` maps the keys, and a name or parent column
`"` or `|`, which a selector cannot spell; the table's load maps the keys, and a name or parent column
is mapped on the first draw that selects by name or descends through the table. A `name` needs a `key`, since a name naming several rows is reported by
their keys, or a `parent`, inside whose row a name names one row, so `first-name[Kim]`
is settled by the `sex` selected before it and a name repeating inside one parent row
@@ -813,11 +813,12 @@ token {w} is repeated, and uppercase operand "w" holds "w" to one draw per expan
The target is about a microsecond per value (goal 12).
Each file is parsed, validated and weight-indexed once, bar a table's name and parent
columns, which are mapped on the first `Fake` that selects by name or descends through
it. With the shipped set alone, `New` loads nothing: a category loads on the first call
reaching it, with every category it reads and every table linked to it. Beside
`--data-path`, `WithDataPath` or `WithDataFS`, every category loads in `New`. Proving the draw fences adds a pass over the loaded tree per
With only the shipped set, `New` loads nothing: a category loads on the first `Fake`,
`FakeRecord`, `FakeStruct` or template that reads it, with every category it reads and
its whole table family. Beside `--data-path`, `WithDataPath` or `WithDataFS`, every
category loads in `New`. Each file is parsed, validated and weight-indexed once, bar a
table's name and parent columns, which are mapped on the first `Fake` that selects by
name or descends through it. Proving the draw fences adds a pass over the loaded tree per
fence, and walks what a render reads only where data holds a reference, so a set that
holds none pays for the passes alone. A `Fake` call then costs about what its output
costs: an unweighted pick is O(1) whatever the list's length, a weighted one
+2 -4
View File
@@ -42,7 +42,5 @@ package fejkdata
// `table.parentT`, named by the root that chain ends at: `table.familyRoot`.
// - whole — a read of a table with no selector and no descent, {/city}, landing on
// the `table` itself, so it draws a row apart from every pin: `branches.wholePins`.
// - fence — a load-time check, so rendering a compiled tree cannot fail: `New`
// runs them over the sources it loads, the first call reaching a shipped category
// over what that call loads, and `NewTemplate` and `FakeStruct` over what those
// compile.
// - fence — a load-time check, run over each category, template or struct as it
// loads or compiles, so rendering a compiled tree cannot fail.
+22 -17
View File
@@ -80,7 +80,7 @@ tags and any other caller read one.
2026-09-03, Lilleman auf Larv.
`New` proves the loaded tree acyclic, an inline node is a finite tree of its own, and
Each load proves what it loads acyclic, an inline node is a finite tree of its own, and
nothing in the tree can reference it, so no render of it reaches itself.
## An inline template that does not compile is misuse (exit 2), including a reference that resolves to nothing
@@ -116,19 +116,23 @@ still lives in `data/` as JSON; `--data-path` layers over it.
`todo.md` item 77 revises it: a library imports the shipped data as Go modules.
## The shipped set alone loads a category on the first call reaching it; beside a `--data-path` every category loads in `New`
## With only the shipped set, a category loads on the first call reaching it; beside a `--data-path`, every category loads in `New`
2026-10-03, a restructure the maintainer approved. Goal 12: a
`New` pays nothing for the categories a run never reaches, which matters once the shipped
set grows to hundreds of MB. A category loads with every category it references and its
whole table family, and is bound as a whole load binds, so it renders as it would from
`New`. `List` reads `shippedindex.go`, which `go generate` writes from `data/`, and
`TestShippedIndexIsCurrent` fails while it is stale. CI loads the whole shipped set, and
loads each shipped category alone into a fresh `New`, so none fails at first reach. Goal 6: beside a `--data-path` or a
`WithDataFS`, every category loads in `New`, so each mistake in a source the user wrote is
`New`'s error, though it parses the whole shipped set, against goal 12, until
`todo.md` item 79 loads only what the user's sources reach. Valid while CI proves the shipped set
whole and each category alone.
2026-10-03, a restructure the maintainer approved.
Goal 12: `New` pays nothing for a category a run never reaches, which matters once the
shipped set grows to hundreds of MB. A category loads with every category it references
and its whole table family, and binds as a whole load does, so it renders the same as
after a whole load. `List` reads `shippedindex.go`, which `go generate` writes from
`data/`; `TestShippedIndexIsCurrent` fails while the index is stale. CI loads the whole
shipped set, and each shipped category alone into a fresh `New`, so none fails at first
reach.
Goal 6: beside a `--data-path`, `WithDataPath` or `WithDataFS`, every category loads in
`New`, so each mistake in the user's data is `New`'s error. That parses the whole shipped
set, against goal 12, until `todo.md` item 79 ends it.
Valid while CI proves the shipped set whole and each category alone.
## A bare reference draws each time; a reference path is held
@@ -171,7 +175,8 @@ release instead. Before `v1.0.0` a minor carries what a major would.
Any edit to a category shifts its stream and everything drawn after it, so a promise
across versions would freeze every shipped list; a fixture is re-pinned on a bump, as
this repo's own are. Goal 9 reads wider than this until `todo.md` item 85 rewords it.
this repo's own are. This works against goal 9's "always" until `todo.md` item 85
rewords the goal.
## An error is a contract by what it names, not its bytes
@@ -654,7 +659,7 @@ its ZIPs would shape the two trees differently, so both draw inside the pinned l
and an address agrees at that level. A street's own code is the exact pairing to add
when a source carries it.
## A locale's `address` reads its country's `geo` tree, so a locale folder never loads alone
## A locale's `address` reads its country's `geo` tree, so a locale folder is no data set on its own
2026-09-24, Lilleman auf Larv.
@@ -911,8 +916,8 @@ mode field, leaked the check's leaves and errors with the draw's pins, arm and m
since Go tracks a struct's fields as one, and a repeat of a reference path rose from 66
to 106 allocations. `drawSteps` takes its state as parameters and a step holds no node,
so no benchmark gained an allocation. Valid while Go's escape analysis tracks
a struct's fields as one. The two loops have drifted, against DRY, until `todo.md` item 80
walks both through one per-step function.
a struct's fields as one. Two loops over one rule work against DRY, and they already
disagree on a repeat level; `todo.md` item 80 walks both through one per-step function.
## A name lives in the category binding it, or in the repeat binding it, and is drawn on its first read
+6 -6
View File
@@ -22,11 +22,11 @@ var _ [^uint(0)>>63 - 1]struct{} // docs/decisions.md#64-bit-targets-only
// ErrNoData is returned by New when no source is loaded at all.
var ErrNoData = errors.New("no data: WithoutShippedData needs at least one WithDataPath or WithDataFS")
// Generator generates fake data from a loaded namespace tree. Create one with [New].
// Generator generates fake data from a namespace tree. Create one with [New].
// It is safe for concurrent use; a seeded sequence is reproducible only when drawn
// from one goroutine.
type Generator struct {
// mu guards rand, records, structs and root, which loads a shipped category on the
// mu guards rand, records, structs and root, which gains a shipped category on the
// first call reaching it.
mu sync.Mutex
rand *generatorState
@@ -86,8 +86,8 @@ func WithoutShippedData() Option {
// in order with the last winning a name clash. Each JSON file becomes a category
// named after the file (address.json -> "address") and each subdirectory a
// namespace segment. It errors on a missing directory, invalid JSON, invalid data,
// or no data at all. The shipped set alone loads each category on the first call
// reaching it; beside WithDataPath or WithDataFS, every category loads here.
// or no data at all. With only the shipped data, a category loads on the first call
// reaching it; with WithDataPath or WithDataFS, every category loads here.
func New(opts ...Option) (*Generator, error) {
c := config{shipped: true}
for _, opt := range opts {
@@ -104,8 +104,8 @@ func New(opts ...Option) (*Generator, error) {
return &Generator{rand: rng, root: root}, nil
}
// load is the tree New starts from: beside a source of the caller's, every category loads
// here, so each mistake in that source is New's error.
// load is the tree New starts from.
// docs/decisions.md#with-only-the-shipped-set-a-category-loads-on-the-first-call-reaching-it-beside-a---data-path-every-category-loads-in-new
func (c config) load() (folder, error) {
if c.shipped && len(c.sources) == 0 {
return unloadedTree(), nil
+3 -3
View File
@@ -148,8 +148,7 @@ func inputValue(input string) (any, error) {
return raw, nil
}
// bindInline loads the shipped categories n reads, then binds n against root; typedByGo is
// binding's.
// bindInline loads the shipped categories n reads, then binds n against root.
func bindInline(root *folder, n node, label string, typedByGo bool) error {
scope := inlineScope(n, label)
if err := refuseFolderRefs(scope); err != nil {
@@ -169,7 +168,8 @@ func bindInline(root *folder, n node, label string, typedByGo bool) error {
}.bind()
}
// refuseFolderRefs refuses a reference an inline node writes from a folder, which it has none of.
// refuseFolderRefs refuses each reference in scope not written from the root, {/x}: an
// inline node sits in no folder.
func refuseFolderRefs(scope nodeScope) error {
return scope(func(path string, m node) error {
t, ok := m.(*template)
+9 -9
View File
@@ -15,8 +15,8 @@ var shippedFS embed.FS
var shippedSource = dataSource{fsys: shippedFS, baseDir: "data"}
// shippedEntry is a shipped category's entry in shippedindex.go: the table beside it that
// it links to, "" for none, and the paths List advertises below it. After changing it, empty
// shippedEntry is a shipped category's entry in shippedindex.go: its parent table, "" for
// none, and the paths List advertises below it. After changing it, empty
// shippedIndex's literal by hand so the package builds, then run generate.
type shippedEntry struct {
parent string
@@ -97,9 +97,10 @@ func unloadedReads(root *folder, dir []string, scope nodeScope) []unloadedCatego
return out
}
// loadShipped loads the shipped categories wanted, with every category they read and every
// table linked to one, then binds what it loaded, as New binds a whole load.
// TestEveryShippedCategoryLoadsAlone proves each closure, so a failure is the index's.
// loadShipped loads the shipped categories wanted, with every category they read and the
// family of each, then binds what it loaded as a whole load binds.
// TestEveryShippedCategoryLoadsAlone reaches each category, so a failure here means a stale
// index.
func loadShipped(root *folder, wanted []unloadedCategory) {
var sites []categorySite
for queue := wanted; len(queue) > 0; queue = queue[1:] {
@@ -124,8 +125,8 @@ func loadShipped(root *folder, wanted []unloadedCategory) {
}
}
// load parses and compiles the category, and moves it from its folder's unloaded
// categories to its children.
// load parses and compiles the category, and moves it from its folder's unloaded map to
// the folder's children.
func (u unloadedCategory) load() (categorySite, error) {
dir := path.Join(append([]string{shippedSource.baseDir}, u.dir...)...)
entries, err := fs.ReadDir(shippedSource.fsys, dir)
@@ -140,8 +141,7 @@ func (u unloadedCategory) load() (categorySite, error) {
return siteIn(u.dir, u.in, u.name), nil
}
// linkedTables is the tables beside u that link to it or that it links to: its parent, and
// every table whose parent it is.
// linkedTables is u's parent beside it, and every table beside it whose parent u is.
func (u unloadedCategory) linkedTables(e shippedEntry) []unloadedCategory {
var out []unloadedCategory
if e.parent != "" {
+1 -1
View File
@@ -39,7 +39,7 @@ func loadedCategories(f *Generator) []string {
return out
}
// REGENERATE=1, which go generate sets, rewrites the index.
// REGENERATE=1 rewrites the index.
func TestShippedIndexIsCurrent(t *testing.T) {
got, err := shippedIndexSource(newShippedWhole(t))
if err != nil {
+4 -4
View File
@@ -67,7 +67,7 @@
| 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 |
| 71 | 0.1.0 | | **Accept a name as a `calc` operand, so `{calc(n * 2)}` computes from the pick `{n}` prints.** | 3 | 4 | 5 | 7 | 5.3 | 17.2 |
| 79 | 0.1.0 | decision | **Beside a `--data-path`, load only the shipped categories the user's sources override or reach, as the shipped set alone does.** | 6 | 6 | 7 | 8 | 12 | 13.7 |
| 79 | 0.1.0 | decision | **Beside a `--data-path`, load in `New` only the user's categories, the shipped ones they override and the shipped ones they read; load the rest on first reach.** | 6 | 6 | 7 | 8 | 12 | 13.7 |
| 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 |
| 81 | 0.2.0 | question | **Refuse at load every `calc` divisor not proven nonzero, in a string column too.** | 4 | 4 | 6 | 8 | 4, 6 | 20.6 |
| 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 |
@@ -136,7 +136,7 @@
### 77. Split the shipped data into Go modules per locale and country, which a library imports by choice, and embed every one in the CLI.
The CLI binary carries every module and may grow to hundreds of MB; a library imports only what it uses, as it imports any dependency, and nothing is fetched at run time. Decide the module boundaries (a locale, a country's `geo/` tree, `misc`) and how an imported module registers with `New`. Goal 2 says what ships where since 2026-10-03.
The CLI binary carries every module and may grow to hundreds of MB; a library imports only what it uses, as it imports any dependency, and nothing is fetched at run time. Decide the module boundaries (a locale, a country's `geo/` tree, `misc`) and how an imported module registers with `New`. Goal 2 says what ships where.
### 78. Ship 10% of every place's streets, at least 10, and 10% of all US places drawn by population, each place weighted by population.
@@ -255,9 +255,9 @@ Needs item 4.
`checkCalc` and the value proof read an operand as a sibling field, so `{calc(n * 2)}` is refused with `no field "n"` today. Item 4 stops an operand holding its field for the expansion, after which a name is the one way to show the operand a calc computes from, as the README's Computation example does.
### 79. Beside a `--data-path`, load only the shipped categories the user's sources override or reach, as the shipped set alone does.
### 79. Beside a `--data-path`, load in `New` only the user's categories, the shipped ones they override and the shipped ones they read; load the rest on first reach.
The decision "The shipped set alone loads a category on the first call reaching it; beside a `--data-path` every category loads in `New`" parses the whole shipped set on every `--data-path` run, about 45 ms today and seconds once items 61 and 78 land. Goal 6 needs only the user's categories, the shipped ones they override, and the shipped ones whose reads reach those, so the index would carry each category's reads.
The decision "With only the shipped set, a category loads on the first call reaching it; beside a `--data-path`, every category loads in `New`" parses the whole shipped set on every `--data-path` run, about 45 ms today and seconds once items 61 and 78 land. Goal 6 needs only the user's categories, the shipped ones they override, the shipped ones they read, and the shipped ones whose reads reach an overridden category, so the index would carry each category's reads.
### 58. Ship prebuilt binaries, a container image, and packages for Homebrew, Scoop and the biggest Linux package managers, so the CLI needs no Go.