The gold-spec methodology — a process flow whose every step is a user story, carrying the information it moves, the requirements that hold it and the questions still open, and the gates that keep all of it honest.
A chapter is a process flow, and a corpus is one main flow with one tree under it. A step that
is itself a process drills into its own chapter, named for that step — chapter-1.3-charging.md
expands step ch1-3 of chapter-1-ordering.md — so the file names alone show where every chapter
sits. Not a set of top-level flows side by side: name the one the corpus starts from with
main_flow, and the gate fails any flow that is not reached from it; leave it unnamed, and the gate
warns when more than one flow is entered from nothing, and suggests which is the main one.
One main flow says when to draw a drill-down and when to
link another process instead.
New here? Getting started walks one complete chapter part by part and gets the gate green; Cite the spec from a test shows the mark a test carries to say which story it proves. All the documentation. goldspec runs on Python 3.9 or later.
Already have a document? Bringing in a document you already have says where each part of it goes — steps, requirements, open questions, measured numbers — and what is dropped.
Already using it? CHANGELOG.md says what each release changed and whether taking it asks anything of you.
plumb observes a run and derives a board. It works for any
project with any workflow, and requires no spec. Nothing under its src/ may import this
package, and a project adopts a methodology by installing one — never by being handed one with its
tooling.
The line is about delivery. The two specs may cite one another; nothing plumb ships may import this package. The import half is enforced by a test in each repository that reads the other's shipped modules as source data.
That separation is not a preference. Everything here presumes conventions: how a subject is declared, where flows live, what a chapter looks like. To check that every story is described and placed on a flow, a tool must decide what a story is and where it lives — which is mandating a workflow. Opted into, the identical checks are simply the method a project chose.
A declared set handed over as data is a second home for subjects the spec already owns, and it drifts from the moment either moves.
goldspec reads a project's spec and the test sources that cite it, and imports nothing from
plumb — a test in this repository holds it to that. Nothing under
plumb's src/ may import goldspec, and that is enforced rather than promised —
test_nothing_shipped_imports_the_methodology in plumb's own suite reads its source as data and
fails on a single import.
The gates are tests. A project runs them in its own suite, so plumb observes a test that ran
and passed, exactly as it observes any other, and never learns the test was a methodology gate. No
new interface was needed for any of this.
Two artifacts ship from the same release, listed in one SHA256SUMS and covered by one signature:
| what it is for | |
|---|---|
goldspec.pyz |
the command. Checking a spec by hand, or before anything is wired up. Install it by following the instructions on the site, which check its checksum and signature; it needs no project of yours to be a Python one |
goldspec-<version>-py3-none-any.whl |
the library. pip install it and import check_spec_directory into your own test — which is what the paragraph above actually describes |
If you are reading a spec, take the command. If you are holding a spec to its gates on every run, take the library.
goldspec [SPEC_DIR] [--sibling DIR]…
SPEC_DIR defaults to spec. --sibling is the command-line form of the siblings key below,
repeatable, and adds to any the file names.
Adopting goldspec means adopting the shape — a subject is declared once, where it is written, and
placed in a flow — and never our particular strings. Ids namespaced REQ or sh, flows in a directory called something
else, chapters named to suit you: all of it is yours, and the gates check it just the same.
Say so in a goldspec.toml, inside your spec directory or beside it — those two places and no
other, so a file that is being ignored is a file in a place you can see is wrong:
[conventions]
namespace = "sh" # the prefix every subject id carries
flow_dir = "flows" # where the process flows live, relative to the spec
flow_glob = "story-*.md" # how a flow file is named
siblings = ["../other/spec"] # more of this corpus, read for what it defines, never checked
main_flow = "story-1-orders.md" # the one flow the corpus starts from; every flow must be reached from itThose five keys are the whole set — shown here with values that differ from the defaults
(ch, diagrams, chapter-*.md, no siblings, no main flow), because a file states only what
differs. A key this version does not read stops the run rather than being ignored. Once
main_flow is named, every other flow is a drill-down named for the step it is entered from, and a
flow neither drawn from the main flow nor linked from a flow that is reached fails the gate.
A chapter is held to the shape it is written in. Written as a flow, its information lives with
the node that moves it — a ## Nodes section where each node carries its story and states what it
takes and leaves, each row saying where it came from. Written in the older shape, with one
## Input → process → output and one ## Data definitions per chapter, it is counted and not
held to the node format's checks. So a corpus converts a chapter at a time and an unconverted
chapter is not failed for the shape it is in; there is no flag to set, and none to remember to
unset.
Two limits on that, because they are real and a reader should meet them here rather than in a run. An unconverted chapter declares no subjects, so a test citing one is reported as citing something nobody wrote down, and a spec in which nothing is converted fails — the gate finds no subjects at all, which it refuses rather than calls a pass. Convert the chapters your tests cite first and the rest follow at their own pace.
Every key is optional and the defaults are our habits, so a project that took them writes no file
at all. The command reads it and so does the library — conventions_for(spec) is what both
call — because a project that states its conventions twice has two gates over one spec that cannot
see each other, which is the defect this whole method exists to remove.
Two locations, in that order, and deliberately not a search up to the filesystem root: an upward search finds a file you did not write, in a directory you may not own, and reports its conventions as yours. Put the file inside the spec directory if one repository holds more than one spec — a file beside it is found for every spec directory next to it, which is right for a project with one spec and wrong for a project with two.
A file anywhere else is not read and nothing says so, because nothing can: a directory with no
goldspec.toml in it and a project that took the defaults are the same thing. The run's first line
is what tells you which happened.
Every run says which conventions it judged by, and where they came from:
goldspec: conventions from spec/goldspec.toml
goldspec: conventions: ids namespaced 'sh', flows in flows/story-*.md, main flow story-1-orders.md
The second line ends with the main flow only where main_flow is named. Both are worth reading
when a fault surprises you: every message this gate emits is phrased as a verdict on your spec, and
the one cause it cannot phrase is having been handed the wrong strings.