From e17660e8ba45bc85f7919b5b6026d306ca80b04c Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Mon, 5 Oct 2026 11:37:59 +0200 Subject: [PATCH] Answer prose round 1: state the gate once in item 5, title the engine decision by what this commit holds, and plainer wording across items 97 to 107 and the decisions --- AGENTS.md | 2 +- docs/decisions.md | 23 ++++++++++++----------- todo.md | 44 ++++++++++++++++++++++---------------------- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d8c6ba5..2d94507 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -112,4 +112,4 @@ In [docs/decisions.md](docs/decisions.md): - A path draws through its compiled steps - A name lives in the category binding it, or in the repeat binding it, and is drawn on its first read - comprehension floor: every dimension and the overall at 7.0 or above; the change making every `{…}` draw afresh and items 97 to 106 ship with no scoring run, and item 5 scores them -- The template engine stays the root package until item 107, and what stands alone lives in `internal/` +- The template engine stays the root package until item 107, and items 97 to 103 move what stands alone into `internal/` diff --git a/docs/decisions.md b/docs/decisions.md index 0eb339f..52efcde 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -504,10 +504,10 @@ option earns its place. A link is proved against the parent's keys and a key's uniqueness is a data mistake, so both are load-time; the row lookup, by name and by parent, serves only a draw, so it waits -for the first one, keeping `New` linear in the bytes read. On 2026-10-05 building every -shipped lookup at load took 13 ms and 3.2 MiB on top of a 60 ms `New`, a cost that grows -with every locale and with item 78 while most tables are never drawn by name or parent; -the maintainer kept the lookup on first draw, written once. +for the first one, keeping `New` linear in the bytes read. On 2026-10-05, building every +shipped lookup at load added 13 ms and 3.2 MiB to a 60 ms `New`. That cost grows with every +locale and with item 78, and most tables are never drawn by name or parent. The maintainer +kept building the lookup on first draw, once per table. ## Two categories may name one TSV @@ -827,15 +827,16 @@ Valid while names are read only inside the category binding them. ## comprehension floor: every dimension and the overall at 7.0 or above; the change making every `{…}` draw afresh and items 97 to 106 ship with no scoring run, and item 5 scores them -2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat, and the hardest units were the draw fences that change deleted. The change making every `{…}` draw afresh and `todo.md` items 97 to 106 delete or move what the earlier chunks of the below-floor round touched, so a scoring run before item 106 merges would score code about to go; the calc change accepting a name as an operand shipped unscored on 2026-10-05 for that reason. They finish the round `AGENTS.md`'s comprehension rule lets merge below the floor, a restructure the maintainer approved. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until `todo.md` item 5 runs. +2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat, and the hardest units were the draw fences that change deleted. The change making every `{…}` draw afresh and `todo.md` items 97 to 106 delete or move what the earlier chunks of the below-floor round touched, so a scoring run before item 106 merges would score code about to go; the calc change accepting a name as an operand shipped unscored on 2026-10-05 for that reason. With that change, items 97 to 106 are the restructure the maintainer approved: the round `AGENTS.md`'s comprehension rule lets merge below the floor. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until `todo.md` item 5 runs. -## The template engine stays the root package until item 107, and what stands alone lives in `internal/` +## The template engine stays the root package until item 107, and items 97 to 103 move what stands alone into `internal/` 2026-10-05, Lilleman auf Larv. Serves goals 3.2 and 3.4. Two architect reviews found the engine's types, `template`, `table`, `arm`, `op` and `nameBinding`, bound in one cycle because several passes each write part of the same structs; the template -language's recursion needs only a little of it. Moving the engine before items 101 to 106 -give each value one writer would spread that knot across packages, behind forwarding calls +language's recursion reads few of those types. Moving the engine before items 101 and 104 to +106 give each value one writer would spread that knot across packages, behind forwarding calls from every public method. The draw state and `DataType` read no engine type, and `DataType` -moves with the proofs' bounds keyed by it; the grammar, the reading of data files, the -proofs' bounds, the builtins and a table's rows stop reading one once items 98 to 103 cut -their edges, so they move now, each behind an import list a test checks. Valid until `todo.md` item 107 moves the engine. +moves with `proven`, what a proof knows of a value, which is keyed by it; the grammar, the +reading of data files, `proven`, the builtins and a table's rows stop reading any engine +type once items 98 to 103 remove those reads, so they move now, each behind an import list +a test checks. Valid until `todo.md` item 107 moves the engine. diff --git a/todo.md b/todo.md index c65f2c9..2139124 100644 --- a/todo.md +++ b/todo.md @@ -73,12 +73,12 @@ | 57 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.5 | | 34 | 0.1.0 | | **Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.** | 2 | 4 | 6 | 7 | 4 | 20.9 | | 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 | -| 5 | 0.1.0 | decision | **Run the nine-seat comprehension panel once items 97 to 106 have restructured the code, and file what it names.** | 1 | 3 | 3 | 9 | 3 | 20.4 | +| 5 | 0.1.0 | decision | **Run the nine-seat comprehension panel and larv-review's scoring run once items 97 to 106 have restructured the code, and file what the panel names.** | 1 | 3 | 3 | 9 | 3 | 20.4 | | 91 | 0.1.0 | | **Accept a path through a name as a `calc` operand, so `{/misc.coordinate as c}{c}: {calc(c.lat * 60)}` computes from the coordinate `{c}` prints.** | 3 | 4 | 6 | 7 | 4.1, 5.3 | 19.9 | | 89 | 0.1.0 | | **Fail `New` without `WithSeed`, naming the option, and seed the CLI from the system.** | 3 | 3 | 6 | 7 | 10.1 | 16.0 | -| 102 | 0.1.0 | defect | **Move a table's rows, with their family, proofs, pins, row draws and lookups, into `internal/rows`, and walk a path through one per-step function that refuses a level carrying a `repeat` in the load check and in `Fake` alike.** | 5 | 6 | 4 | 8 | 3.2, 3.4, 5 | 15.9 | +| 102 | 0.1.0 | defect | **Move a table's rows and the links between tables into `internal/rows`, and walk every path through one per-step function, so `Fake` refuses a level carrying a `repeat` as the load check does.** | 5 | 6 | 4 | 8 | 3.2, 3.4, 5 | 15.9 | | 97 | 0.1.0 | decision | **Move the seeded randomness and its `{seq()}` counters into `internal/drawstate`, built from a seed, and test which packages each package may import.** | 2 | 4 | 2 | 8 | 3.2, 3.4 | 15.4 | -| 103 | 0.1.0 | | **Move the folder walk and the reading of a data tree's JSON and TSV files into `internal/datafiles`, which hands compiling a reader for a table's rows.** | 3 | 4 | 2 | 8 | 3.2, 3.4 | 14.4 | +| 103 | 0.1.0 | | **Move the folder walk and the reading of a data tree's JSON and TSV files into `internal/datafiles`, which hands the engine a reader for each table's rows.** | 3 | 4 | 2 | 8 | 3.2, 3.4 | 14.4 | | 98 | 0.1.0 | | **Move how the template language is written — format tokens, paths and selectors, reference sigils, names and `calc` syntax — into `internal/grammar`.** | 3 | 5 | 2 | 8 | 3.2, 3.4 | 14.2 | | 100 | 0.1.0 | | **Move the builtins, with their checksums, layouts and transforms, into `internal/builtinfunc`, and check `calc`'s operands where its template links.** | 3 | 5 | 2 | 8 | 3.2, 3.4 | 14.2 | | 106 | 0.1.0 | | **Settle each record's shape when its template links, so `Generator.records` goes.** | 2 | 2 | 2 | 7 | 3.2 | 14.1 | @@ -152,7 +152,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. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. The CLI binary may grow to hundreds of MB. The maintainer decided on 2026-10-04, under goals 2.3, 6.2, 7.3, 9 and 10.1: +The CLI binary may grow to hundreds of MB. The maintainer decided on 2026-10-04, under goals 2.3, 6.2, 7.3, 9 and 10.1: - One module per locale, one per country's `geo/` tree, and one for `misc`. A module registers nothing when imported. - A bare `New()` loads no data and fails, naming the option to add. `WithoutShippedData` goes. - A data module is an `fs.FS` passed to `WithDataFS`, as anyone's data is, and `New` takes several. A module carrying functions loads through an option of its own, so a data module that starts carrying functions breaks its users. @@ -167,15 +167,15 @@ Split it into items before starting. Before the module API ships, a panel of the ### 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. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. Needs item 77. Ten streets per place crowds 500 Stockholm customers onto ten streets. `geo-se.py` and `geo-us.py` keep 10% of each place's streets, at least 10. `geo-us.py` draws 10% of every Census place, down to the smallest village, picking each with odds by population, and every table weights a place by its population, so a draw is not mostly tiny villages. Replaces the decision "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", which the maintainer withdrew on 2026-10-03. Re-pin seeded output and the shape in their own commits. +Needs item 77. Ten streets per place crowds 500 Stockholm customers onto ten streets. `geo-se.py` and `geo-us.py` keep 10% of each place's streets, at least 10. `geo-us.py` draws 10% of every Census place, down to the smallest village, picking each with odds by population, and every table weights a place by its population, so a draw is not mostly tiny villages. Replaces the decision "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", which the maintainer withdrew on 2026-10-03. Re-pin seeded output and the shape in their own commits. ### 61. Add a locale, with its `geo/` tree, for each of the ten most-spoken languages and each Nordic country. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. 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. +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. ### 16. Add the remaining locale categories: company, phone, finance, vehicle, words. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. Shape: T = table, t = template, c = choice. +Shape: T = table, t = template, c = choice. `sv_SE` ([source research](docs/research/research-sources-se.md)) @@ -209,7 +209,7 @@ Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. Shape: ### 15. Add the remaining `misc` tables and templates, one row of its detail's table per chunk. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. Shape: T = table, t = template, c = choice. +Shape: T = table, t = template, c = choice. | Category | Shape | Source | Licence | |---|---|---|---| @@ -236,33 +236,33 @@ Nothing has shipped, so "no longer paths", "where it used to fail" and "where it ### 34. Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. `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. +`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. ### 96. 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. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. Asked of the maintainer by the product-owner pass of the chunk that made every `{…}` draw afresh. Since every `{…}` draws afresh, `{net} x {qty} = {calc(net * qty, 2)}`, `{place.postal-code} {place.locality}` and struct tags `sv_SE.address.street` beside `sv_SE.address.postal-code` load and may render a pair that disagrees, and nothing tells the author to bind a name (goal 4.1). Goal 5.6 asks such a template to fail, showing `{place as p}{p.postal-code} {p.locality}` for one place and two names for two. The decision "Every `{…}` draws afresh, and only a name keeps a pick" lists the draw checks as names, paths through names, cycles and the repeat cap, so this fence revises it. `{w} {w}`, one spelling twice, stays two draws. The maintainer answered yes on 2026-10-05, for reads inside one category or one struct; reads across categories, `{/city.name}, {/country.name}`, keep loading. +Asked of the maintainer by the product-owner pass of the chunk that made every `{…}` draw afresh. Since every `{…}` draws afresh, `{net} x {qty} = {calc(net * qty, 2)}`, `{place.postal-code} {place.locality}` and struct tags `sv_SE.address.street` beside `sv_SE.address.postal-code` load and may render a pair that disagrees, and nothing tells the author to bind a name (goal 4.1). Goal 5.6 asks such a template to fail, showing `{place as p}{p.postal-code} {p.locality}` for one place and two names for two. The decision "Every `{…}` draws afresh, and only a name keeps a pick" lists the draw checks as names, paths through names, cycles and the repeat cap, so this fence revises it. `{w} {w}`, one spelling twice, stays two draws. The maintainer answered yes on 2026-10-05, for reads inside one category or one struct; reads across categories, `{/city.name}, {/country.name}`, keep loading. -### 5. Run the nine-seat comprehension panel once items 97 to 106 have restructured the code, and file what it names. +### 5. Run the nine-seat comprehension panel and larv-review's scoring run once items 97 to 106 have restructured the code, and file what the panel names. -Needs items 97 to 106. The decision "comprehension floor: every dimension and the overall at 7.0 or above; the change making every `{…}` draw afresh and items 97 to 106 ship with no scoring run, and item 5 scores them" suspends every scoring run and panel until then. +Nothing else in `todo.md` but items 97 to 106 merges until a panel scores 7.0 or above, per `AGENTS.md`'s comprehension rule. The decision "comprehension floor: every dimension and the overall at 7.0 or above; the change making every `{…}` draw afresh and items 97 to 106 ship with no scoring run, and item 5 scores them" suspends every scoring run and panel until then. ### 91. Accept a path through a name as a `calc` operand, so `{/misc.coordinate as c}{c}: {calc(c.lat * 60)}` computes from the coordinate `{c}` prints. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. A calc operand parses as a bare identifier, so `{calc(c.lat * 60)}` fails with `unexpected ".lat * 60"`, and a second binding of `/misc.coordinate.lat` would draw another coordinate. Resolve the path through `linkName`'s leaves, as `operandNodes` does for a bare name. +A calc operand parses as a bare identifier, so `{calc(c.lat * 60)}` fails with `unexpected ".lat * 60"`, and a second binding of `/misc.coordinate.lat` would draw another coordinate. Resolve the path through `linkName`'s leaves, as `operandNodes` does for a bare name. ### 89. Fail `New` without `WithSeed`, naming the option, and seed the CLI from the system. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. Goal 10.1 has the caller supply the seed, and today `New` without `WithSeed` seeds itself. This item ships in 0.1.0 beside item 77, so `New` breaks once before anyone depends on it. Mark `WithSeed` required in the Library section's options table. Then no test swaps the package's `randomBytes`, as `fejkdata_test.go` does today, failing every `New` in the package while it runs. +Goal 10.1 has the caller supply the seed, and today `New` without `WithSeed` seeds itself. This item ships in 0.1.0 beside item 77, so `New` breaks once before anyone depends on it. Mark `WithSeed` required in the Library section's options table. Its tests then stop swapping `randomBytes`, as `fejkdata_test.go` does today, which fails every `New` in the package while that test runs. -### 102. Move a table's rows, with their family, proofs, pins, row draws and lookups, into `internal/rows`, and walk a path through one per-step function that refuses a level carrying a `repeat` in the load check and in `Fake` alike. +### 102. Move a table's rows and the links between tables into `internal/rows`, and walk every path through one per-step function, so `Fake` refuses a level carrying a `repeat` as the load check does. -Needs items 98 and 101. `internal/rows` owns the parent and child links, and each rows table carries the engine table holding it as a payload set when it is built, so a step to a relative reaches its format and cells with no side map: the family has one writer, and `internal/rows` never sees a template. The rows code reads `checkName` and the selector spelling from `internal/grammar`. The lookups keep the weights `sumWeights` parsed, where `builtLookup` parses them again. Today `pathCheck.walk` and `probePath` are two loops over one rule for passing a choice, a table or a field, and they have drifted: `probePath` skips `pathCheck.enter`, so `Fake("x.a")` under `"repeat":3` renders one draw where the README's Correlated fields section refuses it; the refusal owes a `CHANGELOG.md` entry. The shared step takes its state as parameters, as `drawSteps` does: one loop switching on a mode raised a repeat of a reference path from 66 to 106 allocations (decision "A path draws through its compiled steps"). Technical principle: DRY. +Needs items 98 and 101. `internal/rows` takes the rows, their key and parent proofs, the pins, the row draws and the lookups, and owns the parent and child links. Each rows table holds the engine table that owns it as a payload of a type parameter, set when it is built, so a step to a relative reaches its format and cells with no side map, the links have one writer, and `internal/rows` imports no engine type. The rows code reads `checkName` and the selector spelling from `internal/grammar`. The lookups reuse the weights `sumWeights` parsed; today `builtLookup` parses them again. Today `pathCheck.walk` and `probePath` are two loops over one rule for passing a choice, a table or a field, and they have drifted: `probePath` skips `pathCheck.enter`, so `Fake("x.a")` under `"repeat":3` renders one draw where the README's Correlated fields section refuses it; the refusal owes a `CHANGELOG.md` entry. The shared step takes its state as parameters, as `drawSteps` does: one loop switching on a mode raised a repeat of a reference path from 66 to 106 allocations (decision "A path draws through its compiled steps"). Technical principle: DRY. ### 97. Move the seeded randomness and its `{seq()}` counters into `internal/drawstate`, built from a seed, and test which packages each package may import. -First of the restructure items 97 to 106, which give each value one writer a reader can name and move what stands alone into `internal/`; the decision "The template engine stays the root package until item 107, and what stands alone lives in `internal/`" says why the engine stays. This item's first commit adds a test, using `go/build`, that lists each package's allowed imports; every later item extends it. `New` reads the system's entropy for an unseeded generator and hands the seed over, until item 89 moves that read into the CLI. The constructor and the generator's field take the name of what they build, a draw state with its counters, not half of it, and `New`'s local `rng` stops shadowing the `rng` interface. A function module of item 77 reaches the randomness through an interface the root declares; the concrete state stays internal. +First of the restructure items 97 to 106, which give each value one writer a reader can name and move what stands alone into `internal/`; the decision "The template engine stays the root package until item 107, and items 97 to 103 move what stands alone into `internal/`" says why the engine stays. This item's first commit adds a test, using `go/build`, that lists each package's allowed imports; every later item extends it. `New` reads the system's entropy for an unseeded generator and hands the seed over, until item 89 moves that read into the CLI. `newRand` and the generator's `rand` field are renamed after the draw state, which holds the `{seq()}` counters as well as the randomness, and `New`'s local `rng` stops shadowing the `rng` interface. A function module of item 77 reaches the randomness through an interface the root declares; the concrete state stays internal. -### 103. Move the folder walk and the reading of a data tree's JSON and TSV files into `internal/datafiles`, which hands compiling a reader for a table's rows. +### 103. Move the folder walk and the reading of a data tree's JSON and TSV files into `internal/datafiles`, which hands the engine a reader for each table's rows. Needs item 98. The folder walk, the hidden-file and empty-folder rules, reading a category's rows, and the check that every TSV is named move; the engine compiles each category as the walk hands it over, a table's rows are read through the reader only when its JSON names them, and errors keep their order. @@ -272,7 +272,7 @@ Needs item 97. Pure code that reads strings: `scanUnit`, `formatToken` and `pars ### 100. Move the builtins, with their checksums, layouts and transforms, into `internal/builtinfunc`, and check `calc`'s operands where its template links. -Needs item 99. `checkArgs` takes the arguments alone: `calc`, its one user of the fields, checks its operands where the template links, and a load with two mistakes reports the same one first as today. `ibanArg` joins the builtins, and `callFn` takes `internal/drawstate`'s type. +Needs item 99. `checkArgs` drops its `fields` parameter: `calc`, the one builtin reading it, checks its operands where the template links, and a load with two mistakes reports the same one first as today. `ibanArg` joins the builtins, and `callFn` takes `internal/drawstate`'s type. ### 106. Settle each record's shape when its template links, so `Generator.records` goes. @@ -288,7 +288,7 @@ The two-way link `nameScope.owner` ↔ `template.ownNameScope` is written by a t ### 79. Beside a `--data-path`, load in `New` the user's categories, the shipped ones they override or read, and the shipped ones whose reads reach an overridden one; load the rest on first reach. -Needs item 5 to score 7.0 or above, per `AGENTS.md`'s comprehension rule. Needs item 77, which makes an override fail unless it says it replaces. +Needs item 77, which makes an override fail unless it says it replaces. The decision "With only the shipped set, a category loads on the first call reaching it; beside a `--data-path`, every category loads in `New`" parses the whole shipped set on every `--data-path` run, about 45 ms today and seconds once items 61 and 78 land. Goal 6 needs only the user's categories, the shipped ones they override, the shipped ones they read, and the shipped ones whose reads reach an overridden category, so the index would carry each category's reads. @@ -298,7 +298,7 @@ Needs item 98. `proven` is keyed by `DataType`, and a package the root imports c ### 105. Link each template in steps that each return what they build — its references, its bindings' heads and addressed keys, its compiled arms, its column read — so no step writes into what an earlier one built. -Needs item 104. `linkBindings` writes `nameBinding.head` and `tail` in `linkRefs`'s first pass and `linkName` reads them in its second, so swapping the passes hands `linkName` a nil head. `linkPasses` does the same: `linkColumnRead` reads the `arm.named` that `linkNames` wrote and writes `link.readsColumn` into the link `linkTemplate` built, and `checkNameReads` reads the arms both filled. `arm` is filled in three places, `compileFormat`, `compileArms` and `linkName`, and `splitArm` runs before the link and again after it, so five seats of an earlier panel traced three phases. Technical principle: one owner per value. +Needs item 104. `linkBindings` writes `nameBinding.head` and `tail` in `linkRefs`'s first pass and `linkName` reads them in its second, so swapping the passes hands `linkName` a nil head. `linkPasses` does the same: `linkColumnRead` reads the `arm.named` that `linkNames` wrote and writes `link.readsColumn` into the link `linkTemplate` built, and `checkNameReads` reads the arms both filled. `arm` is filled in three places, `compileFormat`, `compileArms` and `linkName`, and `splitArm` runs before the link and again after it, so five readers of an earlier panel traced three phases. Technical principle: one owner per value. ### 58. Ship prebuilt binaries, a container image, and packages for Homebrew, Scoop and the biggest Linux package managers, so the CLI needs no Go. @@ -397,7 +397,7 @@ Require the path step to reach a sibling category. ### 107. Move the template engine into `internal/`, leaving the root the public API and its options. -Needs item 5. The engine returns column values and the root builds `Record`. Revises the decision "The template engine stays the root package until item 107, and what stands alone lives in `internal/`", and "The vocabulary sits below `doc.go`'s package clause, not in the package doc" where the vocabulary moves. +Needs item 5. The engine returns column values and the root builds `Record`. Revises the decision "The template engine stays the root package until item 107, and items 97 to 103 move what stands alone into `internal/`", and, if the vocabulary moves, "The vocabulary sits below `doc.go`'s package clause, not in the package doc". ### 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}`.