quality

package
v0.0.0-...-32478e7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Overview

Package quality is the corpus audit: every quality claim this project makes, written down as a rule that runs.

The rules are the ones in spec 08 §2, and they keep the numbering they have there, because a CI log that says S07 has to mean the same thing as the checklist that says S07. Each is a named function of the committed Markdown and the manifests, and of nothing else. Nothing here opens a PDF, calls a model or reaches the network, which is what lets the whole audit run in CI, where the PDFs are not and cannot be, and what makes two runs of it on the same commit give the same answer.

A hard rule failing means the corpus is wrong. A soft rule failing means somebody should look. The exit status is the hard rules alone, so the soft ones can be honest about how much they do not know without turning the build red for it.

The audit is expected to be red for a while. taocp's README says its complete audit "intentionally exits nonzero until all reported hard failures are repaired", and the same is true here: a rule written so that today's corpus passes it is a rule that measures nothing.

Index

Constants

View Source
const (
	CoverageBegin = "<!-- BEGIN COVERAGE -->"
	CoverageEnd   = "<!-- END COVERAGE -->"
)

The markers the generated block sits between. They are HTML comments so that they are invisible in every renderer and visible in the source.

View Source
const (
	KindSection  = "section"
	KindExercise = "exercise"
	KindSolution = "solution"
)

The three shapes of content file.

View Source
const (
	Structure   = "structure"
	Tags        = "tags"
	Mathematics = "mathematics"
	Figures     = "figures"
	References  = "references"
	Translation = "translation"
	Solutions   = "solutions"
	Publication = "publication"
	Hygiene     = "hygiene"
)

The groups, in the order the report prints them.

Variables

This section is empty.

Functions

func BookOf

func BookOf(d Doc) string

BookOf is the volume a content file belongs to, by the short book id, which is what a book scoped glossary row is matched against. A solution answers "" and that is right: nothing translates a solution against the glossary yet, and its front matter names the exercise rather than the volume.

Exported for the same reason Prose is. The adherence report asks L06's question a second time, and if it scoped the glossary differently the report and the rule would disagree about the same file.

func Coverage

func Coverage(c *Corpus) string

Coverage renders the block. It is a pure function of the manifests and the committed Markdown, like everything else here, so the README it writes can be checked by regenerating it.

func Failures

func Failures(r *Result) []string

Failures is the one-line summary the command prints last, listing the hard rules that failed and nothing else, because that is the list somebody has to work through before the build goes green.

func FreeGatewayModel

func FreeGatewayModel(name string) bool

FreeGatewayModel says whether a model name is one the free gateway serves.

A file translated on two routes records both names, so the test is per name rather than on the whole string: one gateway answer anywhere in a file is a file worth asking for again, which is how L08 reads a cut down model too.

func GroupOrder

func GroupOrder() []string

GroupOrder is the groups in the order spec 08 writes them, which is the order anything printing the rules should use.

func JSON

func JSON(r *Result, version, commit string) ([]byte, error)

JSON renders the machine form. version and commit are the run's, and are passed in rather than looked up here, because this package does not shell out.

func Math

func Math(body string) (spans []Span, unclosed *Span)

Math splits a normalised body into its math spans. See mathtex.Split.

func Prose

func Prose(body string) string

Prose is a body with the mathematics and the heading attributes taken out, which is the only text a glossary term can honestly be looked for in. It is exactly what L06 reads, exported so that the report and the rule cannot drift apart on what counts as a mention.

func Report

func Report(r *Result) string

Report renders reports/audit.md.

func SmallModel

func SmallModel(name string) bool

SmallModel is the same test, for the run rather than for the audit.

L08 finds a cut down model after the file is written, which for a section of fifteen chunks is eleven minutes too late and for a chapter of twenty six sections is a night of it. The run says so on the first chunk that comes back that way, and it is the same rule saying it, because two answers to "is this a small model" is one answer too many.

func Text

func Text(r *Result, verbose bool) string

Text is the terminal form: one line per finding, in the order the rules ran, which is the order somebody fixing them wants.

func WriteCoverage

func WriteCoverage(root string, block string) (bool, error)

WriteCoverage puts the block back into the README between its markers, and reports whether anything changed.

Types

type Check

type Check struct {
	ID    string
	Group string
	Hard  bool
	Title string
	Run   func(*Corpus) ([]Finding, error)

	// Need says why this rule cannot run against this corpus, and is empty
	// when it can. A shallow checkout has no history for T05 and a corpus with
	// no Vietnamese has nothing for L01 to compare; both are reported as not
	// run, because a rule that passes by having nothing to look at is the one
	// kind of green nobody should trust.
	Need func(*Corpus) string
}

A Check is one rule.

