Skip to content

Refuse a colour typed into what the build reads [#14] - #146

Merged
iderex merged 5 commits into
mainfrom
tokens-live-in-one-file
Aug 11, 2026
Merged

Refuse a colour typed into what the build reads [#14]#146
iderex merged 5 commits into
mainfrom
tokens-live-in-one-file

Conversation

@iderex

@iderex iderex commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What was wrong

The invariant table carried this row as owed rather than decided, because what
it would read was not in the tree:

go run . invariants
  design-tokens-live-in-exactly-one-file: not decided, waiting on the token file, in #65 and #66. There is no token in this tree, so the rule would compare one absent file against another

That was the last of the three rows the table has been printing as owed since
the gate landed. The pinned copy landed on 7b4df72, so there is now a file for
the row to be about.

What this does

Adds the row. It refuses a colour written into what the build reads, in any of
the hex forms the published file uses.

The design system is published as data and this repository vendors a copy of it,
which is decisions/0007-where-the-design-tokens-live.md: the page and the
stylesheet are generated from that copy and neither is typed. A hex value typed
into a template is the second definition that record refuses, and it is the one
mistake somebody actually makes, because the value is on the screen in front of
them while they are writing the markup that needs it. The day the published
value moves, the page goes on rendering the old one perfectly.

It reads the shape rather than the published values. A rule comparing against
the copy would refuse the values that are in it today and pass the ones somebody
typed that are not, which is the wrong half: what is wrong with a typed colour
is that it was typed, whether or not it is currently right.

The population is a new one, the files the build reads to render a page. A value
in a document is prose, a value in a test is what a case needs in order to fail,
and a value in a golden file is a copy of output rather than a source of it.
Only what the build reads can put a wrong value on a page, and drawing the
population wider would refuse the golden files #33 lands and the fixtures this
suite already carries.

Exactly one file means one and not zero. A tree carrying no copy is refused by
name rather than passing as a row that found no second definition of a value
that has no first one.

The near miss is the address that is the same bytes as a colour. A fragment
reference is dropped before the match, so href="#a1b2c3" is an address and
#a1b2c3 in a style attribute is a refusal. The shorthand forms are in the set
because they spell published values exactly: #FFFFFF is a published value and
#fff is the same colour.

Closes

Closes #14.

Its done-when asks for a check named exactly Enforce greppable invariants and,
for each invariant in the table this issue lists, a recorded run where a one-line
violation reds that check and the output names which invariant refused. The seven
this issue lists are eight rows, and each one now has such a run. The query, per
commit:

gh api repos/Flowfin/site/commits/COMMIT/check-runs \
  --jq '[.check_runs[]|select(.name=="Enforce greppable invariants")|.conclusion]|join(",")'

ccb2d5e1  failure   the language row, on #116
f5dc82f0  failure   the title, script, unfinished-marker and tool-marker rows, on #116
a162b9f0  failure   the allowlist row, on #124
b6ab6dc1  failure   the colour row, on this branch
5a4c6868  failure   the affiliation row, on this branch

All run 2026-08-11. Rows the table has gained since from other issues are not
counted here and carry their proof where they were argued.

What was run

The gate at the commit being pushed:

go run . ci
gate: 6 legs, in order: format, vet, test, build, links, invariants
  needs-network was not asked for. Asking costs a request to the public name from whatever machine runs it, and a verdict that moves when somebody else's service does rather than when this tree changes. Ask with: go run ./harness/needs-network
  format: ok, 27 file(s)
  vet: ok
  test: ok, 13 test file(s)
  build: ok, 1 file(s)
  links: every reference that stays inside this site resolves to a file the build wrote
  invariants: ok, 16 rule(s) decided, 1 owed and not decided
6 of 6 legs ran. None was skipped.

Sixteen decided and one owed, against fifteen and two before. The one still owed
is image-dimensions-match-the-file, which waits on the first image the build
writes and belongs to #69.

git diff --name-only origin/main...HEAD
internal/invariant/invariant.go
internal/invariant/invariant_test.go

The two recorded runs this issue asks for

A colour typed into the template, pushed on b6ab6dc and taken back out on
b99574e. Enforce greppable invariants on that head, conclusion failure,
at https://github.com/Flowfin/site/actions/runs/31538415894/job/93934983785 :

design-tokens-live-in-exactly-one-file: REFUSED, 1 violation(s)
  it refuses a colour written into what the build reads, in any of the hex forms the published file uses, outside a fragment reference
  because a colour typed into a template is a second definition of a value published somewhere else, and the day the published one moves the page goes on rendering the old one perfectly, so nobody sees it
  templates/page.html.tmpl: line 2 writes the colour #121216, and data/design-tokens.json is the one file a colour is read from
invariants: 1 rule(s) refused this tree

The affiliation row landed with the notice itself and its refusal was recorded
from a local run of the verb rather than from the check. That run is taken here,
pushed on 5a4c686 and taken back out on 94e1b7a, so every row this issue
lists has one of the same kind. Conclusion failure, at
https://github.com/Flowfin/site/actions/runs/31538838949/job/93936334655 :

page-carries-the-affiliation-notice: REFUSED, 1 violation(s)
  it refuses a produced page that does not carry the affiliation notice
  dist/index.html: this page carries no affiliation notice, and the sentence it has to carry is Flowfin is not affiliated with the Jellyfin project.
invariants: 1 rule(s) refused this tree

Both violations are reverted and neither is in what this branch adds, which the
git diff --name-only above is the whole of.

The guard, deleted and watched go red

Both halves of the row, one at a time:

the decision made to report nothing
  --- FAIL: TestEveryRowRefusesItsOwnViolationAndPassesTheNeighbour
      row design-tokens-live-in-exactly-one-file passed its own violation
  --- FAIL: TestTheColourRowRefusesATypedColourAndLeavesAFragmentAlone
  --- FAIL: TestRunRefusesAColourTypedIntoTheTemplate

the refusal of a tree carrying no copy removed
  --- FAIL: TestRunRefusesATreeCarryingNoCopyOfTheDesignTokens

The suite carries the pair the table already requires: the row against the
one-line mistake, and the row against a page of its own population that breaks
nothing. Beside that it carries the four spellings of a colour it refuses and
the four shapes it leaves alone, because what this row has to get right is which
bytes are a colour and which are an address that looks like one.

No test was skipped. Nothing here needs a display, a browser or elevation.

The means

A row in the existing invariant table, in Go, decided by the same verb as every
other row and reported under the same check name. That is what this repository
already holds for a rule read off the tree, and it is why the rule can be proved
to bite by a suite and run on the machine where the mistake was made. A grep in
a workflow step, which is what this issue originally described, has neither, and
the argument for the departure is in the body of #116 rather than repeated here.

Who read it

Nobody else has read this. The ruleset requires no approving review, so this is
merged by whoever opened it, and the evidence above stands in place of a second
reader: the row was made to refuse a real template through the real check,
twice, and both outputs are quoted rather than described.

iderex added 5 commits August 11, 2026 23:31
The invariant table carried this row as owed, waiting on a token file that was
not in this tree. The pinned copy landed, so the row lands with it, and it is
the last of the three the table was printing as not decided when the gate was
first added.

What it refuses is a colour written into what the build reads. The design system
is published as data and this repository vendors a copy, so the page and the
stylesheet are generated from that copy and neither is typed. A hex value typed
into a template is a second definition of a value published somewhere else, and
the day the published one moves the page goes on rendering the old one
perfectly, so nobody sees it.

It reads the shape and not the published values. A rule comparing against the
copy would refuse the values in it today and pass the ones somebody typed that
are not, which is the wrong half: what is wrong with a typed colour is that it
was typed, whether or not it is currently right.

The population is the files the build reads rather than every tracked file. A
value in a document is prose, a value in a test is what a case needs in order to
fail, and a value in a golden file is a copy of output rather than a source of
it. Only what the build reads can put a wrong value on a page.

Exactly one file means one and not zero, so a tree carrying no copy is refused
by name rather than passing as a row that found no second definition of a value
with no first one.

The near miss is the address that is the same bytes as a colour. A fragment
reference is dropped before the match, so href="#a1b2c3" is an address and

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
#a1b2c3 in a style attribute is a refusal.
The row's own done-when asks for a recorded run where a one-line violation reds
the check the invariant gate reports under. This is that line, and the commit
after it takes it back out. It is the colour a template would carry if somebody
wrote the value instead of rendering it.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
The run it was pushed for is recorded in the body of this pull request. What
lands is the row, and the template is the one line it was before.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
The row landed with the notice itself and its refusal was recorded from a local
run of the verb rather than from the check this issue is named after. This takes
that run, and the commit after it puts the sentence back. It is the sentence
being dropped while the footer stays, which is what an edit to the wording
actually looks like.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
The run it was taken for is recorded in the body of this pull request. The
template is the one line it was before.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit e74c846 into main Aug 11, 2026
17 checks passed
@iderex
iderex deleted the tokens-live-in-one-file branch August 11, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the greppable invariant gate under the check name Enforce greppable invariants

1 participant