diff --git a/decisions/conformance-declaration.md b/decisions/conformance-declaration.md index 3cd8394..15ccfac 100644 --- a/decisions/conformance-declaration.md +++ b/decisions/conformance-declaration.md @@ -20,7 +20,7 @@ it, in the shortest form a claim can take. A report carries the values, the measurements, the method each one was produced with and the hardware it ran on, so a reader who doubts it can run it again and find out. -The report lives in the client's repository rather than here. It describes one +The report lives in the client's repository. It describes one build of one client at one moment, so a copy kept in this tree is stale the next time that client releases, and a stale copy of somebody else's evidence is worse than no copy. This repository holds the system. Each client holds its own @@ -79,8 +79,7 @@ A missed budget number is already answered, in may be changed in the open for everybody but not waived for one release. Nothing here reopens that. -Two things it does not answer, which belong to the report rather than to the -budget. +Two things it does not answer. Both belong to the report. A value that does not match blocks nothing on its own. A corner radius two pixels off harms no one, and a client that cannot publish until every value matches is a @@ -112,8 +111,8 @@ files can. Every check this repository owns reads this repository. A client's report lives in a tree this repository does not hold and does not gate, so no leg of this gate can refuse a badge, a missing entry or a summary verdict that collapses the -three states. What is available is narrower and worth naming rather than -implying: this repository can hold its own published pages to the wording in the +three states. What is available is narrower, and it is named here so nobody has +to infer it: this repository can hold its own published pages to the wording in the section above, and it can publish the values and the method in forms a client's own gate can read, which is #39 and #41. diff --git a/decisions/design-system-binding.md b/decisions/design-system-binding.md index 3834282..1b8af19 100644 --- a/decisions/design-system-binding.md +++ b/decisions/design-system-binding.md @@ -126,7 +126,7 @@ is not comparable between two clients and this rule cannot be applied fairly. A number may be changed, and it may not be waived. Changing it is a change to this system, argued in the open, applying to every client from then on. Waiving it for one release is how a budget becomes a preference, and it is the specific failure -this file exists to prevent. If 80 ms turns out to be the wrong number, the repair +the rule above prevents. If 80 ms turns out to be the wrong number, the repair is to move it and say why, for everybody. A client that has not been measured has not met the budget. Not measured and met diff --git a/decisions/first-release.md b/decisions/first-release.md index e03082d..da5f26e 100644 --- a/decisions/first-release.md +++ b/decisions/first-release.md @@ -73,7 +73,7 @@ noise at one. ## What blocks it These are the open issues the sequence above cannot happen without. The list is -a judgement rather than a derivation, and the current state of each one is: +a judgement, and the current state of each one is: for n in 1 5 7 11 17 18 19 24 25 26 27 28 31 32 33 34 35 51 53 54 55; do gh issue view "$n" --repo Flowfin/hub --json number,state,title \ diff --git a/decisions/gate-parity.md b/decisions/gate-parity.md index c7f99d5..839a9a0 100644 --- a/decisions/gate-parity.md +++ b/decisions/gate-parity.md @@ -1,7 +1,7 @@ # Which legs of the plugin board's gate this board carries The gate on `iderex/jellyfin-plugin-sso` is the target this board is measured -against. It is public, so it is read rather than described: +against. It is public, so the list below is read out of the API: gh api repos/iderex/jellyfin-plugin-sso/rules/branches/main \ --jq '.[] | select(.type=="required_status_checks") @@ -20,8 +20,8 @@ against. It is public, so it is read rather than described: prettier dependency-review -Run 2026-08-09. Thirteen names, and they are read at the time of writing rather -than copied from where this question was raised, because a required set moves. +Run 2026-08-09. Thirteen names, read at the time of writing, because a required +set moves and a copy taken where the question was raised would already be stale. Parity does not mean copying. That board is a compiled plugin with a test project, a fuzz harness and a packaging step, and several of its legs have @@ -56,17 +56,16 @@ Both run 2026-08-09. Ten required names against the target's thirteen. Some of this board's own legs are missing from that list, and the set grows every time one lands, because nothing adds a name to a ruleset on its own. A leg in that state runs on every pull request and blocks nothing. How many there are -today is derived rather than written here, since the number moves whenever -either side does: +today is derived by the command below, since the number moves whenever either +side does: comm -23 <(grep -o 'Gate: [a-z0-9-]*' .github/workflows/gate.yml | sort -u) \ <(gh api repos/Flowfin/hub/rules/branches/main \ --jq '.[] | select(.type=="required_status_checks") | .parameters.required_status_checks[].context' | sort) -The left side is the workflow's job names rather than the leg list, and the two -cannot disagree: `internal/gate`'s suite refuses a leg with no job and a job -with no leg. +The left side is the workflow's job names, and it cannot disagree with the leg +list: `internal/gate`'s suite refuses a leg with no job and a job with no leg. Widening the required set is #48 and is not done here; recording that the gap exists is. @@ -90,8 +89,8 @@ tool over the built plugin to produce the archive an operator installs. This board produces no archive: it publishes a file that points at archives other repositories built. -**Package (JPRM) / Generate SBOM.** DROPPED, and it is the one drop worth -arguing rather than stating. An SBOM lists what a build depends on, and this +**Package (JPRM) / Generate SBOM.** DROPPED, and it is the one drop that owes an +argument. An SBOM lists what a build depends on, and this tree's dependency set is empty and is held empty by the absence of a `go.sum` that `CONTRIBUTING.md` spends a section on. An SBOM of nothing is a file nobody reads. The condition that reverses this is the first `require` line in `go.mod`, @@ -106,8 +105,8 @@ What carries over unchanged is that a finding blocks rather than annotates, and that half is not free. The analyse step uploads its results and exits zero whether it found anything or not, so a job that stopped there would be green with findings in it. `go run . scan` reads the report the analysis wrote and exits -non-zero on any result. It is a Go verb over a directory rather than an -expression in the workflow, which is what `decisions/means.md` asks for, and +non-zero on any result. It is a Go verb over a directory, which is what +`decisions/means.md` asks for, so no logic sits in the workflow file, and `internal/scan` carries a fixture for each of its refusals. Three of those four refusals are the analysis having NOT happened rather than the @@ -128,7 +127,7 @@ same certificate, same refusal of a non-merge commit without the line. **Deterministic PR-hygiene checks.** ADAPTED, landed as `Gate: pr-hygiene` in `internal/hygiene`. What carries over is the tiering, so high-confidence rules block and soft conventions annotate, and the explicit skip for an author from -outside the repository that announces itself rather than going quiet. What is +outside the repository, which announces itself on every run it applies to. What is dropped from it is every rule about a solution file, a changelog format or a test project, none of which exist here. @@ -157,8 +156,8 @@ names. The target runs a pattern linter over its source; this board's equivalent is `Gate: no-hardcoded-names`, which refuses an account or organisation name written into source or into a workflow step, and `Gate: site-fetches-nothing-outside`, which refuses a served page that would load from -another host. Both are Go tests over the tree rather than a pattern language, -which is what `decisions/means.md` asks for: a rule that can be run against a +another host. Both are Go tests over the tree, which is what +`decisions/means.md` asks for: a rule that can be run against a planted input rather than one that can only be shown to bite by breaking the tree. **Reject Trojan Source Unicode.** ADOPTED, unchanged, and already required. @@ -180,8 +179,7 @@ exists to notice changing. Neither is a required name on the target's list above, and both are legs it runs. They are recorded here because a parity list that quietly drops what was too much -trouble is the failure this document exists against, and #46 is where that was -raised. +trouble is the failure #46 raised. **Coverage. ADOPTED, with a floor, as `Gate: coverage`.** It costs no dependency: `decisions/means.md` fixes the toolchain as Go, coverage instrumentation arrives @@ -222,7 +220,7 @@ one in `CONTRIBUTING.md`: a guard ships with a planted input it refuses. **Mutation. DROPPED.** The Go toolchain carries no mutation runner, so adopting it means adding the first third-party tool to a tree whose empty dependency set -is a stated property rather than an accident: there is no `go.sum`, the toolchain +is a stated property: there is no `go.sum`, the toolchain refuses an unrequired import at build time, and `dependency-review` exists to notice that changing. `decisions/means.md` asks for that cost to be paid knowingly, and the thing bought does not pay it here. @@ -232,10 +230,10 @@ the generator's value is in its refusals, and a test that calls a refusing function and asserts it returned is green whether the refusal fired or not. This board answers that question per guard instead of per run. Every pull request adding a guard shows it refusing a planted input, and the fixtures live in the -tree afterwards, so the answer is in the suite rather than in a nightly report -nobody opens. +tree afterwards, so the answer lives in the suite, next to the guard it is about, +where a nightly report nobody opens would have held it. -The bound on that is real and is stated rather than hidden: a planted fixture +The bound on that is real and is stated here: a planted fixture proves the guard bites for the case somebody thought of, and a mutation run looks for the case nobody did. Nothing here replaces that. @@ -271,5 +269,5 @@ gate's own report says on every run that none of them ran. Which of these becomes a required name on `main` is #48. This document says what each leg is; a ruleset is what makes one block, and nothing in this tree can read a ruleset. The list above was derived by running the commands at the top on -2026-08-09, and it is derived again rather than trusted the next time somebody -needs it. +2026-08-09. Run them again the next time somebody needs the list; what they +print then is the answer. diff --git a/decisions/manifest-schema.md b/decisions/manifest-schema.md index b475ef7..9388e88 100644 --- a/decisions/manifest-schema.md +++ b/decisions/manifest-schema.md @@ -151,13 +151,12 @@ the failure #23 is about and is not left to a checkout setting. **Keys are emitted in the order above, not sorted.** Sorted keys would put `category` before `changelog` and scatter a version entry's identity through it. The order above is fixed by the order of the fields in the types the generator -marshals, so it is a property of the source rather than a formatting pass, and -changing it is a change to a struct that a reader sees. +marshals, so it is a property of the source, and changing it is a change to a +struct that a reader sees. **Strings carry their own characters.** An ampersand is `&` and not `\u0026`, an angle bracket is `<` and not `\u003c`, and a non-ASCII character is emitted as -UTF-8 rather than as an escape. The published catalogue does the same, which is -worth stating as bytes rather than as an intention: +UTF-8. The published catalogue does the same, and the bytes say so: python -c " raw=open('jf-manifest.json','rb').read() @@ -213,13 +212,13 @@ above as bytes. It carries two plugins so the plugin ordering is visible, one wi on one plugin with five entries on one and two on the other so the per-target cap is visible as a shape, and a changelog containing an ampersand, angle brackets, a quotation mark, a backslash and a non-ASCII letter so the escaping is fixed by an -example rather than by a sentence. +example a reader can run. It is a fixture and its contents are fixture contents. The names, guids, URLs and digests in it are invented, the host is the documentation domain reserved for exactly this, and nothing in it is a claim about any real release. A fixture built out of the real catalogue would prove the state of the world on the day it was -built rather than the format. +built, and the format is what needs proving. The test that compares generator output against it byte for byte is #29. This file and the fixture land together because a format decision written without an example diff --git a/decisions/means.md b/decisions/means.md index 87f1b22..2d6ed3f 100644 --- a/decisions/means.md +++ b/decisions/means.md @@ -41,7 +41,7 @@ of the workflows this repository already carries are that shape: Run 2026-08-08 at 6a98de6. Both are worth keeping, because they are cheap and because rewriting somebody's working scanner is not free. What they are not is a -pattern to extend once the checks start encoding decisions rather than scanning +pattern to extend once the checks start encoding decisions instead of scanning for characters. ## What it adds that the tree does not already carry diff --git a/decisions/plugin-identity.md b/decisions/plugin-identity.md index 943218d..bfc7c7f 100644 --- a/decisions/plugin-identity.md +++ b/decisions/plugin-identity.md @@ -48,7 +48,7 @@ catalogue uses and those six are in both of them. Optional: `imageUrl`. Absent in eleven of the thirty-four entries measured there, so a plugin with no artwork is ordinary rather than defective, and the key is -omitted rather than emitted empty. +left out where there is no value for it. `guid` is also checked for shape, because it is the one field whose damage is invisible. It must be eight, four, four, four and twelve hexadecimal characters @@ -59,8 +59,8 @@ the entry as a plugin it has never seen. `owner` is read from the descriptor like the rest. Entry 4 of #1 decided that the catalogue carries the account that publishes the releases, and the reason given there was that the field should show the same name as the release it came from. -Reading it out of the release is that decision implemented rather than a second -place the name is decided. Writing the answer into this generator instead would +Reading it out of the release is that decision implemented, and it opens no +second place where the name is decided. Writing the answer into this generator would be refused by `no-hardcoded-names`, which is `decisions/names-are-data.md`. ## Why the sidecar rather than the build descriptor @@ -105,7 +105,7 @@ for, and a reader has to know which of them to ignore. The cost is stated: a release that ships a `build.yaml` and no sidecar carries identity this generator will not read, and is refused rather than parsed by the -other route. That is one file format rather than two, and two readers of one fact +other route. That holds the tree to one file format, and two readers of one fact disagree the first time one of them is corrected. ## What this costs, and the cost is live today @@ -160,8 +160,7 @@ ENFORCEMENT` for that half. ## What this does not settle Where `version`, `targetAbi`, `changelog` and `timestamp` are read from. They sit -in the same file and they are the version entry rather than the identity, and -nothing here reads them. +in the same file, they belong to the version entry, and nothing here reads them. What the run does with a refused plugin. That is `decisions/failure-posture.md`, which already places a defect in the newest diff --git a/decisions/release-procedure.md b/decisions/release-procedure.md index 86d6744..67d0761 100644 --- a/decisions/release-procedure.md +++ b/decisions/release-procedure.md @@ -29,7 +29,7 @@ of a run reads one run's whole output or the previous one's and never a mixture. Landed. The placed file is a tracked file, so it reaches the address the way every other change does: a branch, a pull request, the gate, a merge. There is no route from a generator run to the served directory that skips the merge, and -that is deliberate rather than missing. +that gap is deliberate, and it is recorded here so it reads that way. Served. The site is served from the `docs` directory of the default branch by a host this repository does not run and cannot make deploy. A merge is not a @@ -103,8 +103,8 @@ them holds. The conditions are `internal/readiness`'s and every one of them is tripped in that package's own suite against a planted reading, so a condition that has never refused anything is not among them. -Two answers rather than one when a condition does not clear, and they are not -the same sentence. A condition that HOLDS is a fact about this tree, and the +A condition that does not clear has two answers, and they are not the same +sentence. A condition that HOLDS is a fact about this tree, and the repair is to change the tree. A condition NOT EVALUATED is a fact about this run: the address nobody has recorded, the read that failed, the release lists that could not be fetched. Both refuse. An unread condition is never a clear diff --git a/decisions/source-set-declaration.md b/decisions/source-set-declaration.md index 6df3ed7..7e76d94 100644 --- a/decisions/source-set-declaration.md +++ b/decisions/source-set-declaration.md @@ -103,8 +103,8 @@ first release is proving. ## What refuses a violation -Nothing yet. This file states where the set comes from; #24 is the loader that -reads it and refuses a declaration whose repository or release it cannot resolve, +Nothing yet. The declaration below is where the set comes from; #24 is the loader +that reads it and refuses a declaration whose repository or release it cannot resolve, and #30 is the check that refuses either name being written into the source instead. Until those land, the rule holds because nothing has broken it, which is a different sentence from the rule being enforced. diff --git a/decisions/speed-budget.md b/decisions/speed-budget.md index c06e4dc..a107575 100644 --- a/decisions/speed-budget.md +++ b/decisions/speed-budget.md @@ -11,21 +11,20 @@ usable and on whether the cache was warm. Playback start depends on whether the server is transcoding, which is not a property of the client at all. So each number below carries what starts the clock, what stops it, the device and -the network, how many runs, and which statistic is reported. The percentile -rather than the mean, because the complaint a person has is about the worst thing -they noticed and not the average they did not. +the network, how many runs, and which statistic is reported. The percentile, +because the complaint a person has is about the worst thing they noticed. Where a number cannot be measured on a clean runner it says so and names the harness requirement it needs. `decisions/headless-and-unelevated.md` is why that -is a name rather than a skipped job, and `internal/harness` is where the names -are. A budget that quietly stopped being checked is worse than one that says -which parts are not. +requirement carries a name, and `internal/harness` is where the names are. A +budget that quietly stopped being checked is worse than one that says which +parts are not. ## Focus change, under 80 ms **Starts** at the key event as the device reports it: the down edge, not the up -edge, and taken from the platform's own input timestamp rather than from when the -application loop noticed it. **Stops** at the first frame presented in which any +edge, and taken from the platform's own input timestamp, which is stamped before +the application loop notices it. **Stops** at the first frame presented in which any pixel of the newly focused element differs from the frame before it. Presented, not painted, and not the callback: what the person waits for is the screen. @@ -41,14 +40,14 @@ because one 300 ms move is a thing a person sees and a mean hides. **Where it runs.** `needs-browser` for a web client and hardware for a native one. Not measurable on a clean runner: the number is about frames presented on a display and there is no display in the gate. The frame-timing part of it is -measurable in a headless browser with a compositor and that is an approximation -of this number rather than this number. +measurable in a headless browser with a compositor, and what that yields is an +approximation, recorded under that word. ## Dropped frames, none at 60 fps **Starts** when a scroll of 200 tiles begins, driven by a synthetic input at a -fixed velocity rather than by a hand, so two runs are comparable. **Stops** when -the scroll ends and the content is at rest. +fixed velocity, so two runs are comparable; a hand repeats nothing closely +enough to compare. **Stops** when the scroll ends and the content is at rest. A dropped frame is a presented frame whose interval from the previous presented frame exceeds 1.5 times the display's frame period. That threshold is stated @@ -57,9 +56,9 @@ missed, and a rule reading "over 16.7 ms" counts scheduling noise as a drop and reports a number nobody can hit. **Device and network.** The slowest supported device, artwork already in the -local cache. Fetching artwork mid-scroll is a real failure and it is the first -number's neighbour rather than this one: this measures whether the client can -present what it already has. +local cache. Fetching artwork mid-scroll is a real failure and it belongs beside +the first number: this one measures whether the client can present what it +already has. **Runs and statistic.** 10 scrolls, and the reported number is the total count of dropped frames across all 10, not an average. The budget is zero, so an average @@ -82,7 +81,8 @@ the application's own first line and is the point the person's wait starts at. **Cold** is stated because it is where the number is usually lost: no warm process, no warm page cache, no pre-rendered view, and the artwork cache emptied. -The device is rebooted before each run rather than the application restarted. +The device is rebooted before each run; restarting the application leaves too +much of the machine warm. **Device and network.** Slowest supported device. A local network with the server on the same subnet, because a cold start makes requests and the number is about @@ -117,9 +117,8 @@ this on a fast network is measuring the wrong case. percentile. **Where it runs.** `needs-browser` for a web client, hardware for a native one. -The static half of it is a gate check rather than a harness one, because a tile -whose height is not fixed in the stylesheet is visible in the source; the -dynamic half is not. +The static half of it is a gate check, because a tile whose height is not fixed +in the stylesheet is visible in the source; the dynamic half needs the harness. ## Playback start, under 2 s diff --git a/docs/design-tokens.json b/docs/design-tokens.json index 4b818ab..fd961c9 100644 --- a/docs/design-tokens.json +++ b/docs/design-tokens.json @@ -76,7 +76,7 @@ "focus": { "what": "The one place in the interface with colour in it. Every value below marks what has focus and nothing else.", "combinations": "A brightness scheme and a colour-vision preset combine, and the combination is what an eye actually sees, so the accent is stated per preset per scheme rather than as two lists a reader multiplies. Five presets times two schemes is ten accents.", - "note": "The table on the published page prints one colour per preset, and it is the dark one. A client reading that table alone gets the light scheme wrong for every preset. That disagreement is why this file exists and why it is the authority rather than the page.", + "note": "The table on the published page prints one colour per preset, and it is the dark one. A client reading that table alone gets the light scheme wrong for every preset. The values here are the authority for both schemes, and the page prints a summary of them.", "ring-width-applies-to": "Both schemes. A preset raises the ring for every brightness or for neither.", "accent": { "role": "The mark itself: the ring drawn around what has focus, and any glyph that carries the same signal.",