Answer architecture round 1: a module's reads sits on its index entry or, for a source without an index, in the load that walks it, the note names default modules, the shipped modules are listed by name, datafiles drops its unused base, and item 195 files the builtins' second parse
This commit is contained in:
@@ -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