Run returns what it found. It returns an error only when it could not look: a manifest that will not parse is an error, a manifest that says something wrong is a finding. Keeping those apart is what lets one broken rule fail loudly without the other forty going quiet.

func Checks

func Checks() []Check

Checks is every rule the audit knows, for the command that lists them.

type Corpus

type Corpus struct {
	Opt  Options
	Root string

	Books     *corpus.BooksManifest
	TOC       *corpus.TOCManifest
	Sections  *corpus.SectionsManifest
	Exercises *corpus.ExercisesManifest
	Editions  *corpus.EditionsManifest

	Tags  *tags.Set
	Items map[string][]tags.Item // per language, in the order tags are handed out

	Docs      []Doc                        // every content file of every language
	Pages     map[string][]corpus.PageFile // per book, in page order
	PagePaths map[string][]string          // the paths of those pages, same order

	// Sources are the files of a source language the caller did not ask to
	// audit, read only so that a translation can be compared with the thing it
	// was translated from. They are not audited and no rule walks them.
	//
	// bourbaki audit -lang vi means audit the Vietnamese, and Docs holds what
	// that run is about. But every translation rule reads a file and the English
	// beside it, and a run that had loaded no English reported all forty nine
	// files as translations whose source does not exist, once per rule, four
	// hundred and forty one findings for a corpus that had nothing wrong with
	// it. The English has to be in hand and out of scope at the same time, so it
	// is read into its own list.
	Sources []Doc

	Maps map[string]*pagemap.Map // per book, as committed

	Refs *refs.Result // the reference graph over English

	// Langs are the languages found under content/, English first. Solutions
	// live under content/solutions and are not a language.
	Langs []string

	// Tracked is git ls-files, and TrackedErr why it is not available. The
	// hygiene rules are about what is committed rather than about what is in
	// the directory, so outside a git checkout they do not run.
	Tracked    []string
	TrackedErr error

	// TagsDiff is what this checkout did to tags/tags since Base.
	TagsDiff    string
	TagsDiffErr error

	// Figures are the files under figures/, relative to the root.
	Figures []string
	// contains filtered or unexported fields
}

A Corpus is everything the rules read, loaded once.

func Load

func Load(opt Options) (*Corpus, error)

Load reads the corpus. It reads every file the rules need exactly once, because forty rules each walking content/ is forty times the io for the same bytes, and because two rules that disagree about what a file says are worse than either being wrong.

func (*Corpus) InLang

func (c *Corpus) InLang(lang string) []Doc

InLang is the docs of one language.

func (*Corpus) OfKind

func (c *Corpus) OfKind(kind string) []Doc

Sections, Exercises and Solutions are the docs of one shape.

func (*Corpus) Pairs

func (c *Corpus) Pairs() []Pair

Pairs is every translated file that names an English source, with the ones that name none or name a file that is not there left out. L01 is what reports those, so a report that also reported them would say the same thing twice.

func (*Corpus) SourceLangs

func (c *Corpus) SourceLangs() map[string]bool

SourceLangs are the languages the library is printed in, which is English plus every language a registered volume carries, so in practice en and fr.

It is what separates an extraction from a translation. content/fr is read off the French volume the same way content/en is read off the English one, so it names no translated_from and there is no English file to compare it against. Without this the translation rules would take all of the French for a translation with its source missing and say so once per file.

func (*Corpus) Translations

func (c *Corpus) Translations() []string

Translations are the languages other than English.

type Doc

type Doc struct {
	Path string // relative to the corpus root, forward slashes
	Lang string
	Kind string // section, exercise or solution
	Body string
	Raw  []byte

	Section  *corpus.SectionFrontMatter
	Exercise *corpus.ExerciseFrontMatter
	Solution *corpus.SolutionFrontMatter

	// Err is front matter that would not parse. The file is kept in the list
	// with its raw bytes, because the rules that read bytes rather than fields
	// can still say something useful about it, and S02 is what reports the Err.
	Err error
	// contains filtered or unexported fields
}

A Doc is one committed content file, read once and shared by every rule.

Body is normalised, so a rule that counts or hashes it agrees with what assembly wrote. BodyLine turns a line of that body back into a line of the file, which is the number the reader needs and the number a CI annotation has to carry.

func (Doc) BodyLine

func (d Doc) BodyLine(n int) int

BodyLine is the file line of line n of the body, counting both from one.

type Finding

type Finding struct {
	Check string `json:"check"`
	Group string `json:"group"`
	Hard  bool   `json:"hard"`
	File  string `json:"file,omitempty"` // relative to the corpus root
	Line  int    `json:"line,omitempty"`
	Msg   string `json:"message"`
}

A Finding is one thing wrong, at a place somebody can open.

File and Line are what make this worth having over a count. An audit that says seven files have stranded operators sends the reader back to grep; one that says which line does not.

