From 98f24243ee557821a768293c9ad512f883bbe66a Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Wed, 11 Nov 2026 20:22:11 -0300 Subject: [PATCH] refgen: a figure with no constant behind it is held by the text that decides it Fourteen of the figures the pages say were listed as held by nothing: a session's seven days is a field's default, the metric store's backup a systemd timer, the retentions a composite literal, the proxy's record a Terraform local. A line may now name the text that decides the figure, in spin or the module, and the check fails when the file no longer has it, whatever its spacing. Eleven lines move to it; three stay unheld, each another party's fact or a variable's default among many. Co-Authored-By: Claude Fable 5.2 --- README.md | 4 ++-- tools/refgen/figures.go | 42 ++++++++++++++++++++++++++++-------- tools/refgen/figures.txt | 25 ++++++++++----------- tools/refgen/figures_test.go | 7 ++++-- tools/refgen/main.go | 2 +- 5 files changed, 54 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 9a3b675..9dad9e0 100644 --- a/README.md +++ b/README.md @@ -130,8 +130,8 @@ that is not one task (`taskgate`). module's defaults by `task reference`, and a page shows one with ``. - **A figure said in prose has a line** in `tools/refgen/figures.txt`: every duration, size and count a hand-written page says outside ``, with the constant of spin's that decides it - (`spin:# = 15m`), which `task reference:check` reads and holds the line to, or - why nothing holds it. A figure with no line fails, and so does a line its page no longer says. + (`spin:# = 15m`), which `task reference:check` reads and holds the line to; with the text of a file + that decides it where no constant does (`spin: has ""`); or with why nothing holds it. A figure with no line fails, and so does a line its page no longer says. - **What a release is made of**, `reference/release.md`, is written from spin's `versions.yaml` at the release installations run (its spin-machine), images.json and the module's newest release. - **Diagrams** are the ones spin and spin-terraform-aws draw beside their sources (`.dot`, `.d2`): diff --git a/tools/refgen/figures.go b/tools/refgen/figures.go index 550377b..1332e05 100644 --- a/tools/refgen/figures.go +++ b/tools/refgen/figures.go @@ -17,9 +17,14 @@ import ( // // | | spin:# = // -// for a figure a constant of spin's decides, which refgen reads and compares - 15m, 12h, 300, 3 - -// or, in the third place, why nothing holds it: a default of a file refgen cannot evaluate, or -// words that are no figure of the code's. A page that showed a figure with has no line: +// for a figure a constant of spin's decides, which refgen reads and compares - 15m, 12h, 300, 3; +// +// | | spin: has "" +// +// for one decided where no constant is - a field's default, a timer's unit, a template's line, a +// Terraform local - held by the text that decides it still being in the file, of spin or of +// spin-terraform-aws, whatever its spacing; or, in the third place, why nothing holds it: another +// party's fact, or words that are no figure of the code's. A page that showed a figure with has no line: // the component is the code's own number. // // Before it a page said "an hour" of a credential the code had made fifteen minutes, and "about @@ -29,8 +34,12 @@ const figuresList = "tools/refgen/figures.txt" // saidFigure is a duration, a size or a count of tries, in digits or in words. var saidFigure = regexp.MustCompile(`(?i)\b(?:(?:\d+(?:[.,]\d+)?|an?|one|two|three|four|five|six|seven|eight|nine|ten|twelve|fourteen|fifteen|twenty|thirty|sixty|ninety|half an?|a quarter of an|a few|few)[ -](?:(?:second|minute|hour|day|week|month|year)s?|times|attempts)|\d+(?:\.\d+)? ?[KMGT]i?B)\b`) -// held is a figure's third place when a constant decides it. -var held = regexp.MustCompile(`^spin:(\S+)#(\w+) = (\S+)$`) +// held is a figure's third place when a constant decides it, and has when a file's text does. +var ( + held = regexp.MustCompile(`^spin:(\S+)#(\w+) = (\S+)$`) + has = regexp.MustCompile(`^([\w-]+):(\S+) has "(.+)"$`) + gaps = regexp.MustCompile(`\s+`) +) type figureLine struct { n int @@ -38,10 +47,11 @@ type figureLine struct { } // checkFigures holds every figure in the prose of a hand-written page to a line of list, every -// line to words its page still says, and every line that names a constant to that constant's -// value in the checkout spin. spin is "" in a run without it, which leaves the constants to the -// run that has it. -func checkFigures(docs, list, spin string) ([]string, error) { +// line to words its page still says, and every line that names a constant or a file's text to +// the checkout that has it. from is the checkouts at hand: a line of a repository that is not +// among them - spin's, in a run without spin - is left to the run that has it. +func checkFigures(docs, list string, from map[string]checkout) ([]string, error) { + spin := from["spin"].root raw, err := os.ReadFile(list) if err != nil { return nil, err @@ -89,6 +99,20 @@ func checkFigures(docs, list, spin string) ([]string, error) { bad = append(bad, fmt.Sprintf("%s:%d: %s does not say %q: take the line out", list, l.n, l.page, l.words)) continue } + if m := has.FindStringSubmatch(l.what); m != nil { + c, ok := from[m[1]] + if !ok { + continue + } + raw, err := os.ReadFile(filepath.Join(c.root, m[2])) + switch { + case err != nil: + bad = append(bad, fmt.Sprintf("%s:%d: %s says %q, and %s no longer has %s: correct the page and the line", list, l.n, l.page, l.words, m[1], m[2])) + case !strings.Contains(gaps.ReplaceAllString(string(raw), " "), gaps.ReplaceAllString(m[3], " ")): + bad = append(bad, fmt.Sprintf("%s:%d: %s says %q, and %s's %s no longer says %q: correct the page and the line", list, l.n, l.page, l.words, m[1], m[2], m[3])) + } + continue + } m := held.FindStringSubmatch(l.what) if m == nil || spin == "" { continue diff --git a/tools/refgen/figures.txt b/tools/refgen/figures.txt index e5a90b5..16d6788 100644 --- a/tools/refgen/figures.txt +++ b/tools/refgen/figures.txt @@ -4,26 +4,27 @@ # | | spin:# = # # where a constant of spin's decides the figure - task reference:check reads it and fails when it -# is another value, naming the page - or, in the third place, why nothing holds it. A figure a +# is another value, naming the page; or `: has ""`, where the text that decides +# it is held to still being in the file; or, in the third place, why nothing holds it. A figure a # page says that has no line fails, and so does a line whose words its page no longer says. A # "not held" line is a debt: the figure is true only while somebody remembers it. -admin/configuration.md | seven days | not held: struct field default SessionDurationHours: 168 in DefaultSecurity() of internal/controlplane/settings/security.go, overridden by the session_duration_hours setting +admin/configuration.md | seven days | spin:internal/controlplane/settings/security.go has "SessionDurationHours: 168," admin/hosts.mdx | 24 hours | spin:internal/controlplane/server/runnertokenserver/runnertoken.go#registrationTokenExpiry = 24h admin/hosts.mdx | 30 days | spin:internal/controlplane/server/runnertokenserver/runnertoken.go#maxRegistrationTokenExpiry = 720h -admin/observability.md | fifteen minutes | not held: systemd timer OnUnitActiveSec=15min in internal/installation/install/units/spin-vmbackup.timer -admin/observability.md | 30 days | not held: var DefaultRetention (PlatformLogs: 30) in internal/logstore/logstore.go, overridden by the logs_platform_retention_days setting -admin/observability.md | 7 days | not held: var DefaultRetention (WorkspaceLogs: 7) in internal/logstore/logstore.go, overridden by the logs_workspace_retention_days setting -admin/observability.md | 7 days | not held: var DefaultRetention (Traces: 7) in internal/logstore/logstore.go, overridden by the traces_retention_days setting +admin/observability.md | fifteen minutes | spin:internal/installation/install/units/spin-vmbackup.timer has "OnUnitActiveSec=15min" +admin/observability.md | 30 days | spin:internal/logstore/logstore.go has "PlatformLogs: 30," +admin/observability.md | 7 days | spin:internal/logstore/logstore.go has "WorkspaceLogs: 7," +admin/observability.md | 7 days | spin:internal/logstore/logstore.go has "Traces: 7}" admin/observability.md | 30 days | spin:internal/metricstore/metricstore.go#RetentionDays = 30 -admin/observability.md | thirty seconds | not held: poll_frequency = "30s" in the Alloy template internal/installation/boot/files/alloy.alloy.tmpl +admin/observability.md | thirty seconds | spin:internal/installation/boot/files/alloy.alloy.tmpl has "poll_frequency = "30s"" admin/recovery.md | five minutes | spin:internal/pgserver/server.go#ArchiveTimeoutSeconds = 300 admin/recovery.md | five minutes | spin:internal/pgserver/server.go#ArchiveTimeoutSeconds = 300 -admin/users.md | seven days | not held: struct field default SessionDurationHours: 168 in DefaultSecurity() of internal/controlplane/settings/security.go, overridden by the session_duration_hours setting +admin/users.md | seven days | spin:internal/controlplane/settings/security.go has "SessionDurationHours: 168," admin/users.md | a year | spin:internal/controlplane/server/authserver/apikeys.go#maxAPIKeyLifetime = 8784h aws/cost.mdx | Three months | not a figure: a heading naming the period the cost table prices aws/cost.mdx | a month | not a figure: the unit of S3's price per GB a month aws/cost.mdx | a month | not a figure: the unit of Route 53's price per hosted zone -aws/network.mdx | ten-second | not held: Terraform local internal_record_ttl = 10 in modules/controlplane/dns.tf of spin-terraform-aws +aws/network.mdx | ten-second | spin-terraform-aws:modules/controlplane/dns.tf has "internal_record_ttl = 10" aws/setup.md | a few minutes | not a figure: an estimate of how long Let's Encrypt issuance takes aws/setup.md | ten minutes | not a figure: an estimate ("About") of the first bring-up time aws/setup.md | a few minutes | not a figure: an estimate of how long a first runner takes to come up @@ -39,16 +40,16 @@ concepts/workspaces.md | 512 MB | spin:internal/controlplane/server/workspaceser concepts/workspaces.md | 10 GB | spin:internal/controlplane/server/workspaceserver/workspace.go#DefaultDiskGB = 10 concepts/workspaces.md | three times | spin:internal/lifecycle/budget.go#GenerationAttempts = 3 concepts/workspaces.md | Ten minutes | spin:internal/controlplane/server/workspaceserver/idle.go#WarnMinutes = 10 -overview/evaluate.md | 30 GB | not held: flag default -free-gb 30 in hack/lanes/cmd/lanes/ci.go (the CI run's preflight); nothing checks it for the development stack +overview/evaluate.md | 30 GB | spin:hack/lanes/cmd/lanes/ci.go has "fs.Int("free-gb", 30," overview/how-it-works.mdx | five minutes | spin:internal/pgserver/server.go#ArchiveTimeoutSeconds = 300 overview/how-it-works.mdx | a minute | spin:internal/controlplane/domain/reconcile/reconcile.go#Every = 1m overview/how-it-works.mdx | three times | spin:internal/lifecycle/budget.go#GenerationAttempts = 3 overview/how-it-works.mdx | five minutes | spin:internal/controlplane/settings/settings.go#DefaultVolumeRPOSeconds = 300 -overview/one-machine.md | 30 GB | not held: flag default -free-gb 30 in hack/lanes/cmd/lanes/ci.go (the CI run's preflight); nothing checks it for the development stack +overview/one-machine.md | 30 GB | spin:hack/lanes/cmd/lanes/ci.go has "fs.Int("free-gb", 30," quickstart.md | few minutes | not a figure: an estimate of how long a runner takes to come up security/data.md | 64 KiB | spin:internal/storage/crypto/layer.go#LayerFrameBytes = 65536 security/data.md | fifteen minutes | spin:internal/controlplane/server/volumeserver/storagecreds.go#StorageCredentialTTL = 15m -security/identity.md | seven days | not held: struct field default SessionDurationHours: 168 in DefaultSecurity() of internal/controlplane/settings/security.go, overridden by the session_duration_hours setting +security/identity.md | seven days | spin:internal/controlplane/settings/security.go has "SessionDurationHours: 168," security/identity.md | a year | spin:internal/controlplane/server/authserver/apikeys.go#maxAPIKeyLifetime = 8784h security/identity.md | fifteen minutes | spin:internal/installation/provider/aws/stsjoin/stsjoin.go#maxExpiry = 15m security/identity.md | an hour | spin:internal/controlplane/server/runnertokenserver/runnertoken.go#runnerTokenExpiry = 1h diff --git a/tools/refgen/figures_test.go b/tools/refgen/figures_test.go index 8a391c1..e954339 100644 --- a/tools/refgen/figures_test.go +++ b/tools/refgen/figures_test.go @@ -19,6 +19,9 @@ func TestCheckFigures(t *testing.T) { "listed with why nothing holds it": {"About 220 MB.", "# sizes\np.md | 220 MB | not held: the size of Microsoft's server\n", ttl, ""}, "a link's target is not the page's": {"[It stops]() and [reads](/a/30-days/).", "", ttl, ""}, "a flag's own value is the CLI's": {"Pass `--expires 30 days`.\n\n```bash\nsleep 5 minutes\n```", "", ttl, ""}, + "held by a file's text": {"Lasts seven days.", "p.md | seven days | spin:p/p.go has \"Tries = 3\"\n", ttl, ""}, + "the text is gone": {"Lasts seven days.", "p.md | seven days | spin:p/p.go has \"Tries = 4\"\n", ttl, `no longer says "Tries = 4"`}, + "the text's file is gone": {"Lasts seven days.", "p.md | seven days | spin:q/q.go has \"Tries = 3\"\n", ttl, "spin no longer has q/q.go"}, "a figure with no line": {"Lasts an hour.", "", ttl, `says "an hour", which nothing holds`}, "the constant moved": {"Lasts fifteen minutes.", "p.md | fifteen minutes | spin:p/p.go#TTL = 15m\n", strings.Replace(ttl, "15", "10", 1), "TTL is now 10m0s, not 15m"}, "the count moved": {"Tried three times.", "p.md | three times | spin:p/p.go#Tries = 3\n", strings.Replace(ttl, "= 3", "= 5", 1), "Tries is now 5, not 3"}, @@ -42,7 +45,7 @@ func TestCheckFigures(t *testing.T) { if err := os.WriteFile(list, []byte(c.list), 0o644); err != nil { t.Fatal(err) } - bad, err := checkFigures(docs, list, spin) + bad, err := checkFigures(docs, list, map[string]checkout{"spin": {root: spin}}) if err != nil { t.Fatal(err) } @@ -64,7 +67,7 @@ func TestCheckFiguresWithoutSpin(t *testing.T) { if err := os.WriteFile(list, []byte("p.md | fifteen minutes | spin:gone.go#TTL = 15m\n"), 0o644); err != nil { t.Fatal(err) } - if bad, err := checkFigures(docs, list, ""); err != nil || len(bad) > 0 { + if bad, err := checkFigures(docs, list, nil); err != nil || len(bad) > 0 { t.Errorf("checkFigures without spin: %v, %v", bad, err) } } diff --git a/tools/refgen/main.go b/tools/refgen/main.go index 7044050..ad67cc0 100644 --- a/tools/refgen/main.go +++ b/tools/refgen/main.go @@ -174,7 +174,7 @@ func run() error { func() ([]string, error) { return checkDomains(*docs, *tf) }, func() ([]string, error) { return checkTofu(*docs, *tf) }, func() ([]string, error) { return checkReads(*docs, from) }, - func() ([]string, error) { return checkFigures(*docs, figuresList, *spin) }, + func() ([]string, error) { return checkFigures(*docs, figuresList, from) }, } { b, err := c() if err != nil {