From 4930a2ac8d4b2f1f814bf233e2147b40b581f52e Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 7 Oct 2026 22:17:21 +0200 Subject: [PATCH] Narrow goal 5.7's decisions to the refusals that catch a mistake, list the change as shipped under the floor, and drop item 94 --- AGENTS.md | 5 ++-- docs/decisions.md | 61 +++++++++++++++++------------------------------ todo.md | 23 +++++++----------- 3 files changed, 33 insertions(+), 56 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 491079d..ddfafa6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,7 +42,7 @@ In [docs/decisions.md](docs/decisions.md): - A struct tag is a template by its shape - The CLI renders exactly the template on its stdin, and adds nothing - A template that does not compile is misuse (exit 2), including a reference that resolves to nothing, save a lone reference under the CLI's `--format` until item 129 -- `NewTemplate` rejects a padded JSON template, and the CLI's stdin drops the whitespace around one +- Whitespace around a JSON template is dropped, in `NewTemplate` and on stdin - `FakeTemplate` and `NewTemplate` both stay - The shipped data is embedded, not discovered - With only the shipped set, a category loads on the first call reaching it; beside a `--data-path`, every category loads in `New` @@ -59,7 +59,7 @@ In [docs/decisions.md](docs/decisions.md): - The repeat cap bounds renders, not bytes - 64-bit targets only - A constant zero divisor is a load error; in a string column a divisor that is not constant prints `Inf` -- In data, a default written out and a constant spelled as a sample are load errors +- In data, a constant spelled as a sample is a load error - Samples say what they emit, transforms what they do - A record is a template seen as columns; a Go struct is the one second schema - A struct's records follow Go's field access, and compile on first use @@ -81,7 +81,6 @@ In [docs/decisions.md](docs/decisions.md): - `..` steps up to the parent row, and a step down after it draws afresh, or once per name's pick - A table selects by one key column, a code, never a free-form name - A parent row with no child row is a load error -- The choice-of-rows fence guards a data file's root, and requires string fields - A table is a record of string columns - The key map is built at load, the rest on first draw - Two categories may name one TSV diff --git a/docs/decisions.md b/docs/decisions.md index 7841ec1..1d2818e 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -65,14 +65,10 @@ can never collide with any of those spellings, and the leading `[` or `"` is gat valid JSON so a stray copied bracket never swallows a tag — it names nothing, and says so. Reserving the characters whole — though only a leading one could collide — keeps one simple name rule instead of a leading-position special case. -The JSON string is what makes the library's own advice reachable: the error for an -object holding only a format names `"…"`, and that spelling has to work where it is -printed. On stdin and in `NewTemplate`, `"x {/a}"` and `x {/a}` render alike, and the -maintainer kept the JSON string on 2026-10-07, so goal 5.7 does not refuse it: its escapes, -`\t` and `\n`, carry what bare text cannot. A struct tag of one reference alone, -`{/users}`, is refused naming the path `users`: both render the same text, and only the -path names a record. A folder-relative `{.name}` or `{..name}` is refused naming -`{/name}`, since an inline template sits in no folder. `IsTemplate` exports the rule, so struct tags and any other caller read one. +A JSON string stays a template even where its bare text renders alike, `"x {/a}"` beside +`x {/a}`: its escapes put a tab or a newline on one `echo` line. A folder-relative `{.name}` or `{..name}` is refused naming +`{/name}`, since an inline template sits in no folder. `IsTemplate` exports the rule, so +struct tags and any other caller read one. ## The CLI renders exactly the template on its stdin, and adds nothing @@ -86,9 +82,9 @@ bench of the README's personas picked this on 2026-10-07 over dropping a newline adding one: a rule a user states in one sentence, at the cost of `printf` or a `--separator` now and then. -A JSON template is its JSON: the maintainer ruled the whitespace around it no part of the -template, so goal 5.7 does not refuse it on stdin, while `NewTemplate` still refuses a -padded one. Stdin holding only whitespace is misuse, since an unset variable sends just a +A JSON template is its JSON, so the whitespace around it is no part of the template, per +the decision "Whitespace around a JSON template is dropped, in `NewTemplate` and on +stdin". Stdin holding only whitespace is misuse, since an unset variable sends just a newline. A lone reference, `{/users}` or `"{/users}"`, names the record `users` under `--format`, @@ -110,14 +106,14 @@ The whole template is the spelling under test, and `NewTemplate` compiles, links validates as one step. Under the CLI's `--format`, a lone reference to nothing exits 1 until item 129: it reads a path, and only the data is absent. -## `NewTemplate` rejects a padded JSON template, and the CLI's stdin drops the whitespace around one +## Whitespace around a JSON template is dropped, in `NewTemplate` and on stdin -2026-09-03, Lilleman auf Larv; the CLI's stdin 2026-10-07. Serves goal 5.7. +2026-10-07, Lilleman auf Larv. Applies KISS. -Padding is the one place the two readings disagree — a format string renders it, JSON -drops it — so `NewTemplate` names the spelling that renders rather than silently -choosing. On stdin the CLI drops it, per the decision "The CLI renders exactly the -template on its stdin, and adds nothing". +Padding is where the two readings disagree: a format string renders it, JSON drops it. A +template that parses as JSON is its JSON, so its padding is dropped, as `encoding/json` +drops it. Goal 5.7 refuses only a spelling that suggests a mistake, and padding around +JSON suggests none. ## `FakeTemplate` and `NewTemplate` both stay @@ -169,10 +165,6 @@ from the one pick a name keeps. - `{x as n}` binds a reference, or a field of the template binding it or a path into one, `{place as p}`: an inline template, and a choice of rows inside one category, have no reference to bind, and goal 4.1 holds there too. -- A name read once is refused where writing what it binds in its place draws the same, and - the error names that spelling (goal 5.7): `{/x as n}{n.y}` is `{/x.y}`. A calc cannot read - a reference or a path, so a calc's one read of a name bound to either is not refused, and - neither is a read inside a nested repeat, which keeps one pick on every line. Valid while goal 5.2 has each `{…}` make a new pick. @@ -191,8 +183,8 @@ naming the locale, so a folder renames and copies without editing its references Data files, the CLI and the Go API are the public API, and a consumer must be able to take a minor without an edit — so an added column is a major, since it changes the CSV header and the `INSERT` column list, as is a removed value, which changes what a fixture -holds, and a new option, which reserves a field name. Goal 5.7's refusals grow by -tightening, so every fence invalidates some file. Each such release names the rejected +holds, and a new option, which reserves a field name. A new refusal tightens what +loads, so every new fence invalidates some file. Each such release names the rejected spelling and its replacement in the changelog and in the load error, and that is the whole migration: a fence rejects one spelling with one replacement, so the fix is local to each site. A fence that would need a non-local rewrite ships a converter with its @@ -291,14 +283,14 @@ is sometimes not a number loads, and prints `NaN` on each draw where it is not o and `NaN` in a string column work against goals 6.1 and 4 until `todo.md` item 81 refuses them there too. -## In data, a default written out and a constant spelled as a sample are load errors +## In data, a constant spelled as a sample is a load error -2026-09-24, Lilleman auf Larv. +2026-09-24, Lilleman auf Larv; narrowed 2026-10-07. Serves goal 5.7. -`weight: 1`, `repeat: 1`, `separator: ""`, `datatype: "string"`, `int(5,5)`, -`float(1,1,2)`, `+5` and `05` each spell what a shorter form already spells, so each is -rejected naming that form. The CLI's numbers follow the shell instead: `--seed 007` and -`--repeat +3` are 7 and 3, as every command line reads them. +`int(5,5)` and `float(1,1,2)` draw nothing, which suggests a range typed wrong, so each is +rejected naming the constant. A default written out, `weight: 1`, `repeat: 1`, +`separator: ""` or `datatype: "string"`, and a number spelled `+5` or `05`, load as the +shorter form, as the CLI's `--seed 007` and `--repeat +3` do. ## Samples say what they emit, transforms what they do @@ -516,15 +508,6 @@ lead to a row at every level below it, or a render could find nothing to draw. T import script drops or fills such rows; the alternative, falling back to a free draw, would break the consistency the link exists for without saying so. -## The choice-of-rows fence guards a data file's root, and requires string fields - -2026-09-17, Lilleman auf Larv. - -A table is a category with a TSV beside its file, so only a root choice has the spelling -the fence names; a nested choice of same-shaped templates and an inline one keep -loading. Fields must all be strings because a cell is a string node: a choice whose -items carry a nested choice is not one table but two linked ones. - ## A table is a record of string columns 2026-09-17, Lilleman auf Larv. @@ -865,7 +848,7 @@ Valid while names are read only inside the category binding them. 2026-10-04, Lilleman auf Larv. Serves goal 3 and applies KISS. -Shipped under the floor by the maintainer's decision of 2026-10-07: the CLI reading its template from stdin, with the scoring run and its ratchet waived for that chunk. +Shipped under the floor by the maintainer's decisions of 2026-10-07: the CLI reading its template from stdin, with the scoring run and its ratchet waived for that chunk; and narrowing goal 5.7 to the refusals that catch a mistake, dropping the rest. Last scoring run, 2026-10-07, on commit ad59967: Navigation 7.00, Locality 5.88, Shape 6.50, Self-sufficiency 6.13, overall 6.38. The nine-seat panel at depth 1, run beside it: Navigation 7.17, Locality 6.06, Shape 6.67, Self-sufficiency 6.56, overall 6.56. Against the runs on ac6158e, the scoring run's overall rose from 6.25 and its Shape from 6.38, while its Self-sufficiency fell from 6.38; the panel's overall held, and its Locality and Self-sufficiency fell by 0.05 each. Locality is lowest again, as at every panel since 2026-09-22. All 13 seats named the named-pick read path hardest: `readField`, `readName`, `readUnder` and `keeps`, with the pick keys `levelKeys` and `addressedKeys` build at load; twelve kept `doc.go`'s vocabulary open beside it. What the two runs found is filed in `todo.md`, and `todo.md` item 153 runs both again. Valid while `AGENTS.md` gates goal 3 at 7.0. diff --git a/todo.md b/todo.md index 06544e8..47d6144 100644 --- a/todo.md +++ b/todo.md @@ -70,7 +70,7 @@ | 61 | 0.1.0 | | **Add a locale, with its `geo/` tree, for each of the ten most-spoken languages and each Nordic country.** | 4 | 9 | 9 | 10 | 2.1 | 30.8 | | 16 | 0.1.0 | | **Add locale categories: company, phone, finance, vehicle, words.** | 3 | 8 | 8 | 9 | 2.2, 15 | 28.1 | | 15 | 0.1.0 | | **Add `misc` tables and templates, one row of its detail's table per chunk.** | 3 | 9 | 8 | 8 | 2, 15, 14 | 25.9 | -| 129 | 0.1.0 | decision | **Take a template wherever the library renders, so `fake:"{/sv_SE.person.first}"` and `Fake("{/sv_SE.person}")` render, and text with no `{…}` prints as written, as the CLI already does.** | 5 | 8 | 8 | 10 | 5.9, 5.7, 7 | 25.0 | +| 129 | 0.1.0 | decision | **Take a template wherever the library renders, so `fake:"{/sv_SE.person.first}"` and `Fake("{/sv_SE.person}")` render, and text with no `{…}` prints as written, as the CLI already does.** | 5 | 8 | 8 | 10 | 5.9, 7 | 25.0 | | 57 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.5 | | 96 | 0.1.0 | | **Refuse two reads into one sibling, category or struct category that no name joins, `{place.postal-code} {place.locality}`, showing the spelling for one pick and the one for two.** | 4 | 4 | 7 | 7 | 5.6, 4 | 20.9 | | 153 | 0.1.0 | decision | **Run the nine-seat comprehension panel and the scoring run again once items 145 to 152 and item 10 have merged, and file what the panel names.** | 1 | 3 | 3 | 9 | 3 | 20.4 | @@ -115,17 +115,15 @@ | 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 | | 159 | 0.2.0 | | **Name the quoted spelling where a template that is a bare JSON number, boolean or null is refused, padded or not: `42` names `"42"`.** | 1 | 2 | 3 | 6 | 6, 5.1 | 14.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 | 16 | 14.0 | | 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, 10 | 14.0 | | 158 | 0.2.0 | | **Drop a UTF-8 byte order mark that starts stdin, and say in the README how to stop PowerShell 5.1 sending `Göteborg` as `G?teborg`.** | 2 | 2 | 4 | 5 | 7.2, 5.1 | 13.5 | | 79 | 0.2.0 | decision | **Beside a `--data-path`, load in `New` the user's categories, the shipped ones they override or read, and the shipped ones whose reads reach an overridden one; load the rest on first reach.** | 6 | 6 | 7 | 8 | 13 | 12.9 | +| 27 | 0.2.0 | | **Merge `en_US.ip` and `sv_SE.ip`, today byte-identical, into one `misc.ip`.** | 3 | 2 | 4 | 6 | 8 | 12.3 | | 107 | 0.2.0 | decision | **Move the template engine into `internal/`, leaving the root the public API and its options.** | 6 | 9 | 3 | 8 | 3.2, 3.4 | 12.2 | | 112 | 0.2.0 | | **Check a calc's field operands where its template resolves, beside its name operands, once item 36 reports every mistake at once.** | 2 | 2 | 1 | 6 | 3.2 | 10.3 | -| 94 | 0.2.0 | | **Refuse a name whose every read passes one deeper level, naming the binding of that level: `{place as p}{p.geo.town.name} {p.geo.town.zip}` is `{place.geo.town as t}{t.name} {t.zip}`.** | 3 | 3 | 3 | 5 | 5.7 | 10.2 | | 108 | 0.2.0 | defect | **Name item 26 in the decision "No builtin reads the clock, so a date is bounded by days, never by an age", as the item that ends its conflict with goal 4.** | 1 | 1 | 1 | 4 | 3.4 | 8.0 | -| 14 | 0.3.0 | | **Spell `misc.creditcard`'s digit runs `{digits(n)}`.** | 2 | 2 | 4 | 6 | 5.7 | 15.1 | | 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 `childNamed`'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 | @@ -133,17 +131,18 @@ | 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 | | 115 | 0.3.0 | | **Spell a rows file's line one way in every error, naming the file.** | 1 | 2 | 3 | 6 | 6 | 13.5 | +| 14 | 0.3.0 | | **Spell `misc.creditcard`'s digit runs `{digits(n)}`.** | 2 | 2 | 4 | 6 | 8 | 13.3 | | 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 | +| 40 | 0.3.0 | | **Parse `defaultTable`'s path with the library's grammar.** | 2 | 2 | 3 | 5 | 3.2 | 12.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 | 12 | 12.2 | -| 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 | | 86 | 0.3.0 | | **Read both `en_US.phone` formats' area codes from one category, as their exchange reads `en_US.phone-exchange`.** | 1 | 2 | 2 | 5 | 3.2 | 11.5 | | 23 | 0.3.0 | | **Give every other shipped category that could reach something real a path that never does.** | 3 | 6 | 6 | 7 | 16 | 11.0 | | 75 | 0.3.0 | | **Run every README `sh`, `go` and `text` example as a test, and compare each "Renders" line to a seeded render.** | 2 | 6 | 4 | 7 | 12.2 | 10.8 | | 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 | | 72 | 0.3.0 | principle | **Fail the merge gate when test coverage falls below the last recorded figure.** | 2 | 3 | 2 | 5 | 3 | 10.2 | +| 30 | 0.3.0 | | **Draw `email.local` from `username`, so the two share one handle list.** | 2 | 2 | 3 | 5 | 8 | 10.0 | | 95 | 0.3.0 | principle | **Hold a struct's columns and the field each fills in one slice.** | 1 | 2 | 1 | 5 | 3.2 | 9.5 | | 116 | 0.3.0 | | **Turn a row index into its rows file's line in one function.** | 1 | 2 | 1 | 5 | 3.2 | 9.5 | | 19 | 0.3.0 | | **Fill the 398 Swedish localities weighted 200 from SCB småorter.** | 2 | 4 | 4 | 6 | 14 | 9.2 | @@ -463,18 +462,10 @@ The engine returns column values and the root builds `Record`. Revises the decis Needs item 36. `checkCalcFields` runs at compile and `checkCalcNames` in `resolveTemplates`, two passes over one rule. Moving the field check there before item 36 would change which of two mistakes in two category files a load reports, since every category compiles before any resolves. -### 94. Refuse a name whose every read passes one deeper level, naming the binding of that level: `{place as p}{p.geo.town.name} {p.geo.town.zip}` is `{place.geo.town as t}{t.name} {t.zip}`. - -Both spellings load and draw alike, against goal 5.7. Widen the read-once refusal in `namefence.go` to the longest prefix every read of a name shares. - ### 108. Name item 26 in the decision "No builtin reads the clock, so a date is bounded by days, never by an age", as the item that ends its conflict with goal 4. Item 26 revises the decision, but the decision does not point back to it. -### 14. Spell `misc.creditcard`'s digit runs `{digits(n)}`. - -A `"repeat": 14` over `{digits(1)}` draws every 14-digit run with the odds `{digits(14)}` does. Both load, since neither has a part that changes nothing; the shipped data writes the obvious one. Re-pin seeded output in its own commit. - ### 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`. @@ -503,6 +494,10 @@ In `internal/rows`, `Pins.PinRow` names `t.path` and `Pins.MustRow` panics with Today a cell reads `g.t.b, line 3` (`namedNode.labelIn`), a cell check `t.tsv: line 3, b` (`checkCells`) and a link `t.tsv line 3` (`rows.Table.Link`). A reference cycle found from a table's format through a cell names no line at all: `reference cycle: g.t -> format -> b -> b -> /h -> …`, where the column-to-cell step in `renderEdges` repeats the column name. +### 14. Spell `misc.creditcard`'s digit runs `{digits(n)}`. + +A `"repeat": 14` over `{digits(1)}` draws every 14-digit run with the odds `{digits(14)}` does. Both load, since neither has a part that changes nothing; the shipped data writes the obvious one. Re-pin seeded output in its own commit. + ### 11. Test that every `geo` tree holds the five table names and the `address` columns that port across countries. Nothing checks it, and a new country breaks it silently.