func (Finding) At

func (f Finding) At() string

At is the finding's place, in the form an editor and a CI annotation both understand.

func (Finding) String

func (f Finding) String() string

type GroupSummary

type GroupSummary struct {
	Group   string
	Hard    int
	Soft    int
	NotRun  int
	Failing []string
}

GroupSummary is one row of the summary table.

type JSONCheck

type JSONCheck struct {
	ID      string `json:"id"`
	Group   string `json:"group"`
	Hard    bool   `json:"hard"`
	Title   string `json:"title"`
	Skipped string `json:"skipped,omitempty"`
	Found   int    `json:"found"`
}

JSONCheck is one rule's outcome without its findings, so that a consumer can tell a rule that passed from a rule that never ran. Those two are the same empty list and the difference is the whole point.

type JSONReport

type JSONReport struct {
	Version  string         `json:"version,omitempty"`
	Commit   string         `json:"commit,omitempty"`
	Root     string         `json:"root"`
	Langs    []string       `json:"langs"`
	Hard     int            `json:"hard"`
	Soft     int            `json:"soft"`
	Checks   []JSONCheck    `json:"checks"`
	Findings []Finding      `json:"findings"`
	Counts   map[string]int `json:"counts"`
}

JSONReport is the machine form, for the CI annotator and for anything that wants to track the numbers over time.

This is where the run's own metadata belongs. Version and Commit change with every run and every release, which is exactly why they cannot be in the Markdown, and exactly why something reading a series of these runs wants them.

type Options

type Options struct {
	Root string

	// Langs are the languages to audit. Empty means every language the corpus
	// has, which is what a run with no flags should do.
	Langs []string

	Only []string
	Skip []string

	// ValidateTeX turns on M04. It is off by default because it is the one
	// rule here that is slow, and the only one whose answer depends on which
	// TeX is installed on the machine running it.
	ValidateTeX bool

	// Base is the commit T05 reads the history of tags/tags against.
	Base string

	// Assembled is what bourbaki assemble would write, keyed by absolute path,
	// with Stale the committed files no page produced. S09 is the comparison
	// of that against what is on disk.
	//
	// It is passed in rather than computed here because the assembler's driver
	// lives with the command, and because a caller that has just assembled
	// should not pay for it twice. AssembleErr says why it is absent, so S09
	// reports not run rather than passing on an empty map.
	Assembled   map[string][]byte
	Stale       []string
	AssembleErr string
}

Options is what the command passes in.

func (Options) Selected

func (o Options) Selected(id string) bool

Selected answers whether -only and -skip leave this rule in. It is for a caller that has to do expensive work for one rule and should not do it when the rule is not going to run: the audit runs the whole assembler for S09, and running it for bourbaki audit -only hygiene would be a minute of nothing.

type Outcome

type Outcome struct {
	Check    Check
	Findings []Finding
	Skipped  string
}

An Outcome is one rule's run.

Skipped carries why a rule did not run, and it is printed rather than swallowed. A shallow CI checkout cannot read the history of tags/tags and a corpus with no Vietnamese cannot check a Vietnamese translation; both are fine, and an audit that reported them as passes would be lying about its own coverage.

type Pair

type Pair struct {
	Translation Doc
	English     Doc
}

A Pair is what pair is, for a caller outside the rules.

The rules are not the only thing that has to walk the translations against their sources: report translation counts what is translated and how closely the glossary is followed, and it has to pair the files the same way the rules do or it will report on a set the rules never looked at.

type Result

type Result struct {
	Outcomes []Outcome
	Corpus   *Corpus
}

A Result is the whole run.

func Run

func Run(opt Options) (*Result, error)

Run loads the corpus once and puts every selected rule over it.

func (*Result) Failing

func (r *Result) Failing() []Outcome

Failing is the rules with findings, and Skipped the rules that could not run.

func (*Result) Findings

func (r *Result) Findings() []Finding

Findings is every finding of the run, hard and soft together.

func (*Result) Groups

func (r *Result) Groups() []GroupSummary

Groups counts what ran, per group, for the summary table.

func (*Result) Hard

func (r *Result) Hard() int

Hard is how many findings came from a rule the corpus is held to. It is the exit status: zero means the corpus is as good as this audit can tell, and anything else means it is not.

func (*Result) Skipped

func (r *Result) Skipped() []Outcome

func (*Result) Soft

func (r *Result) Soft() int

Soft is how many findings came from a rule that only asks for a look.

type Span

type Span = mathtex.Span

A Span is one stretch of mathematics in a body, without its delimiters. The splitter is in package mathtex, under both this package and extract, because the tool that writes the pages and the audit that reads them back have to agree about where the mathematics is.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL