Skip to content

The heads say which client burns the file, and a paid read count names the API - #391

Merged
Apolloccrypt merged 1 commit into
mainfrom
site-claims-burn-heads
Sep 3, 2026
Merged

The heads say which client burns the file, and a paid read count names the API#391
Apolloccrypt merged 1 commit into
mainfrom
site-claims-burn-heads

Conversation

@Apolloccrypt

Copy link
Copy Markdown
Owner

Batch 4: the half of the burn-on-read sweep #388 left in the heads because #380 held them open. #380 has landed, so this closes the claim.

1. The heads

A meta description is the sentence that travels. A search result and a chat preview show it with none of the page around it to qualify it.

Page Old (meta, og, twitter, JSON-LD: 4 places each) New
audit-log-export "... without a second exposure. The file is destroyed after the first read." "... without a second exposure. The web app destroys the file after the first read."
parasend "Send a file that is scrambled on your device and gone once it has been downloaded." "Send a file scrambled on your device that the web app deletes after the first read."

Both stay inside the 50-165 char window seo-contract enforces, and apply_seo_head.py --check still reports 0 pages.

Checked and left alone, per instruction:

  • /get, /ontvang, /parashare keep "burns after one download". The reason turned out to be better than the one this branch had been using: it is not the web app, it is that GET /v2/dl/:token/get deletes the blob outright and never looks at views_remaining (relay.js, td.used = true; blobStore.delete(blobHash);). A download-token link is single-use on every plan. Only GET /v2/outbound/:hash spends a read, and it is the only place that decrements the counter. Their exemption from the head sweep is pinned to those two lines, not to their slugs, so it goes red the day the route stops burning.
  • /vs keeps "burn-on-read" as the name of a comparison dimension in a list next to "EU jurisdiction" and "self-hosting". The compound noun is a feature name and the patterns deliberately do not match it.
  • The seo-contract PINNED table needed no change: /parasend and /audit-log-export are not in it, and the pinned /pricing description never carried a read count.

2. A paid read count names the API

"Up to 10 reads per link" on a price card is an offer, and a Pro buyer who sends through the web app or an extension never sees a second read: those clients do not ask for one.

Page Line
pricing:436, :464 Pro and Enterprise cards, <li>Up to N reads per link **through the API**</li>
parasend:326, :341 same two cards
index:526 Pro price line
about:213 ParaSend Pro paragraph
privacy:208 "a paid plan buys more reads per link through the API"
terms:246 "more reads per link through the API"

Enterprise went with Pro, and the four prose spots went with the cards, because leaving them inconsistent is a review finding waiting to happen. The rule in block 37 moved from client pages only to every page: the misleading half was never the page, it was the offer.

The cards stay pinned to tiers.js. pricing-page.test.js uses a substring match (statesLine), so its Up to 10 reads per link pin still holds unchanged. Block 12 now captures the card phrase with the qualifier, so /about has to repeat that too. pricing-fold unaffected.

3. The vocabulary, and a miss I have to report

Sabotage S11 got past the block. Putting the /parasend head back to "gone once it has been downloaded" stayed green: the moment vocabulary knew once it has been read and not the three other verbs for the same event. Widened to read|downloaded|opened|delivered, with the miss written into the comment above it so the next person sees how the list is meant to grow.

4. Row 37

Closed. It now records why the three download-link pages keep their wording, and ends "Nothing on this claim is open".

Sabotage, four ways, all red

Sabotage Message
/parasend head back to "gone once it has been downloaded" (S11) parasend: the head says "gone once it has been downloaded" with no page around it to qualify it
/audit-log-export head back to the flat claim audit-log-export: the head says "destroyed after the first read" with no page around it to qualify it
Pro card drops "through the API" pricing: offers "Up to 10 reads per link" without naming the API; through the web app or an extension a paid plan never gets a second read
/v2/dl stops burning outright the /v2/dl download-token route no longer deletes the blob outright; /get, /ontvang and /parashare call a Paramant link single-use because it does

Gates

