Legal accountability: plain register, and mechanisms that break when a claim stops being true - #1
Merged
Merged
Conversation
added 7 commits
July 30, 2026 19:30
Both documents read as engineering memoranda about law rather than as legal
instruments. They narrated their own drafting ("stated because absence is a
feature", "this is worth stating plainly"), carried correction histories
addressed to users who have never existed, and named database tables, columns
and environment variables in prose.
The register is now conventional and the vocabulary is a reader's. Privacy
policy 1250 -> 600 lines, terms 802 -> 443.
NO SUBSTANTIVE POSITION CHANGED. Every retention period, the USD 100 and USD 50
caps with the SeatGeek bullet still separately findable by name, the eight
SeatGeek restrictions, the third-party beneficiary grant, Illinois law and
venue, the no-arbitration position, the Apache-2.0 software/service split and
the biometric disclaimer are unchanged.
Implementation detail is the one thing deliberately removed rather than
reworded. A privacy policy is a representation under FTC Act section 5, so a
document asserting that users.auth_method constrains sign-in becomes a false
legal statement the moment somebody renames the column, with no lawyer involved.
The names moved to section F of the new publication checklist, where a stale
name is a documentation defect instead.
Disclosures were preserved rather than dropped. Where an OPEN marker qualified
something a reader relies on, it became plain prose saying the same thing: no
log retention period is configured, no scheduled retention run has occurred, the
backup lifecycle is configured but unreadable by us. One marker is kept verbatim
because it qualifies a statement about the reader's own rights.
The drafting rationale, the counsel questions and the evidence trail move to
docs/compliance/publication-checklist.md, including the CTDPA sensitive-data
question and the fact that no client presents the documents, which on the day
live events are enabled is a SeatGeek clause 4.3 breach in three places.
Digests re-pinned. Versions held at DRAFT-1: both are unpublished, both
databases hold zero revisions, and a bump would make this the first revision,
which the epoch guard requires to be material. This change is cosmetic under
each document's own rubric, so bumping would have meant asserting a materiality
label invented to satisfy a guard.
…are unmet The existing legal machinery answers "did the document change?". Digests are pinned, epochs are guarded, a formatter run is not an amendment. Every one of the 14 legal tests asserts something about the documents themselves. None asserts that a claim inside them is true of the system. So the documents have integrity and no accuracy. Nothing notices when the code moves out from under a sentence, and enabling a feature is the moment where the largest number of claims can become false at once. This adds the interception point. A capability declares in code what has to be true before it may be switched on, and the process refuses to boot otherwise. WHY A REGISTRY AND NOT A COMMENT. Because the comment was tried and rotted in a day. SEATGEEK_ENABLED carried exactly this reasoning, listing the preconditions for enabling events: DPAs unsigned, no Article 27 representative, EULA unpublished. Two of the three were false within twenty-four hours. All four processor agreements were executed 2026-07-30 and the Article 27 representative became moot when the service moved to a United States posture. Nothing failed, because prose has no relationship to the thing it describes. That comment is corrected here and now points at the registry. TWO KINDS OF PRECONDITION. A checkable one is proved by a predicate. An attested one is something no code can prove, such as whether a screen exists and a human saw it; it is satisfied only by a dated attestation written into the registry, which makes enabling that capability a reviewed change rather than an environment variable flip, and leaves a record of who checked and when. An attestation left null blocks. SEATGEEK_ENABLED=true is therefore refused today, naming the three unmet conditions. The most important is that no client presents the documents, which on the day events are enabled breaches SeatGeek clause 4.3 in three places at once, one of which is not limited to SeatGeek data. EVERY PREDICATE IS PROVED ABLE TO FAIL. A predicate that cannot fail is this repository's house defect, so each is tested twice: against the real documents, and against a mutated copy. A test enumerates the registry and fails if any predicate lacks a mutation case. That test earned its place immediately: the MusicBrainz mutation replaced only exact-case occurrences while the predicate matched case-insensitively, so it proved nothing until corrected. A test also parses config.ts and fails if any *_ENABLED flag has no registry entry, which is the catch for the feature nobody thought was a legal event. DOCS_ENABLED is recorded as inert with a reason rather than omitted, so "nothing to check" is always a decision on the record.
Two suites already existed. legal-versions asserts things about the documents. legal-triggers asserts a capability is not enabled before its preconditions hold. Neither notices ordinary development: somebody adds an analytics dependency on a Tuesday and privacy policy section 3.6 becomes a false statement of fact under FTC Act section 5 with every gate green. This is the third leg, in three parts. SECTION F IS NOW EXECUTED. Moving the table and column names out of the policy removed a coupling: a developer renaming users.email used to at least meet the policy in a grep. Section F was inert prose, which traded a misrepresentation risk for a drift risk, and that trade is only correct if the drift risk closes. The suite parses section F, classifies every backticked token, and asserts each file, environment variable, package script, table and column exists. It refuses to pass if fewer than fifteen tokens resolve, so emptying the table or breaking the classifier is a failure rather than a quiet loss of coverage. THE SECTION 3.6 NEGATIVES ARE ASSERTED. No analytics, telemetry or error reporting dependency; no payment dependency; no schema identifier for precise location; none for biometric data or a password. These are the highest consequence claims in the document and the cheapest to violate, each being one dependency away, and they are what a reader is least able to verify. A further test pins them to the text, so softening section 3.6 cannot leave the assertions guarding sentences the document no longer makes. NO USER-OWNED TABLE ESCAPES THE DELETION CLAIM. Section 7 states that deletion removes everything linked to an account in a single step. That holds only while every foreign key to users cascades. verify-migrations asserts this against a real database in CI; this is the static half, so the failure is visible without one and names the legal consequence. THE DETECTORS ARE PROVED ABLE TO FAIL. The cascade detector is exported and run against synthetic schemas: a cascading reference is not reported, a bare reference is caught, and ON DELETE SET NULL is caught rather than mistaken for a cascade. That last case is the one a substring search for "on delete" would wave through, and the one that would silently orphan personal information after an erasure.
The consent screen quotes four figures and four section numbers out of the Terms, which makes it the one document in legal/ derivative of another. It declared what it had been checked against as `id@version`, and a test compared that to the registry. A version only moves when somebody decides to move it, and an unpublished draft is edited in place: the documented precedent here is that redefining a version nobody has ever been served is not a revision. So DRAFT-1 can mean different bytes on different days. Both source documents were rewritten twice on 2026-07-30 while staying DRAFT-1. The interlock stayed green through all of it, certifying as re-read a set of figures sitting in text nobody had re-read. That is the defect the digest lock exists to prevent, reintroduced one layer up: a check that reports success because the thing it compares was not the thing that changed. The pin is now `id@version#digest`, twelve hex characters of the source document's content hash. A prefix rather than all 64 because this line is read by people and a wrapped hash is a line nobody checks; 48 bits is far past coincidence for a drift check and is not defending against a crafted collision. An entry in the old form parses to `digest: null` and is rejected by name rather than accepted, because silently treating it as satisfied would reopen the hole. VERIFIED END TO END, not just asserted. Perturbing the Terms turns the digest lock red. Recording the new digest in the registry without re-reading the highlights, which is precisely what happened twice today, now turns the interlock red as well. Six unit tests cover the parser: it extracts a digest, reports a digest-less entry as such, stops the version at the hash so neither field absorbs the other, distinguishes a stale digest from a current one at the same version, and returns null rather than an empty object when the line is absent, since an empty object would compare equal to nothing and be vacuous. consent-presentation held at DRAFT-0: unpublished, zero revisions in both databases, and adding a digest to a provenance line alters neither what a user is shown nor what they are asked, which is the only question that makes a change to this document material under its own rubric.
Two legal positions lapse with time rather than with a code change, so neither the trigger registry nor the claims suite can see them go wrong. SeatGeek's section 1 permits them to change their terms at any time, with continued use as acceptance and no notice to us. If their clause 8.2 cap on themselves drops below USD 50, the fourth bullet of terms section 13 becomes less protective than their 4.3 requires while this repository sits untouched. The seatgeek-cap-is-fifty predicate catches OUR figure moving and structurally cannot catch theirs. State privacy statutes change on their own schedule. Connecticut removed the volume threshold from two of its three applicability triggers on 1 July 2026, converting "we are below every threshold" from an argument into a non-sequitur. That amendment was signed in 2025 and took effect a year later, and nothing here would have surfaced it. The existing answer was "re-audit quarterly", written in a checklist. An intention with no trigger is what this codebase rejects everywhere else. A failing test rather than a reminder, because a reminder is another intention. This is shaped like a certificate expiry check: disruptive in proportion to how overdue it is, impossible to miss, and cleared by doing the work. The failure names what to re-read, why it matters, where the evidence lives, and says to fix what the audit finds before recording the date. The overdue arithmetic takes `now` as a parameter so it can be tested against fixed dates. A date-based check exercised only on the day the suite happens to run is a coincidence, not a test. The boundary is tested too: an audit exactly on its due date is not yet overdue, because firing a day early would make every stated interval a day longer than the real one.
The pre-commit hook ran `pnpm exec lint-staged`. On a machine without pnpm it exited 127 before running anything, so `git commit` failed for a missing package manager rather than for a problem with the commit. The predictable response is --no-verify, which also skips the secret scan, and on a public repository that is the expensive half. Five commits on this branch went in that way. A gate that fails for the wrong reason does not get fixed; it gets bypassed, and it takes the rest of the gate with it. The runner now degrades the way the scanner above it already does: pnpm if present, otherwise node_modules/.bin/lint-staged, which is the same check through a different launcher. It still fails outright when lint-staged is reachable by neither, since that is a broken install rather than a fresh checkout. The gitleaks branch is deliberately left as a warning. Requiring it would block every contributor who has not installed it, and CI enforces it regardless. That is a policy decision that was already made; this changes only the case where the check could have run and did not. This commit was made with the hook enabled, which is the test.
Nineteen lint errors, none of them behavioural. Fourteen were auto-fixable (array-type, unnecessary assertions, void-expression arrow shorthand, unnecessary template expressions); the remaining five were numbers interpolated into template literals, which restrict-template-expressions requires be made explicit with String(). Found only by CI. Locally I ran prettier and tsc and took formatting-plus-types as sufficient, which it is not: eslint is a third gate with its own rules and "pnpm lint" is what the pipeline runs. Verified here with ./node_modules/.bin/eslint . --max-warnings=0, which is the same command.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six commits. The first rewrites the two legal documents; the rest build the machinery that keeps them accurate as the product changes.
The problem
Every one of the existing legal tests asserts something about the documents: that they exist, that digests are well-formed, that a formatter run is not an amendment. None asserted that a claim inside them is true of the system.
So the documents had integrity and no accuracy. Nothing noticed when the code moved out from under a sentence. Somebody adds an analytics dependency on a Tuesday and privacy policy section 3.6 becomes a false statement of fact under FTC Act section 5, with every gate green.
What landed
6d80ea55dad053911aa66fb54c2dc2dc81c6e294f1A legal document goes false in three ways: a capability is enabled, the code changes, or time passes. Each now has something that breaks.
No substantive position changed in the rewrite
Every retention period, the USD 100 and USD 50 caps with the SeatGeek bullet still separately findable by name, the eight SeatGeek restrictions, the third-party beneficiary grant, Illinois law and venue, the no-arbitration position, the Apache-2.0 software/service split and the biometric disclaimer are unchanged.
Implementation detail is the one thing removed rather than reworded. A privacy policy is a representation under FTC Act section 5, so a document asserting that
users.auth_methodconstrains sign-in becomes a false legal statement the moment somebody renames the column. The names moved to section F ofdocs/compliance/publication-checklist.md, where a stale name is a documentation defect instead, and a test now asserts every identifier there still exists.Disclosures were preserved, not dropped. Where an
[OPEN]marker qualified something a reader relies on, it became plain prose saying the same thing. One marker is kept verbatim because it qualifies a statement about the reader's own rights.Every predicate is proved able to fail
A predicate that cannot fail is this repository's house defect, so each is tested twice: against the real documents, and against a mutated copy. A test enumerates the registry and fails if any predicate lacks a mutation case.
That test earned its place immediately. The MusicBrainz mutation replaced only exact-case occurrences while the predicate matched case-insensitively, so it proved nothing until corrected.
The cascade detector is exported and run against synthetic schemas. The case that matters is
ON DELETE SET NULL: a substring search foron deletewould wave it through, and it is precisely the one that silently orphans personal information after an erasure.The interlock fix was verified end to end rather than asserted. Perturbing the Terms turns the digest lock red; recording the new digest without re-reading the highlights, which is exactly what happened twice during this work, now turns the interlock red as well.
Consequences worth knowing before merge
SEATGEEK_ENABLED=trueis now refused at startup, naming three unmet conditions. The most important is that no client presents the documents, which on the day events are enabled is a SeatGeek clause 4.3 breach in three places at once.--no-verifybecause the hook exited 127 on a machine without pnpm, which skipped the local secret scan.6e294f1fixes that and was committed through the working hook. gitleaks is absent locally either way, so CI is the backstop for the earlier five.Test plan
apps,legal,docs,packagescheck-public-identifiersexit 0check-publication-blockersexit 1, which is correct: 3 real markers, 10 legend