feat(web): the Plans page, inverted — a transparency artifact with nothing to click (#706) - #740
Merged
Conversation
…thing to click (#706) A retail broker's Plans & Features page is a tier matrix with "Current plan" and "Upgrade" badges. It is radically clear about pricing and honest AS MARKETING, because every row exists to move a reader one row down. Jesse paywalls even paper trading. keel's constitution runs the other way. Two of its eight numbered lines are "Paper is free and unlimited, forever" and "The free engine is never a demo". So this page takes the clarity and refuses the gating: what runs on this device and why it costs nothing, what a tier would change if one existed, and the named trigger that must fire before any of it does. TRACEABILITY IS THE ACCEPTANCE CRITERION, SO IT IS MECHANICAL "Every claim on the page traces to the evolution plan or the constitution -- no aspirational copy." A page of marketing sentences that merely SOUNDED like the project's documents would pass a human reviewer and rot within a month. So every claim carries the repository-relative path it was quoted from, and the tests open that file and look for the sentence. Verbatim, never paraphrased: a paraphrase is a new claim wearing the authority of an old one. The check runs in BOTH directions, and the second is why it earns its place. A claim nobody can trace fails the build -- and so does a claim the project has since edited out of its own documents, which is the failure that would otherwise go unnoticed: a promise page still making a promise nobody kept. Demonstrated: adding a ninth line to §4 of the evolution plan fails this until the page carries it, because the count comes out of the document rather than a literal. THE WHOLE CONSTITUTION, NOT A SELECTION Eight lines. A page that quoted seven would be choosing which of the project's own commitments to show a reader, on the page whose entire subject is what the project promises. NOTHING IS FOR SALE, AND THE PAGE SAYS SO FROM THE TABLE ADR 0004's answer is "not now" and none of its four triggers has fired, so no tier is `shipped`. That is a FIELD rather than a sentence, because "this does not exist yet" is the most important thing the table says and a sentence can be edited past without anything failing -- and the headline is derived from the field, so the two cannot come to disagree. The direction they would disagree in is a shipped product under a headline saying there is nothing to buy. `available_now` is separate and true of exactly one row: the free tier is what the reader is already running. Four rows presented as four CHOICES would be a paywall matrix with the prices greyed out. THE REFUSALS ARE STRUCTURAL, NOT EDITORIAL There is no `cta`, `url`, `action` or `contact` key on this payload, and nothing beneath it a client could build one from -- asserted over the serialised JSON, so a key added anywhere inside is caught. `render.js` cannot make a button out of a page that never sends it a destination. The view builds no anchor, no button, no form and binds no handler, pinned against the parsed function bodies with string literals KEPT (the earlier lesson in this series: a scan that strips them cannot see `el("a")`). Citations are `<code>` and not links -- `keel serve` is a loopback SQLite reader with no document server behind it, so a link would 404, and a path a reader opens in their own checkout is the honest form. And the tier table has no sort control, on the route or in the view. A tier table ordered by price is a shopping comparison. NO DATABASE The one route here whose subject is the PROJECT rather than the deployment. It reads no repository, no config and no network, so it answers on a machine with nothing set up -- a page explaining that keel is free and runs entirely on the operator's own hardware would be a strange one to gate behind having already installed it. Twelve mutants killed: a constitution line softened into marketing, the constitution quoted incompletely, a price drifting from the published one, a tier marked shipped ahead of the PR that ships it, the page growing an upgrade link, the payload carrying a checkout destination, the refusal list dropped from the page, the table becoming sortable by price, a citation becoming a link, the free tier ceasing to be the one that exists, `for_sale` hard-coded rather than read from the table, and the route starting to require a database. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6
…resence, not faithfulness An independent review found thirteen issues. The page's whole claim is that a reader can check it, so the ones that mattered were all about the checking. A SUBSTRING IS NOT A QUOTE Every traceability assertion was `claim.text in document`. That verifies PRESENCE, and presence is not faithfulness: "reopens affiliate links" is a contiguous substring of ADR 0004, where the sentence reads "None of these four reopens affiliate links (rule 2, above)". Quoted alone under the heading "What no tier will ever gate" it would invert the record, carry a checkable citation, and pass. A quote must now begin where a statement begins -- a list item, a table cell, a paragraph, a sentence within one -- with continuation lines joined first, because these documents wrap at 100 columns and a quoted sentence should not have to reproduce where the wrapping fell. That does not make a quote honest; nothing can. It removes the trick of starting a sentence in the middle to drop the word that reverses it. AND ANCHORING CATCHES A QUOTE THAT BEGINS LATE, NEVER ONE THAT ENDS EARLY Which is the dangerous half for a trigger. All three truncations in the first cut were in ADR 0004's "What fires it" column, and each cut widened the condition: trigger 3 lost "not a feature request, a request to pay for upkeep" -- the clause separating it from an ordinary feature request; trigger 1 lost most of its condition; trigger 4 lost "evidenced by external contributors, not by the founder's schedule", which is what stops a tired maintainer being the evidence. Each firing condition is now compared WHOLE against the record's own table, cell for cell. The page was publishing an easier promise than the one that was made. THE LONGEST STRING ON THE PAGE WAS TRACED BY NOTHING `every_claim` yielded promises and not `Tier.buys` -- a whole table column -- and the tier test checked name, price and promise only. A contributor could have added "early access to new rule families" to a tier, rendered it with a citation beside it, and passed every gate. All four cells are traced now, and a new test compares what the payload SHOWS against what is traced, so the next omission fails rather than waiting to be noticed. THE PURCHASE BLOCKLISTS COULD NOT SEE THE RAIL THE PROJECT CHOSE Two lists, neither a superset of the other, both built from generic e-commerce words. Six real payment CTAs passed both -- among them `lightning:lnbc...` and `bitcoin:bc1q...`, which ADR 0004 and Phase F both name as the intended rail. One list now, shared, carrying those schemes. The first belt is stronger and is worth naming: every cell of the tier table has to appear verbatim in the evolution plan, so an injected call to action fails traceability without anyone having had to think of its wording. AND THE NEW "NOTHING CLICKABLE" TEST WAS WEAKER THAN ITS OWN SIBLING #704's blocked `button` and `href` unquoted; #706's blocked `"button"` quoted and dropped `href`. So `el('button', 'cta', 'Buy Pro')` in single quotes, and a `setAttribute` of `href` on a span, passed the newer test while failing the older one. One shared token list now, unquoted. A SHIPPED TIER WOULD HAVE READ AS NOT A THING YET `state` was `GOOD if available_now else UNKNOWN` and ignored `shipped` -- so the day a Pro tier shipped, the one row that had become real would have worn the same badge as the three that had not, under a docstring asserting every other row is unknown because it is "simply not a thing yet". And that row's display collapsed to the word itself: "shipped / shipped", on the only status a future PR will actually set. THE CONSTITUTION COUNTER FAILED ON THREE INNOCENT REFORMATS "First character is a digit" counted a numbered sub-point and a prose line beginning with a date; a renumbered heading raised a bare IndexError instead of saying what was wrong. Anchored regex and a reported failure now, with the two miscounted shapes as a fixture. Also: the page sent `as_of`/`generated_at` and rendered neither -- `gates_payload` next door describes capability rather than deployment and sends no timestamp at all, and nothing on this page was OBSERVED at a moment, so both are gone. `plansView` was inserted between `gatesView`'s jsdoc and `gatesView`, orphaning it -- moved back. Three of six "never paywalled" entries were constitution lines repeated from the section above, two of which are not things a tier could gate; the list is six claims that are actually about gating, in prose rather than with `**` markers rendering as literal asterisks through `plain()`. The section check is derived from the payload's keys rather than a list of literals. And two comments naming nav counts that were wrong by six ("the eighth nav entry", "seven entries today") say what they mean instead of counting. Eleven mutants killed, including: a promise nobody made added to a tier's `buys`, a lightning invoice and a bitcoin URI in a price, a single-quoted Buy button, an href set on a span, a trigger truncated back to a wider condition, a quote starting mid-sentence to invert the record, a shipped tier reading as unknown, the counter reverting to the loose predicate, and a payload section that stops being rendered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6
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.
Closes #706.
A retail broker's Plans & Features page is a tier matrix with "Current plan" and "Upgrade" badges. It is radically clear about pricing and honest as marketing — every row exists to move a reader one row down. Jesse paywalls even paper trading.
keel's constitution runs the other way: two of its eight numbered lines are "Paper is free and unlimited, forever" and "The free engine is never a demo." So this page takes the clarity and refuses the gating.
Traceability is the acceptance criterion, so it is mechanical
"Every claim on the page traces to the evolution plan or the constitution — no aspirational copy." A page of marketing sentences that merely sounded like the project's documents would pass a human reviewer and rot within a month.
So every claim carries the repository-relative path it was quoted from, and the tests open that file and look for the sentence. Verbatim, never paraphrased — a paraphrase is a new claim wearing the authority of an old one.
The check runs in both directions, and the second is why it earns its place. A claim nobody can trace fails the build; so does a claim the project has since edited out of its own documents — a promise page still making a promise nobody kept. Demonstrated by mutation: adding a ninth line to §4 of the evolution plan fails the build until the page carries it, because the count comes out of the document rather than a literal.
The whole constitution, not a selection. A page that quoted seven of eight would be choosing which of the project's own commitments a reader sees, on the page whose entire subject is what the project promises.
Nothing is for sale, and the page says so from the table
ADR 0004's answer is "not now" and none of its four triggers has fired, so no tier is
shipped. That's a field, not a sentence, because "this does not exist yet" is the most important thing the table says and a sentence can be edited past without anything failing. The headline is derived from the field, so the two can't disagree — and the direction they'd disagree in is a shipped product under a headline saying there's nothing to buy.available_nowis separate and true of exactly one row: the free tier is what the reader is already running. Four rows presented as four choices would be a paywall matrix with the prices greyed out.The refusals are structural, not editorial
cta,url,actionorcontactkey on the payload, and nothing beneath it to build one from — asserted over the serialised JSON, so a key added anywhere inside is caught.render.jscannot make a button out of a page that never sends it a destination.el("a")).<code>, not links.keel serveis a loopback SQLite reader with no document server behind it, so a link would 404; a path a reader opens in their own checkout is the honest form, and it keeps the page free of anchors entirely.No database
The one route whose subject is the project rather than the deployment. It reads no repository, no config and no network, so it answers on a machine with nothing set up — a page explaining that keel is free and runs entirely on the operator's own hardware would be a strange one to gate behind having already installed it.
Gates
E501is pre-existing indocs/experiments/)shippedahead of the PR that ships it · the page growing an upgrade link · the payload carrying a checkout destination · the refusal list dropped from the page · the table becoming sortable by price · a citation becoming a link · the free tier ceasing to be the one that exists ·for_salehard-coded rather than read from the table · the route starting to require a database🤖 Generated with Claude Code
https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6