Green locally: site-claims (37), all 41 browser suites (first-screen, pricing-fold, ui-truthfulness, navigation-shell, user-dashboard-documents, seo-contract, links, frontend-loading-contract), cache-bust, csp-inline, static-sanity, check-test-declarations (124 suites), eslint, relay/test/pricing-page.test.js, apply_seo_head.py --check (0 pages), apply-nav.py idempotent. Root integration suites 173 pass, 1 pre-existing environment failure (heartbeat-lib needs @noble/post-quantum, installed by npm ci in CI).

9 files, +87 / -36.

…s the API

Batch 4, the half of the burn-on-read sweep #388 left in the heads because
#380 held them open. #380 landed, so this closes the claim.

THE HEADS. /audit-log-export and /parasend stated burn-on-read flat in
four places each: meta description, og:description, twitter:description
and the JSON-LD WebPage description. A meta description is the sentence
that travels. A search result and a chat preview show it with none of the
page around it to qualify it, and /audit-log-export names the ParaSend web
app only in step 01 of a workflow further down. Both now name the client.

/get, /ontvang and /parashare keep "burns after one download", and the
reason is better than the one this branch has been using. It is not the
web app: it is that GET /v2/dl/:token/get deletes the blob outright and
never looks at views_remaining, so a download-token link is single-use on
every plan. Only GET /v2/outbound/:hash spends a read. Their exemption
from the head sweep is pinned to those two lines in relay.js rather than
to their slugs, so it goes red the day the route stops burning. /vs keeps
burn-on-read as the name of a comparison dimension.

THE CARDS. "Up to 10 reads per link" on a price card is an offer, and a
Pro buyer who sends through the web app or an extension never sees a
second read: those clients do not ask for one. The Pro and Enterprise
cards on /pricing and /parasend now say "through the API", and so do the
same figures on /index, /about, /privacy and /terms. The rule went from
client pages only to every page, because the misleading half was never
the page, it was the offer.

Block 12 captures the card phrase with the qualifier included, so /about
has to repeat that too.

THE VOCABULARY. Sabotage S11 got past the block: putting the /parasend
head back to "gone once it has been downloaded" stayed green, because the
moment vocabulary knew "once it has been read" and not the three other
verbs for the same event. Widened to read, downloaded, opened, delivered,
with the miss written into the comment. S11 is red now.

Sabotage, four ways, all red: either head back to its flat claim, a card
dropping "through the API", and /v2/dl no longer burning outright.

Row 37 of docs/site-claims.md is closed and says why the three
download-link pages keep their wording.
@Apolloccrypt
Apolloccrypt merged commit f0a046e into main Sep 3, 2026
13 checks passed
Apolloccrypt added a commit that referenced this pull request Sep 3, 2026
…e bar is measured on three pages

Review of #392 found the hole in the first round. developer.html keeps its own
navigation (KEEP_OWN_NAV in frontend/apply-nav.py), is stamped by hand, and
carries a hamburger. The round moved Sign in and Help out of the bar below
700px and into a strip under the drawer, and that page never got a strip. So on
a 390px screen both links measured 0x0 there, where main still had them in the
bar. A page the generator does not own is a page a generated fix does not
reach.

Three things, because one of them alone would leave the same gap open.

The markup. developer.html carries the strip now, like every stamped page.

The script. nav.js builds the strip when a page has a menu button and no strip.
A page can forget the markup; this cannot forget to build it. That is a floor
under the live site, not a substitute for the markup, which is why it comes
with the check below rather than instead of it.

The check. tests/navigation-shell reads every .html in frontend/, not only the
51 the generator writes, and fails on any page that has id="nav-hamburger"
without id="nav-mobile-tail". 52 pages carry the button. Taking the strip back
out of developer.html turns it red and names the file.

And the measurement moved. The three phone-bar checks ran on the homepage
alone, which is exactly why they were green while /developer was broken. They
run on /, /pricing and /developer now, each with a fourth check that the strip
really holds /auth/login and /help at 44px or more. /developer measures logo
x16-95, Create account x181-318, menu x330-374, gaps 86 and 12, all heights 44.

39 checks, all green. Rebased on main with #379, #390 and #391.
@Apolloccrypt
Apolloccrypt deleted the site-claims-burn-heads branch September 5, 2026 18:57
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.

2 participants