Answer architecture round 1: stdin drops one newline, \n or \r\n, and reaches the library unchanged, inputError names the input's fault, two decisions and item 110 stop naming a CLI argument, the gate exemption moves to the floor entry, and item 156 asks about the JSON string
This commit is contained in:
@@ -11,8 +11,8 @@
|
||||
wait for one.
|
||||
- README goal 3 is gated at 7.0 on the `comprehension-panel` skill, and below it
|
||||
nothing else merges — no feature, no category, no data — bar a security fix, a
|
||||
dependency bump, a repair of behaviour the README documents, the infrastructure
|
||||
the round itself runs on, and the CLI reading its template from stdin. `todo.md` carries the round: a PR per item it plans, the nine-seat
|
||||
dependency bump, a repair of behaviour the README documents, and the infrastructure
|
||||
the round itself runs on. `todo.md` carries the round: a PR per item it plans, the nine-seat
|
||||
findings at depth 1 or a restructure the maintainer approved, then the nine-seat run
|
||||
again, until the score passes. At or above 7.0
|
||||
every pull request scores with the four-seat run and answers it in one run. No merge
|
||||
@@ -41,7 +41,7 @@ In [docs/decisions.md](docs/decisions.md):
|
||||
- An argument is a template by its shape, not by a flag
|
||||
- The CLI renders the template on its stdin, less one newline ending it
|
||||
- An inline template that does not compile is misuse (exit 2), including a reference that resolves to nothing
|
||||
- The library rejects a padded JSON template, and the CLI trims the whitespace around one on stdin
|
||||
- A padded JSON argument is rejected, not trimmed
|
||||
- `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`
|
||||
|
||||
+3
-4
@@ -28,10 +28,9 @@ replacement, and each removed path, column or flag.
|
||||
under [Names](README.md#names).
|
||||
- First release: the CLI, the library and the shipped data set.
|
||||
- The CLI renders the template piped to its stdin, `echo -n '{/sv_SE.person} hihi' |
|
||||
fejkdata`, and takes no path or template as an argument. One newline ending stdin is
|
||||
dropped, and so is the whitespace around a JSON template. A template of one reference
|
||||
alone, `{/users}`, is the record `users` under `--format`. An argument, or nothing
|
||||
piped in, exits 2.
|
||||
fejkdata`, and takes no path or template as an argument. One newline ending stdin,
|
||||
`\n` or `\r\n`, is dropped. A template of one reference alone, `{/users}`, is the
|
||||
record `users` under `--format`. An argument, or nothing piped in, exits 2.
|
||||
- Table categories: a category JSON naming a `rows` TSV beside it, with the options
|
||||
`key`, `name`, `weight` and `parent`; a path selects a row by key or name,
|
||||
`misc.territory[SE]`, and descends to a linked table by name, one path drawing each
|
||||
|
||||
@@ -35,10 +35,11 @@ inside one: each dot segment descends one level — folders, then the category (
|
||||
file), then fields — and `[SE]` after a [table](#table) selects its row. `--list` prints
|
||||
these paths; a template reads one as `{/path}`.
|
||||
|
||||
One newline ending stdin is dropped, so `echo` and `echo -n`, a file and a heredoc
|
||||
render alike; end stdin with two newlines to print one. Whitespace around a JSON
|
||||
template is dropped too. Which spellings a template rejects, and what each names
|
||||
instead, is under
|
||||
One newline ending stdin, `\n` or `\r\n`, is dropped, so `echo` and `echo -n`, a file
|
||||
and a heredoc render alike; end stdin with two newlines to print one. Some shells' `echo`
|
||||
reads a backslash as an escape, so pipe a template holding one with `printf '%s'` or from
|
||||
a file. One reference alone, `{/users}`, is the record `users` under `--format`; the
|
||||
rest of stdin's rules are under
|
||||
[Decisions](docs/decisions.md#the-cli-renders-the-template-on-its-stdin-less-one-newline-ending-it).
|
||||
|
||||
| Flag | |
|
||||
@@ -56,7 +57,7 @@ instead, is under
|
||||
`--name value` and `--name=value` both work, a short flag's value attaches or
|
||||
follows (`-n3`, `-n 3`) and short flags bundle (`-hn 3`) — see
|
||||
[Decisions](docs/decisions.md#flags-follow-getopt_long); flags go anywhere, `--` ends them. Exit codes: `0` success, `1` runtime error (missing
|
||||
dir, unknown path), `2` misuse — a bad flag, an argument, nothing on stdin, or a
|
||||
dir, a lone reference to nothing), `2` misuse — a bad flag, an argument, nothing on stdin, or a
|
||||
template that does not compile. From a checkout:
|
||||
`go run ./cmd/fejkdata …`.
|
||||
|
||||
@@ -992,7 +993,7 @@ internal/builtinfunc/ the {name()} functions: their checks and draws, checksums,
|
||||
internal/datafiles/ the walk of a data tree: its folders, its category files and the rows files beside them
|
||||
internal/datatype/ the datatype a record column holds; the root's DataType is an alias of it
|
||||
internal/drawstate/ the seeded randomness and the {seq()} counters a generator draws through
|
||||
internal/grammar/ how the template language is written: format tokens, paths and selectors, reference sigils, identifiers, calc syntax, and whether an argument is a template or a path
|
||||
internal/grammar/ how the template language is written: format tokens, paths and selectors, reference sigils, identifiers, calc syntax, and whether a struct tag is a template or a path
|
||||
internal/invariant/ the one phrase every package panics with when an invariant breaks
|
||||
internal/proven/ what a proof knows of a value, and the bounds a calc takes from its operands
|
||||
internal/rows/ a table's rows: the TSV, the options proved over it, the links between tables, row selection and draws, and the pin set one path or one named pick fixes
|
||||
|
||||
+30
-41
@@ -9,7 +9,6 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -36,8 +35,8 @@ A reference names a category or a dotted path into one ({/sv_SE.person.last}), a
|
||||
table's row by key or name ({/misc.territory[SE]}), and .. steps up from a row to
|
||||
the row it links to ({/geo.SE.locality..municipality.name}).
|
||||
|
||||
One newline ending stdin is dropped, so echo and echo -n render alike; end it with
|
||||
two to print one. Whitespace around a JSON template is dropped too.
|
||||
One newline ending stdin, \n or \r\n, is dropped, so echo and echo -n render
|
||||
alike; end stdin with two to print one.
|
||||
|
||||
With --format json, ndjson, csv or sql the template must name a record — a
|
||||
template whose fields are its columns, or one reference alone, {/sv_SE.person} —
|
||||
@@ -60,13 +59,13 @@ A short flag's value attaches or follows (-n3, -n 3); short flags bundle (-hn 3)
|
||||
`
|
||||
|
||||
type invocation struct {
|
||||
args []string
|
||||
dirs []string
|
||||
format string
|
||||
formatSet bool
|
||||
help bool
|
||||
list bool
|
||||
noShipped bool
|
||||
args []string
|
||||
repeat int
|
||||
repeatSet bool
|
||||
seed uint64
|
||||
@@ -267,7 +266,6 @@ func (in invocation) checkFlags() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// pipeHint is how a template reaches fejkdata, for the errors of a run that has none.
|
||||
const pipeHint = "echo -n '{/sv_SE.person}' | fejkdata"
|
||||
|
||||
// check rejects a flag combination or an argument that cannot run, before any data is
|
||||
@@ -333,7 +331,7 @@ func textDraw(f *fejkdata.Generator, src input) (func() (string, error), error)
|
||||
}
|
||||
t, err := f.NewTemplate(src.template)
|
||||
if err != nil {
|
||||
return nil, templateError{err}
|
||||
return nil, inputError{err}
|
||||
}
|
||||
return func() (string, error) { return t.Fake(), nil }, nil
|
||||
}
|
||||
@@ -345,7 +343,7 @@ func (in invocation) recordStream(f *fejkdata.Generator, src input) (func() (*fe
|
||||
if src.path == "" {
|
||||
t, err := f.NewRecordTemplate(src.template)
|
||||
if err != nil {
|
||||
return nil, "", templateError{err}
|
||||
return nil, "", inputError{err}
|
||||
}
|
||||
record = func() (*fejkdata.Record, error) { return t.Fake(), nil }
|
||||
}
|
||||
@@ -393,58 +391,51 @@ func (in invocation) writeRecords(f *fejkdata.Generator, src input, w io.Writer)
|
||||
|
||||
// defaultTable names the INSERT target when --table is absent: the path's last
|
||||
// segment, or "records" for a template that reads no path alone.
|
||||
func defaultTable(arg string) string {
|
||||
if arg == "" {
|
||||
func defaultTable(path string) string {
|
||||
if path == "" {
|
||||
return "records"
|
||||
}
|
||||
var names strings.Builder // the path with its [selectors] cut out
|
||||
for depth, i := 0, 0; i < len(arg); i++ {
|
||||
for depth, i := 0, 0; i < len(path); i++ {
|
||||
switch {
|
||||
case arg[i] == '[':
|
||||
case path[i] == '[':
|
||||
depth++
|
||||
case arg[i] == ']':
|
||||
case path[i] == ']':
|
||||
depth--
|
||||
case depth == 0:
|
||||
names.WriteByte(arg[i])
|
||||
names.WriteByte(path[i])
|
||||
}
|
||||
}
|
||||
segments := strings.Split(names.String(), ".")
|
||||
return segments[len(segments)-1]
|
||||
}
|
||||
|
||||
// templateError marks a failure that is the input's own fault — no template, or one
|
||||
// that does not compile. run reports it as misuse (exit 2, with a pointer to --help),
|
||||
// unlike an unknown path, which is a runtime error (exit 1).
|
||||
type templateError struct{ error }
|
||||
// inputError marks a failure that is the input's own fault — no template, or one that
|
||||
// does not compile. run reports it as misuse (exit 2, with a pointer to --help), unlike
|
||||
// an unknown path, which is a runtime error (exit 1).
|
||||
type inputError struct{ error }
|
||||
|
||||
func (e templateError) Unwrap() error { return e.error }
|
||||
func (e inputError) Unwrap() error { return e.error }
|
||||
|
||||
// input is what stdin names: the path a lone reference reads, or else the template as
|
||||
// the library takes it.
|
||||
// input is what stdin names: the path a lone reference reads, or else the template.
|
||||
type input struct {
|
||||
path string
|
||||
template string
|
||||
}
|
||||
|
||||
// readInput reads stdin less one newline ending it. A JSON object, array or string
|
||||
// sheds the whitespace around it; any other text becomes a JSON string, so the library
|
||||
// reads it as a format string even where it is JSON of another kind, such as 42.
|
||||
func readInput(raw string) (input, error) {
|
||||
text := strings.TrimSuffix(raw, "\n")
|
||||
// parseInput reads stdin's text less one newline ending it.
|
||||
func parseInput(raw string) (input, error) {
|
||||
text, found := strings.CutSuffix(raw, "\n")
|
||||
if found {
|
||||
text = strings.TrimSuffix(text, "\r")
|
||||
}
|
||||
if text == "" {
|
||||
return input{}, fmt.Errorf("stdin holds no template: %s", pipeHint)
|
||||
}
|
||||
if path, lone := loneReference(text); lone {
|
||||
return input{path: path}, nil
|
||||
}
|
||||
if trimmed := strings.TrimSpace(text); trimmed != "" && strings.ContainsRune(`{["`, rune(trimmed[0])) && json.Valid([]byte(trimmed)) {
|
||||
return input{template: trimmed}, nil
|
||||
}
|
||||
quoted, err := json.Marshal(text)
|
||||
if err != nil {
|
||||
return input{}, err
|
||||
}
|
||||
return input{template: string(quoted)}, nil
|
||||
return input{template: text}, nil
|
||||
}
|
||||
|
||||
// loneReference is the path that text, one reference from the root and nothing else,
|
||||
@@ -491,7 +482,7 @@ func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
|
||||
}
|
||||
var src input
|
||||
if !in.list {
|
||||
if src, err = source(stdin); err != nil {
|
||||
if src, err = readStdin(stdin); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
}
|
||||
@@ -515,25 +506,23 @@ func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// source reads the input from stdin.
|
||||
func source(stdin io.Reader) (input, error) {
|
||||
func readStdin(stdin io.Reader) (input, error) {
|
||||
if stdin == nil {
|
||||
return input{}, templateError{fmt.Errorf("the template comes from stdin, and nothing is piped in: %s", pipeHint)}
|
||||
return input{}, inputError{fmt.Errorf("the template comes from stdin, and nothing is piped in: %s", pipeHint)}
|
||||
}
|
||||
raw, err := io.ReadAll(stdin)
|
||||
if err != nil {
|
||||
return input{}, err
|
||||
}
|
||||
src, err := readInput(string(raw))
|
||||
src, err := parseInput(string(raw))
|
||||
if err != nil {
|
||||
return input{}, templateError{err}
|
||||
return input{}, inputError{err}
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// fail reports err and returns its exit code: 2 for the input's own fault, else 1.
|
||||
func fail(stderr io.Writer, err error) int {
|
||||
var te templateError
|
||||
var te inputError
|
||||
if errors.As(err, &te) {
|
||||
return misuse(stderr, te.error)
|
||||
}
|
||||
|
||||
+23
-35
@@ -242,8 +242,8 @@ func TestRunMisuse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestRunTakesNoArgument pins the way from the old spelling, a template or a path as
|
||||
// an argument, to the one that runs: the template on stdin.
|
||||
// TestRunTakesNoArgument pins the error for a path or a template given as an argument:
|
||||
// it shows the template piped to stdin.
|
||||
func TestRunTakesNoArgument(t *testing.T) {
|
||||
for _, args := range [][]string{{"sv_SE.person"}, {"{/sv_SE.person}"}, {"name:", "{/sv_SE.person.last}"}} {
|
||||
code, out, errb := runOut("", args...)
|
||||
@@ -267,47 +267,32 @@ func TestRunTerminalStdinIsMisuse(t *testing.T) {
|
||||
|
||||
func TestRunDropsOneTrailingNewline(t *testing.T) {
|
||||
for stdin, want := range map[string]string{
|
||||
"hihi": "hihi\n",
|
||||
"hihi\n": "hihi\n",
|
||||
"hihi\n\n": "hihi\n\n",
|
||||
"a\nb\n": "a\nb\n",
|
||||
" hihi \n": " hihi \n",
|
||||
"hihi\r\n": "hihi\r\n",
|
||||
"{{x}}\n": "{x}\n",
|
||||
"x: {/sv_SE.person.last}\n\n": "",
|
||||
"hihi": "hihi\n",
|
||||
"hihi\n": "hihi\n",
|
||||
"hihi\r\n": "hihi\n",
|
||||
"hihi\n\n": "hihi\n\n",
|
||||
"hihi\r\n\r\n": "hihi\r\n\n",
|
||||
"a\nb\n": "a\nb\n",
|
||||
" hihi \n": " hihi \n",
|
||||
"{{x}}\n": "{x}\n",
|
||||
"\"a\"\n": "a\n",
|
||||
} {
|
||||
code, out, errb := runOut(stdin, "--seed", "1")
|
||||
if want == "" {
|
||||
if code != 0 || !strings.HasPrefix(out, "x: ") || !strings.HasSuffix(out, "\n\n") || strings.Count(out, "\n") != 2 {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want the template's own newline, then the line's", stdin, code, out, errb)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if code != 0 || out != want {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want %q", stdin, code, out, errb, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTextPrintsAsWritten(t *testing.T) {
|
||||
for _, stdin := range []string{"sv_SE.person", "42", "null", "true", "[Skåne län]", "x[1]y", `say "hi"`} {
|
||||
code, out, errb := runOut(stdin)
|
||||
if code != 0 || out != stdin+"\n" {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want the text as written", stdin, code, out, errb)
|
||||
}
|
||||
code, out, errb := runOut("x: {/sv_SE.person.last}\n\n", "--seed", "1")
|
||||
if code != 0 || !strings.HasPrefix(out, "x: ") || !strings.HasSuffix(out, "\n\n") || strings.Count(out, "\n") != 2 {
|
||||
t.Errorf("a template ending in a newline = %d, %q, %q; want its own newline, then the line's", code, out, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTrimsWhitespaceAroundJSON(t *testing.T) {
|
||||
for _, stdin := range []string{
|
||||
"{\"format\":\"{x}\",\"x\":[\"a\",\"b\"]}\n",
|
||||
" {\"format\":\"{x}\",\"x\":[\"a\",\"b\"]}\n\n",
|
||||
"\t[\"a\",\"b\"]\r\n",
|
||||
" \"a\" \n",
|
||||
} {
|
||||
code, out, errb := runOut(stdin, "--seed", "1")
|
||||
if code != 0 || (out != "a\n" && out != "b\n") {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want a or b", stdin, code, out, errb)
|
||||
func TestRunTextPrintsAsWritten(t *testing.T) {
|
||||
for _, stdin := range []string{"sv_SE.person", "[Skåne län]", "x[1]y", `say "hi"`} {
|
||||
code, out, errb := runOut(stdin)
|
||||
if code != 0 || out != stdin+"\n" {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want the text as written", stdin, code, out, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,6 +449,9 @@ func TestRunTemplateMisuse(t *testing.T) {
|
||||
`{"format":"x"}`: "is a string",
|
||||
"name: {/no.such.path}": "no entry",
|
||||
"a } b": "}}",
|
||||
" [\"a\",\"b\"]": "may not be padded",
|
||||
"[\"a\",\"b\"]\n\n": "may not be padded",
|
||||
"42": "number",
|
||||
} {
|
||||
code, out, errb := runOut(stdin, "--seed", "1")
|
||||
if code != 2 || out != "" || !strings.Contains(errb, "try 'fejkdata --help'") || !strings.Contains(errb, want) {
|
||||
@@ -579,7 +567,7 @@ func TestRunRecordNDJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRecordCSVRoundTrips(t *testing.T) {
|
||||
code, out, errb := runOut("{/users}\n", "--seed", "1", "--format", "csv", "--repeat", "3", "--data-path", recordDir(t))
|
||||
code, out, errb := runOut("{/users}\r\n", "--seed", "1", "--format", "csv", "--repeat", "3", "--data-path", recordDir(t))
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
|
||||
+17
-17
@@ -67,9 +67,8 @@ says so. No `--template` flag is needed. Reserving the characters whole — thou
|
||||
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. An argument or 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
|
||||
where it is printed. 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.
|
||||
|
||||
@@ -79,14 +78,16 @@ other caller read one.
|
||||
|
||||
The CLI takes no path or template as an argument: an argument is misuse, and so is a
|
||||
terminal with nothing piped in, which would otherwise wait for typed input. Both errors
|
||||
show `echo -n '{/sv_SE.person}' | fejkdata`. One newline ending stdin is dropped, so
|
||||
`echo`, a file and a heredoc render as `echo -n` does; only one, so a template that
|
||||
should end in a newline ends stdin with two. Any text that is no JSON object, array or
|
||||
string, `42` and `null` included, is a format string and prints as written. One
|
||||
reference from the root and nothing else, `{/users}`, reads the path it names: it renders
|
||||
the same text, and under `--format` it is the record `users`, with `users` the default
|
||||
`--table`. `--list` still prints paths, each read as `{/path}`. Valid while the CLI reads
|
||||
one template per run.
|
||||
show `echo -n '{/sv_SE.person}' | fejkdata`. One newline ending stdin, `\n` or `\r\n`,
|
||||
is dropped, so `echo`, a file and a heredoc render as `echo -n` does; only one, so a
|
||||
template that should end in a newline ends stdin with two. The rest of stdin reaches
|
||||
`NewTemplate` unchanged, so a padded JSON template is refused there, as goal 5.7 asks.
|
||||
One reference from the root and nothing else, `{/users}`, reads the path it names: it
|
||||
renders the same text, and under `--format` it is the record `users`, with `users` the
|
||||
default `--table`; one to nothing exits 1, as an unknown path does. That reading sits in
|
||||
the CLI against goal 3.2 until `todo.md` item 129 moves it into the library. `--list`
|
||||
still prints paths, each read as `{/path}`. Valid while the CLI reads one template per
|
||||
run.
|
||||
|
||||
## An inline template that does not compile is misuse (exit 2), including a reference that resolves to nothing
|
||||
|
||||
@@ -96,15 +97,12 @@ The whole template is the spelling under test, and `NewTemplate` compiles, links
|
||||
validates as one step. An unknown *path* stays a runtime error (exit 1): there the
|
||||
template is well-formed and only the data is absent.
|
||||
|
||||
## The library rejects a padded JSON template, and the CLI trims the whitespace around one on stdin
|
||||
## A padded JSON argument is rejected, not trimmed
|
||||
|
||||
2026-09-03, Lilleman auf Larv; the CLI's trim 2026-10-07, Lilleman auf Larv. Serves goal 7.2.
|
||||
2026-09-03, Lilleman auf Larv.
|
||||
|
||||
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, `echo` and a file end in a newline nobody meant to render, so the
|
||||
maintainer ruled that the whitespace around a JSON object, array or string is dropped
|
||||
before the library reads it. Valid while a JSON template on stdin is one value.
|
||||
drops it — so the spelling that renders is named rather than silently chosen.
|
||||
|
||||
## `FakeTemplate` and `NewTemplate` both stay
|
||||
|
||||
@@ -852,6 +850,8 @@ 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.
|
||||
|
||||
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.
|
||||
|
||||
## The template engine stays the root package until item 107; what reads no engine type, a table's rows included, sits in `internal/`
|
||||
|
||||
+2
-2
@@ -68,8 +68,8 @@ func rendersInside(nodes []node, t *template) bool {
|
||||
// template reaches the binder through fields. A single read as a calc operand stands where a calc
|
||||
// could not read the spelling, a reference or a path. So {/misc.coordinate.lat as lat}{calc(lat * 60)}
|
||||
// stands, since {calc(/misc.coordinate.lat * 60)} does not compile.
|
||||
// Where the spelling would be a whole CLI argument or a whole struct tag, that entry point refuses
|
||||
// it and names the bare path.
|
||||
// Where the spelling would be a whole struct tag, that entry point refuses it and names the bare
|
||||
// path.
|
||||
func (b *nameBinding) refuseSingleRead(uses []nameUse) error {
|
||||
if len(uses) == 0 {
|
||||
panic(invariant.Broken("name %q has no read at resolve, though its compile found one", b.name))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
`Bar = 9`
|
||||
|
||||
`Next ID = 156`
|
||||
`Next ID = 157`
|
||||
|
||||
| Goal | W |
|
||||
|---|---|
|
||||
@@ -70,14 +70,15 @@
|
||||
| 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 | | **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, 5.7, 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 |
|
||||
| 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 |
|
||||
| 145 | 0.1.0 | | **Move `doc.go`'s definitions of a level, a pick key and an addressed key, with their worked example, to `pickKey` in `pick.go`, and gather `levelKeys`, `addressedKeys`, `renderEnv.keeps` and `readField`'s kept read beside `readUnder`, so a reader follows a named read in one file.** | 2 | 3 | 1 | 9 | 3.4, 3.2 | 15.4 |
|
||||
| 127 | 0.1.0 | | **Give a Swedish street only postal codes whose delivery digit means street or rural delivery, dropping codes whose delivery digit is 9, or record why they stay.** | 3 | 3 | 4 | 6 | 4 | 14.4 |
|
||||
| 110 | 0.1.0 | | **Refuse an empty segment in a path `Fake`, `FakeRecord` and the CLI take, as a reference does: `sv_SE.person.` reports `path has an empty segment`.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
||||
| 110 | 0.1.0 | | **Refuse an empty segment in a path `Fake` and `FakeRecord` take, as a reference does: `sv_SE.person.` reports `path has an empty segment`.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
||||
| 156 | 0.1.0 | question | **Refuse a JSON string whose text stands alone as a format string, `"x {/a}"`, naming the bare text, or keep it as the decision "An argument is a template by its shape, not by a flag" does.** | 2 | 2 | 3 | 6 | 5.7 | 13.1 |
|
||||
| 10 | 0.1.0 | | **Test that every node kind reaches each switch over node kinds.** | 1 | 3 | 2 | 6 | 3.2 | 13.1 |
|
||||
| 148 | 0.1.0 | | **State in `geo.py` the locality row each country's script hands it, and meet the fence that every parent row has a child row in one shared step.** | 2 | 3 | 2 | 6 | 2.1, 3.2 | 12.6 |
|
||||
| 146 | 0.1.0 | | **Move `sharedPaths` and `subPaths` from `list.go` beside `carriedByAll` in `path.go`, and `join` beside `sortedNames` in `sorted.go`.** | 1 | 2 | 1 | 6 | 3.1, 3.2 | 11.3 |
|
||||
@@ -141,7 +142,6 @@
|
||||
| 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 |
|
||||
| 43 | 0.3.0 | | **Call `templateError` a compile failure in its doc.** | 1 | 1 | 1 | 5 | 3.3 | 9.8 |
|
||||
| 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 |
|
||||
@@ -280,9 +280,13 @@ All 13 seats of the 2026-10-07 panel and scoring run on ad59967 again named the
|
||||
|
||||
`docs/research/research-geo-se.md` reads delivery digit 0 or 1 as boxes, 2–4 and 6–7 as street, 5 as rural, 8 as reply mail and 9 as competitions or temporary, with exceptions in big cities. `street_delivery` drops 0, 1 and 8 and keeps 9. The panel of 2026-10-05 found it; whether a shipped street carries a 9 code is unchecked.
|
||||
|
||||
### 110. Refuse an empty segment in a path `Fake`, `FakeRecord` and the CLI take, as a reference does: `sv_SE.person.` reports `path has an empty segment`.
|
||||
### 110. Refuse an empty segment in a path `Fake` and `FakeRecord` take, as a reference does: `sv_SE.person.` reports `path has an empty segment`.
|
||||
|
||||
Today `fejkdata 'sv_SE.person.'` reports `no field ""`, while `{/sv_SE.person.}` reports `path has an empty segment`. `grammar.CheckSegments` is the check a reference, a sibling path and a step up `..` run.
|
||||
Today `Fake("sv_SE.person.")` reports `no field ""`, while `{/sv_SE.person.}` reports `path has an empty segment`. `grammar.CheckSegments` is the check a reference, a sibling path and a step up `..` run.
|
||||
|
||||
### 156. Refuse a JSON string whose text stands alone as a format string, `"x {/a}"`, naming the bare text, or keep it as the decision "An argument is a template by its shape, not by a flag" does.
|
||||
|
||||
Goal 5.7 refuses a wrapper around what could stand alone. That decision keeps the JSON string because, as a CLI argument, bare text read as a path; stdin reads bare text as a format string, so `"x {/a}"` and `x {/a}` render alike there and in `NewTemplate`. A JSON string still earns its place where its escapes carry what bare text cannot. The goals audit of the stdin change found it.
|
||||
|
||||
### 10. Test that every node kind reaches each switch over node kinds.
|
||||
|
||||
@@ -517,10 +521,6 @@ Both phone formats carry the same 21-code `area` list, so an edit to one copy ca
|
||||
|
||||
`technical-principles.md` holds that test coverage should not decline. `docker compose run --rm cover` prints the figure, and neither the `Dockerfile` gate nor `.github/workflows/test.yml` checks it.
|
||||
|
||||
### 43. Call `templateError` a compile failure in its doc.
|
||||
|
||||
Its doc says render failure, and it wraps what `NewTemplate` and `NewRecordTemplate` refuse.
|
||||
|
||||
### 95. Hold a struct's columns and the field each fills in one slice.
|
||||
|
||||
`structShape.fieldIndexes` pairs with its record template's `columns` by index alone, against the technical principle "one owner per value".
|
||||
|
||||
Reference in New Issue
Block a user