Add a constitution — composed as one set
One constitution stands across several repositories. One repository authors the norms (the canon), and every repository that receives them carries a declaration (a link). Below, ① name the constitution, ② read the canon repository and pick its files and scope, ③ add the linked repositories, and the declarations (graduand.json) for every repository that answers to this constitution are composed at once.
⛔ This page neither writes nor commits. It hands you a string, and writing the file belongs to the command that runs on your own machine. The composer is one and the same, so both entrances produce identical bytes.
③ The link declarations — the canon is another repository
Each one points at the canon and carries the pinned values read from that repository. ⛔ Nothing is copied by hand: copying the same value into N repositories by hand is the production line of that drift, and this product measures it. ⭐ A repository that already has a declaration gets its whole declaration plus one axis. ⭐ The canon declaration need not be committed yet — the pins are read against the canon declaration composed just above. ⚠️ Pins can only be written by reading the canon, though: if the canon is private this page cannot produce link declarations, and that work belongs to graduand link in the terminal tab — on your machine both checkouts are in hand.
No linked repository yet. Composing now yields the canon declaration alone — which is a usable result too.
Commit each snippet as graduand.json at that repository root, and the diagnosis at the root reads the repository and reports. If the declaration points at a canon, it reads that too and judges "pins == canon".
Compose the same thing in the terminal
With the checkouts in hand, the command writes the declaration itself. The composer is the same single function this page uses, so both entrances produce identical bytes. ⛔ It does not commit: writing a file and putting it in the history are different acts, and the second one is yours.
The values below are an example — constitution design, canon repository acme/design, file tokens.css, scope :root. Swap in your own names and addresses.
-
In the canon checkout — write "this is the canon"
npx graduand canon --axis design --adapter css-custom-properties --file tokens.css --scope :root--filerepeats (several files). The composer does not believe the declaration, it reads: if a declared file does not exist, or that scope reads no value at all, it refuses instead of writing. ⛔ It never overwrites an existing declaration — it prints the composed document on stdout and moving it is yours. -
In the linked checkout — write "the canon is over there and the values are these"
npx graduand link --axis design --canon acme/design --canon-file tokens.css --canon-scope :root --root . --root ../designSeveral
--rootflags are the heart of this step. The first is the repository being written and the rest are sight — the pins are read from the canon checkout, never taken by hand. ⛔ If the canon is not in sight it refuses rather than writing an empty pin: a pin read from nowhere is an invented basis. -
Ask what stands as the denominator
npx graduand rulesIt prints what the declaration put up as the denominator — admitted rules and the verifiers they chose, the canon address of each shared-value axis, exemptions and their expiry. ⛔ It does not judge: there is no red here, because asking for the denominator violates nothing.
-
Judge
npx graduand --root . --root ../designThe answer is pass · violation · unjudged, and nothing else. A cell nobody looked at is not counted as a pass. To measure the cross axis ("pins == canon") here too, show the canon checkout with
--rootas well — sight of a single repository cannot, in principle, see drift between repositories.