Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -163,7 +163,8 @@ it to a name and read the name
|
||||
The shipped data comes in modules, each a Go package under [`data/`](data) that embeds
|
||||
its files, so nothing is read from disk: one per locale (`en_US`, `sv_SE`), one per
|
||||
country's tree of places (`geo/SE`, `geo/US`), and the locale-neutral `misc`. The CLI
|
||||
carries every module, and a library loads those it imports ([Library](#library)). A
|
||||
carries every module, and a library loads those it passes to `WithDataFS`
|
||||
([Library](#library)). A
|
||||
directory is a namespace: each JSON file is a category named after the file, each
|
||||
subdirectory a dot-path segment, so `mydata/sv_SE/person.json` is `sv_SE.person` and
|
||||
replaces the shipped one. A module's tree spells its namespace the same way, so
|
||||
|
||||
@@ -8,30 +8,36 @@ import (
|
||||
|
||||
// loadSources merges sources in order, the last winning a clash. A source whose manifest
|
||||
// carries an index leaves its categories unloaded; every other source loads here, with
|
||||
// the unloaded categories it needs.
|
||||
// the unloaded categories it needs: docs/decisions.md#a-source-whose-manifest-carries-an-index-loads-each-category-on-first-reach-any-other-loads-in-new-with-what-it-reads
|
||||
func loadSources(sources []datafiles.Source) (folder, error) {
|
||||
root := folder{children: map[string]node{}}
|
||||
defaultModules := map[string][]string{}
|
||||
for _, src := range sources {
|
||||
if err := addSource(&root, src); err != nil {
|
||||
if err := addSource(&root, src, defaultModules); err != nil {
|
||||
return folder{}, err
|
||||
}
|
||||
}
|
||||
return root, loadReached(&root, categorySites(&root), nil)
|
||||
return root, loadReached(&root, categorySites(&root), nil, defaultModules)
|
||||
}
|
||||
|
||||
func addSource(root *folder, src datafiles.Source) error {
|
||||
// addSource places src's categories in root, and sets in defaultModules, for each category
|
||||
// it loads, the modules src reads by default.
|
||||
func addSource(root *folder, src datafiles.Source, defaultModules map[string][]string) error {
|
||||
m, err := src.Manifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.Index != nil {
|
||||
placeIndex(root, src, m.Index)
|
||||
placeIndex(root, src, m)
|
||||
return nil
|
||||
}
|
||||
g := &folder{children: map[string]node{}}
|
||||
if err := src.Walk(compileInto(func(dir []string) *folder { return madeFolder(g, dir) })); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range categorySites(g) {
|
||||
defaultModules[s.path] = m.Reads
|
||||
}
|
||||
mergeFolder(root, g)
|
||||
return nil
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// Package data lists every module fejkdata ships.
|
||||
// Package data lists every module fejkdata ships. A module's tree spells its namespace:
|
||||
// data/geo/SE embeds geo/SE/…, whose categories are geo.SE.*.
|
||||
package data
|
||||
|
||||
import (
|
||||
|
||||
+3
-3
@@ -63,7 +63,7 @@ func TestShippedDataCategories(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestShippedMiscCategories covers the locale-neutral data/misc folder: a proper
|
||||
// TestShippedMiscCategories covers the locale-neutral misc module: a proper
|
||||
// v4 UUID (version/variant nibbles fixed), a MAC address, and credit-card numbers
|
||||
// whose trailing {luhn()} check passes.
|
||||
func TestShippedMiscCategories(t *testing.T) {
|
||||
@@ -467,8 +467,8 @@ func TestShippedTableCellsAreTrimmed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestShippedNamespacedTree loads the whole data/ tree (not a single locale) and
|
||||
// reaches each locale through its folder segment: data/sv_SE/person -> sv_SE.person.
|
||||
// TestShippedNamespacedTree loads every shipped module and reaches each locale through its
|
||||
// folder segment: data/sv_SE/sv_SE/person.json -> sv_SE.person.
|
||||
func TestShippedNamespacedTree(t *testing.T) {
|
||||
f := shipped(t, WithSeed(1))
|
||||
for _, path := range []string{"sv_SE.person", "en_US.person", "sv_SE.address.locality"} {
|
||||
|
||||
@@ -304,3 +304,29 @@ Junior A, decided by:
|
||||
- Locality, Shape: `node.go:23` `folder`: `put` and `putUnloaded` keep a name in one map, where `compileInto` and `unloadedCategory.load` kept it as a pair
|
||||
- Locality, Shape: `data.go:12` `loadSources`: one load path, where `config.load` forked and `loadShipped` was a second, panic-only pipeline
|
||||
- Shape: `data.go:81` `categorySite`: embeds `categoryAt`, where `unloadedCategory` repeated its fields
|
||||
|
||||
## 2026-10-10T11:11:16Z, PR #203 at 421bb49, against bf1becb
|
||||
|
||||
Ruling: worse
|
||||
|
||||
| Seat | Navigation | Locality | Shape | Self-sufficiency | Overall |
|
||||
|---|---|---|---|---|---|
|
||||
| Mid A | same | worse | worse | same | worse |
|
||||
| Inherited architect | same | worse | worse | same | worse |
|
||||
|
||||
Mid A, decided by:
|
||||
|
||||
- Locality, Overall: `node.go:28` `folder.reads`: a third per-name map beside children and unloaded with no invariant tying it to them; every placement site must remember a separate setReads call (`data.go:36` addSource, `data.go:76` mergeFolder, `index.go:27` placeIndex)
|
||||
- Locality, Shape: `resolve.go:96` `withReads`: the manifest's reads travels through folder, categorySite.reads and templateSite.category to feed one error clause, four files to hold for one sentence of error text
|
||||
- Shape: `internal/datafiles/manifest.go:22` `Manifest.Reads`: reads as a load dependency the loader acts on, though it only feeds error text, and "reads" already means a template reading a name or column
|
||||
- Shape: `data/geo/SE/fs.go:2` `SE`: package names SE, US, en_US, sv_SE; in Modules SE.FS sits beside sv_SE.FS with nothing saying which is geography
|
||||
- Locality: `fejkdata.go:94` `New`: loads exactly what the options name, easier than the hidden config{shipped: true} default, though config.given brings back a smaller flag
|
||||
|
||||
Inherited architect, decided by:
|
||||
|
||||
- Locality, Shape, Overall: `node.go:27` `folder.reads`: a third per-name map that put and putUnloaded leave alone, so addSource, mergeFolder and placeIndex must each call setReads, and batch.load relies on placeIndex having done it; action at a distance for one error suffix
|
||||
- Locality: `resolve.go:94` `templateSite.withReads`: reads travels folder, categorySite.reads, a *categorySite per template, then withReads, four hops across three files
|
||||
- Shape: `data/geo/SE/fs.go:2` `SE`: bare package names SE and US do not say geography at WithDataFS(sv_SE.FS, SE.FS); the namespace-doubling rule lives only in README and test code
|
||||
- Shape, Self-sufficiency: `internal/datafiles/manifest.go:22` `Manifest.Reads`: suggests it drives loading though it only feeds withReads; "by default" needs README goal 9.2
|
||||
- Locality, Navigation: `data/modules.go:17` `Modules`: the hidden shippedFS default is gone; Modules lists the modules, errNoData names the fix, and the CLI loads data.Modules() in plain sight
|
||||
- Locality: `fejkdata.go:40` `config.given`: WithDataFS() with no arguments differs from no option, learnt only from its comment
|
||||
|
||||
+4
-2
@@ -143,7 +143,7 @@ package under `data/` embeds its own files, and a library imports the packages i
|
||||
|
||||
- One Go package per locale, one per country's `geo/` tree, and one for `misc`, all in the core's Go module. A package registers nothing when imported.
|
||||
- A bare `New()` loads no data and fails, naming the option to add. `WithoutShippedData` goes.
|
||||
- A data package's `FS` 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. Such a function reaches the randomness through an interface the root declares; `internal/drawstate`'s type stays internal.
|
||||
- A data package's `FS` is an `fs.FS` passed to `WithDataFS`, as anyone's data is, and `New` takes several. It is an exported `var FS embed.FS`, the shape `//go:embed` gives; a function returning it would guard only against a program reassigning a dependency of its own. A module carrying functions loads through an option of its own, so a data module that starts carrying functions breaks its users. Such a function reaches the randomness through an interface the root declares; `internal/drawstate`'s type stays internal.
|
||||
- A module names, in a manifest, every module it reads by default, directly or through another, so the first error names the whole set to import. A manifest is optional: a `--data-path` folder without one is a module. Nothing loads a default on its own.
|
||||
- A manifest names no fejkdata version, and `New` checks none: the shipped packages ship in the core's Go module, and a module from anyone else keeps loading across a minor or a patch, as the README's Versioning promises. The maintainer chose so on 2026-10-09.
|
||||
- In the CLI, a flag of its own says a `--data-path` replaces what it clashes with, so flags still go anywhere on the line. Data authors and hand fixture authors bench it.
|
||||
@@ -582,7 +582,9 @@ when a source carries it.
|
||||
|
||||
2026-09-24, Lilleman auf Larv; revised 2026-10-10, when the shipped data became Go packages.
|
||||
Serves goal 6.2. `sv_SE.FS` loads alone, and `sv_SE.address` then fails at first reach,
|
||||
naming `github.com/larvit/fejkdata/data/geo/SE`, which `sv_SE`'s manifest reads.
|
||||
naming `github.com/larvit/fejkdata/data/geo/SE`, which `sv_SE`'s manifest reads. `New`
|
||||
checks no `reads`: a stand-in providing the same paths under another name would fail it
|
||||
(goal 9.2).
|
||||
`--no-shipped-data -d` takes a locale's folder with its country's, or a set of one's 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
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ func WithDataPath(dir string) Option {
|
||||
func WithDataFS(modules ...fs.FS) Option {
|
||||
return func(c *config) {
|
||||
for _, fsys := range modules {
|
||||
c.sources = append(c.sources, datafiles.FS(fsys, ""))
|
||||
c.sources = append(c.sources, datafiles.FS(fsys))
|
||||
}
|
||||
c.given = true
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@ import (
|
||||
"github.com/larvit/fejkdata/internal/grammar"
|
||||
)
|
||||
|
||||
// indexed is a category an index names and no call has reached yet, and the source holding it.
|
||||
// indexed is a category an index names and no call has reached yet, the source holding it,
|
||||
// and the modules that source reads by default.
|
||||
type indexed struct {
|
||||
entry datafiles.IndexEntry
|
||||
src datafiles.Source
|
||||
entry datafiles.IndexEntry
|
||||
src datafiles.Source
|
||||
defaultModules []string
|
||||
}
|
||||
|
||||
// placeIndex sets every category m's index names as unloaded in root, in place of what
|
||||
@@ -22,9 +24,7 @@ type indexed struct {
|
||||
func placeIndex(root *folder, src datafiles.Source, m datafiles.Manifest) {
|
||||
for _, p := range sortedNames(m.Index) {
|
||||
segs := strings.Split(p, ".")
|
||||
g, name := madeFolder(root, segs[:len(segs)-1]), segs[len(segs)-1]
|
||||
g.putUnloaded(name, &indexed{entry: m.Index[p], src: src})
|
||||
g.setReads(name, m.Reads)
|
||||
madeFolder(root, segs[:len(segs)-1]).putUnloaded(segs[len(segs)-1], &indexed{entry: m.Index[p], src: src, defaultModules: m.Reads})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func (f *Generator) loadCallerPath(path string) (string, []string, error) {
|
||||
return "", nil, fmt.Errorf("fejkdata: %w", err)
|
||||
}
|
||||
if c, ok := categoryOn(&f.root, segs); ok && c.unloaded() {
|
||||
if err := loadReached(&f.root, nil, []categoryAt{c}); err != nil {
|
||||
if err := loadReached(&f.root, nil, []categoryAt{c}, nil); err != nil {
|
||||
return "", nil, fmt.Errorf("fejkdata: %w", loadError{err})
|
||||
}
|
||||
}
|
||||
@@ -107,8 +107,9 @@ func needs(root *folder, s categorySite) []categoryAt {
|
||||
|
||||
// loadReached loads the unloaded categories among wanted and every unloaded category they
|
||||
// or sites need, then runs sites and all it loaded through a whole load's pipeline. On an
|
||||
// error it puts back what it loaded, so the next call fails the same way.
|
||||
func loadReached(root *folder, sites []categorySite, wanted []categoryAt) error {
|
||||
// error it puts back what it loaded, so the next call fails the same way. defaultModules
|
||||
// holds, by path, the modules the source of each of sites reads by default.
|
||||
func loadReached(root *folder, sites []categorySite, wanted []categoryAt, defaultModules map[string][]string) error {
|
||||
queue := wanted
|
||||
for _, s := range sites {
|
||||
queue = append(queue, needs(root, s)...)
|
||||
@@ -119,6 +120,14 @@ func loadReached(root *folder, sites []categorySite, wanted []categoryAt) error
|
||||
err = b.run(root, sites)
|
||||
}
|
||||
if err != nil {
|
||||
err = withDefaultModules(err, func(category string) []string {
|
||||
for _, l := range b {
|
||||
if l.site.path == category {
|
||||
return l.was.defaultModules
|
||||
}
|
||||
}
|
||||
return defaultModules[category]
|
||||
})
|
||||
b.putBack()
|
||||
}
|
||||
return err
|
||||
@@ -169,6 +178,19 @@ func (b batch) run(root *folder, sites []categorySite) error {
|
||||
return categoryPipeline(sites, root.children).run()
|
||||
}
|
||||
|
||||
// withDefaultModules adds to err, where a reference of a category names nothing, the modules
|
||||
// the category's source reads by default, or that it names none.
|
||||
func withDefaultModules(err error, of func(category string) []string) error {
|
||||
var u unresolvedRead
|
||||
if !errors.As(err, &u) {
|
||||
return err
|
||||
}
|
||||
if modules := of(u.category); len(modules) > 0 {
|
||||
return fmt.Errorf("%w; the module holding %s reads by default %s", err, u.category, strings.Join(modules, ", "))
|
||||
}
|
||||
return fmt.Errorf("%w, and the module holding %s names no module it reads by default", err, u.category)
|
||||
}
|
||||
|
||||
func (b batch) putBack() {
|
||||
for _, l := range b {
|
||||
l.site.in.putUnloaded(l.site.name, l.was)
|
||||
|
||||
@@ -90,7 +90,7 @@ func loadInline(root *folder, n node, label string, typedByGo bool) error {
|
||||
if err := refuseFolderRefs(nodes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := loadReached(root, nil, referenced(root, nil, nodes)); err != nil {
|
||||
if err := loadReached(root, nil, referenced(root, nil, nodes), nil); err != nil {
|
||||
return loadError{err}
|
||||
}
|
||||
return pipeline{
|
||||
|
||||
@@ -12,13 +12,12 @@ import (
|
||||
"github.com/larvit/fejkdata/internal/jsonvalue"
|
||||
)
|
||||
|
||||
// Source is one tree to load: an fs.FS and the directory in it to start from. label
|
||||
// prefixes file names in errors; onDisk marks label as a directory that must exist.
|
||||
// Source is one tree to load, from the root of an fs.FS. label prefixes file names in
|
||||
// errors; onDisk marks label as a directory that must exist.
|
||||
type Source struct {
|
||||
fsys fs.FS
|
||||
label string
|
||||
onDisk bool
|
||||
base string
|
||||
}
|
||||
|
||||
// Dir is the tree in the directory path, its errors labelled with it.
|
||||
@@ -26,10 +25,10 @@ func Dir(path string) Source {
|
||||
return Source{fsys: os.DirFS(path), label: path, onDisk: true}
|
||||
}
|
||||
|
||||
// FS is the tree in fsys below base, "" for its root.
|
||||
func FS(fsys fs.FS, base string) Source { return Source{fsys: fsys, base: base} }
|
||||
// FS is the tree in fsys.
|
||||
func FS(fsys fs.FS) Source { return Source{fsys: fsys} }
|
||||
|
||||
// Category is one category file: the folders above it from the source's base, its name,
|
||||
// Category is one category file: the folders above it from the source's root, its name,
|
||||
// and its parsed JSON.
|
||||
type Category struct {
|
||||
Folders []string
|
||||
@@ -50,7 +49,7 @@ func (s Source) labelled(p string) string {
|
||||
|
||||
// dirPath is the folder that dir names, as fsys spells it.
|
||||
func (s Source) dirPath(dir []string) string {
|
||||
if p := path.Join(append([]string{s.base}, dir...)...); p != "" {
|
||||
if p := path.Join(dir...); p != "" {
|
||||
return p
|
||||
}
|
||||
return "."
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -19,7 +18,8 @@ const ManifestFile = ".fejkdata.json"
|
||||
type Manifest struct {
|
||||
// Index is every category of the source by dot path; nil where the manifest has none.
|
||||
Index map[string]IndexEntry `json:"index"`
|
||||
// Reads is every module the source reads by default, directly or through another.
|
||||
// Reads is every module the source reads by default, directly or through another; the
|
||||
// error for a reference naming nothing names them, and nothing loads them.
|
||||
Reads []string `json:"reads"`
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func (s Source) Manifest() (Manifest, error) {
|
||||
if err := s.check(); err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
b, err := fs.ReadFile(s.fsys, path.Join(s.base, ManifestFile))
|
||||
b, err := fs.ReadFile(s.fsys, ManifestFile)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return Manifest{}, nil
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (s Source) Manifest() (Manifest, error) {
|
||||
}
|
||||
|
||||
// ManifestPath is the manifest's path as errors name it.
|
||||
func (s Source) ManifestPath() string { return s.labelled(path.Join(s.base, ManifestFile)) }
|
||||
func (s Source) ManifestPath() string { return s.labelled(ManifestFile) }
|
||||
|
||||
// decodeManifest decodes a manifest, refusing any key or shape it does not define, and
|
||||
// reports every mistake: first each entry's own, in path order, then each parent naming
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// the read's first-level key, put under the rendering template's key, is one of the name's
|
||||
// addressed keys. keptInPick decides this, and readUnder reads it.
|
||||
//
|
||||
// In data/geo/SE/address.json, {.locality as l} binds l, and {l.street.name} passes the levels
|
||||
// In geo.SE.address, {.locality as l} binds l, and {l.street.name} passes the levels
|
||||
// "", "street" and "street.name", the last of which is its key. With {l.name} and {l.postal-code.code},
|
||||
// l's addressed keys are those three, "name", "postal-code" and "postal-code.code". Where p
|
||||
// binds a category whose format reads {first}, and both {p} and {p.first} are read, the {first}
|
||||
@@ -174,8 +174,8 @@ func (m *drawMemo) enteredFrame(scope *nameScope) *pickFrame {
|
||||
// When the pick renders another category, enter takes that category's frames from the pick's
|
||||
// memo, and renderFrame finds them and opens no new one.
|
||||
// So every read through one pick sees one pick of each name the other category binds:
|
||||
// {a.street} and {a.postal-code} in data/sv_SE/address.json read one pick of the name l
|
||||
// that data/geo/SE/address.json binds.
|
||||
// {a.street} and {a.postal-code} in sv_SE.address read one pick of the name l
|
||||
// that geo.SE.address binds.
|
||||
func readName(s *drawstate.State, env renderEnv, a arm) readValue {
|
||||
f := env.frameOf(a.named.scope)
|
||||
if f == nil {
|
||||
|
||||
+18
-19
@@ -3,18 +3,26 @@ package fejkdata
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// templateSite is a template to resolve, with its folder and category, both empty for an inline
|
||||
// template, and eachNode's label for it.
|
||||
// templateSite is a template to resolve, with its folder and its category's path, both empty for an
|
||||
// inline template, and eachNode's label for it.
|
||||
type templateSite struct {
|
||||
t *template
|
||||
folder []string
|
||||
label string
|
||||
category *categorySite
|
||||
category string
|
||||
}
|
||||
|
||||
// unresolvedRead is a reference of a category's template naming nothing.
|
||||
type unresolvedRead struct {
|
||||
category string
|
||||
err error
|
||||
}
|
||||
|
||||
func (u unresolvedRead) Error() string { return u.err.Error() }
|
||||
func (u unresolvedRead) Unwrap() error { return u.err }
|
||||
|
||||
// resolveCategoryTemplates resolves every template of the categories, once all data is merged, so a
|
||||
// reference sees the override-resolved tree.
|
||||
func resolveCategoryTemplates(sites []categorySite, root map[string]node) error {
|
||||
@@ -22,7 +30,7 @@ func resolveCategoryTemplates(sites []categorySite, root map[string]node) error
|
||||
for _, s := range sites {
|
||||
if err := eachNode(s.n, s.path, func(label string, n node) error {
|
||||
if t, isTemplate := n.(*template); isTemplate {
|
||||
ts = append(ts, templateSite{t: t, folder: s.dir, label: label, category: &s})
|
||||
ts = append(ts, templateSite{t: t, folder: s.dir, label: label, category: s.path})
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
@@ -58,8 +66,12 @@ type resolvedNames struct {
|
||||
func resolveTemplates(ts []templateSite, root map[string]node) error {
|
||||
for _, s := range ts {
|
||||
refs, err := s.t.resolveRefs(s.folder, s.label, root)
|
||||
var ne noEntry
|
||||
if err != nil && s.category != "" && errors.As(err, &ne) {
|
||||
return unresolvedRead{category: s.category, err: err}
|
||||
}
|
||||
if err != nil {
|
||||
return s.withReads(err)
|
||||
return err
|
||||
}
|
||||
s.t.refs = refs
|
||||
}
|
||||
@@ -90,16 +102,3 @@ func resolveTemplates(ts []templateSite, root map[string]node) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// withReads adds to err, where a reference of a category names nothing, the modules the
|
||||
// category's manifest names it reading by default, or that it names none.
|
||||
func (s templateSite) withReads(err error) error {
|
||||
var ne noEntry
|
||||
if s.category == nil || !errors.As(err, &ne) {
|
||||
return err
|
||||
}
|
||||
if len(s.category.reads) == 0 {
|
||||
return fmt.Errorf("%w, and the module holding %s names no module it reads by default", err, s.category.path)
|
||||
}
|
||||
return fmt.Errorf("%w; the module holding %s reads by default %s", err, s.category.path, strings.Join(s.category.reads, ", "))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
`Bar = 9`
|
||||
|
||||
`Next ID = 195`
|
||||
`Next ID = 196`
|
||||
|
||||
| Goal | W |
|
||||
|---|---|
|
||||
@@ -128,6 +128,7 @@
|
||||
| 168 | 0.2.0 | principle | **Test the shared steps of `data-import/` in the merge gate, starting with `geo.fence`.** | 2 | 4 | 2 | 6 | 3 | 11.8 |
|
||||
| 161 | 0.2.0 | | **Let a field whose name starts or ends with whitespace be bound to a name, or refuse such an identifier, so `{ x as p}` means one thing.** | 2 | 2 | 2 | 6 | 5.6, 4.1 | 11.7 |
|
||||
| 175 | 0.2.0 | | **Rewrite the comments the 2026-10-09 panel needed several reads of, and delete those it read as restating their code.** | 1 | 2 | 1 | 6 | 3.4 | 11.3 |
|
||||
| 195 | 0.2.0 | principle | **Parse each builtin argument once, in `checkArgs`, and hand `prep` and `proveNumber` the parsed value.** | 3 | 3 | 2 | 6 | 3 | 11.1 |
|
||||
| 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 |
|
||||
| 172 | 0.2.0 | | **Pass `drawSteps` the kind of read it draws as an argument of its own, so no caller signals a sibling path by a nil `pins` or a fresh read by a nil `memo`.** | 2 | 2 | 1 | 6 | 3.2 | 10.3 |
|
||||
| 173 | 0.2.0 | | **Call what a token reads its spelling, never a name, in `format.go`, `arm.go` and `reference.go`, so a name in the engine is only a bound name.** | 2 | 3 | 1 | 6 | 3.2 | 10.1 |
|
||||
@@ -181,7 +182,7 @@ Pick the ten from a published ranking of languages by total speakers, such as Et
|
||||
|
||||
### 185. Seat a panel of the README's audience personas to pull data in through the module API and build a custom CLI, and fix what it names.
|
||||
|
||||
Needs item 184; 0.1.0 is not cut before it. Carries out the decision "The shipped data is Go packages a library imports by choice, and the CLI carries every one, from 0.1.0". Seats: the Go test author, the bulk fixture author, a custom CLI's builder, and a data author with a `-d` folder and the replace flag, each in a scratch Go module against the checkout or a pre-release tag. Bench the geo packages' names, `data.Modules()`, the error naming the import set, and the `.fejkdata.json` manifest a data author writes by hand. Decoding the manifest is most of `New`'s cost (about 0.55 ms and 2449 allocations on 2026-10-10). An entry lists every path below its category, its linked tables' paths too, so `geo.*.region` repeats its whole family. An entry holding only its own paths, with `List` composing a family from the entries naming it as `parent`, would shrink it. Record the ruling in `docs/`.
|
||||
Needs item 184; 0.1.0 is not cut before it. Carries out the decision "The shipped data is Go packages a library imports by choice, and the CLI carries every one, from 0.1.0". Seats: the Go test author, the bulk fixture author, a custom CLI's builder, and a data author with a `-d` folder and the replace flag, each in a scratch Go module against the checkout or a pre-release tag. Bench the geo packages' names, `data.Modules()`, the error naming the import set, and the `.fejkdata.json` manifest a data author writes by hand. Decoding the manifest is most of `New`'s cost (about 0.55 ms and 2449 allocations on 2026-10-10, 2864 allocations once each module carried its own). An entry lists every path below its category, its linked tables' paths too, so `geo.*.region` repeats its whole family. An entry holding only its own paths, with `List` composing a family from the entries naming it as `parent`, would shrink it. Record the ruling in `docs/`.
|
||||
|
||||
### 16. Add locale categories: company, phone, finance, vehicle, words.
|
||||
|
||||
@@ -414,7 +415,7 @@ A hyphen is always subtraction in a calc, and the shipped data has hyphenated fi
|
||||
|
||||
### 194. Let a data author write and check their own `.fejkdata.json` index with the library or the CLI, and fail a load whose index leaves out a category file.
|
||||
|
||||
Goal 6.3 has an author prove their module, and goal 9.1 has fejkdata's data load as anyone's; today only fejkdata's own test, `TestShippedManifestIsCurrent` over `indexEntry`, writes and checks an index. A category file the index leaves out never loads, silently.
|
||||
Goal 6.3 has an author prove their module, and goal 9.1 has fejkdata's data load as anyone's; today only fejkdata's own test, `TestShippedManifestsAreCurrent` over `indexEntry`, writes and checks an index. A category file the index leaves out never loads, silently.
|
||||
|
||||
### 53. Let a table column carry a `datatype`, so `--format json` writes `"safe": true` and `--format sql` a boolean.
|
||||
|
||||
@@ -488,6 +489,10 @@ Against "Most, if not all, code should have automated tests" in `technical-princ
|
||||
|
||||
Reread: `readField`'s note on clearing the pick (`render.go`), `pickKey.under`, which three seats read backwards, `scopeAround`'s "short of the frames n renders itself", `enter` returning `env.base` as the pop mark, and add a line at `walkEvery`'s three-index slice, which three seats found unmarked. Restating: `compileCells`, `columnOrLinked`, `render`'s first line, `categorySites`, `madeFolder`, `categoryOf`, `rendersInside`, `templateSite`, `templateOptions`, `compileFrom`, `flagArg`, `drawSeed`, the `calc` parser's precedence line in `internal/grammar`, and `compileChoice`'s note that a uniform choice draws in O(1), which `choice`'s doc already says. A doc comment `go doc` shows on an exported name stays.
|
||||
|
||||
### 195. Parse each builtin argument once, in `checkArgs`, and hand `prep` and `proveNumber` the parsed value.
|
||||
|
||||
Applies "Validate data and build DTOs as early as possible" in `~/.claude/principles/technical-principles.md`. Today `checkArgs` (`internal/builtinfunc/builtins.go`) checks an argument, then `prep` and `proveNumber` parse it again through `atoi`/`atof`, which panic, as do `layout.go`, `transform.go` and `calc.go`; two parsers can drift, and a drift panics at compile instead of failing the load.
|
||||
|
||||
### 112. Check a calc's field operands where its template resolves, beside its name operands, once item 36 reports every mistake at once.
|
||||
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user