Skip to content

Ship the four required pages, which all returned 404 - #7

Merged
MichalAFerber merged 3 commits into
mainfrom
feat/ds9-required-pages
Sep 3, 2026
Merged

Ship the four required pages, which all returned 404#7
MichalAFerber merged 3 commits into
mainfrom
feat/ds9-required-pages

Conversation

@MichalAFerber

@MichalAFerber MichalAFerber commented Sep 3, 2026

Copy link
Copy Markdown
Owner

This deploys on merge. deploy.yml has no path filter — any push to main runs test then deploy, and the deploy job gates on CLOUDFLARE_API_TOKEN plus the markdownwizard Pages project, skipping green if either is absent. So merging either publishes these pages or reports a skip notice; it does not silently half-land. On this PR only test runs (deploy is gated to push/workflow_dispatch).

Closes the §9 half of Todoist 6hPJQfR42X8MPcgm.

Verified absent first

/privacy, /terms, /docs, and /support all returned 404 — with /definitely-not-real-9f3x/ also 404 as the negative control §9 requires, so this is a real absence and not a host that answers everything. The real-404 requirement was already met correctly; this is purely the missing-pages half.

Three corrections to the scope as briefed

1. There is no monorepo migration, so nothing came free. markdownwizard is not in wizard-web and is not moving there. The one branch that looks like a migration, claude/bootstrap-markdownwizard, adds a Cloudflare bootstrap workflow and ends at the commit "Point the markdownwizard bootstrap at its own repo." Footer.astro lists markdownwizard.app only as a cross-link in the suite roster. This is a hand-rolled static site — plain HTML, no Astro, no package.json at the root — so packages/ui/*.astro is not reachable from here at all. All four pages are written, not inherited.

2. "No DocsPage component" is true but is not why Docs is special. There is no shared docs component because docs are product-specific: all seven monorepo apps have their own src/pages/docs.astro. Docs was never going to fall out of a component for anyone. Conversely DmcaPage.astro does exist, so the shared set is four components, not three.

3. The family template would have shipped false claims here. It routes all contact through a mailer-backed form, which this site's CSP (form-action 'none') forbids and which has no backend to reach. Its Terms declare an MIT open-source product — true today, and exactly what REGISTRY line 32 records as owed a §10 change. Neither claim is made. The site stays public regardless; only the repo's visibility is in question, and these are different objects.

What shipped

Page Notes
/privacy §9 required
/terms §9 required
/docs §9 required (end-user product), written from scratch
/support Not §9-required; the contact route the other two point at

DMCA deliberately omitted. §9 requires it "whenever user data is stored anywhere other than locally on the user's device." Nothing here is: no server, no account, no upload. There is no hosted content to take down. Flagging it because the monorepo siblings all ship one, so this is a deliberate divergence, not an oversight.

One disclosure the template would not have produced. img-src https: means a remote image referenced in your Markdown really is fetched, which reveals your IP to that host. §9 says that where a page cannot claim "nothing leaves," it must say what leaves and where it goes — so the Privacy Policy says it plainly rather than asserting an absolute the code contradicts.

Reachability, which was the other half of the problem

The editor is a single-page app shell whose <footer> is the formatting toolbar, so there was nowhere to hang a site link. The pages now sit on the toolbar's trailing edge past the word count, where the toolbar's existing horizontal scroll keeps them reachable on a narrow screen. body is overflow: hidden for the split layout, so the content pages bring their own scroll container rather than that rule being relaxed underneath the editor. They reuse .sheet.md-body — the styling the preview already renders into — so there is one prose stylesheet to keep correct, not two.

404.html said "Markdown Wizard is a single page, so the editor is the only place to go." That stopped being true in this commit.

The pages are covered by a test that runs

e2e.mjs now checks that each page renders its heading, declares its own canonical, can scroll, carries the nav, is linked from the editor, and is in the sitemap. The canonical assertion is the one that earns its place: four pages built from one template will happily ship four identical canonical URLs, which is worse than none.

The guard has been watched failing

A green from a guard nobody has seen fail is indistinguishable from a green it would return regardless, so it was calibrated against the exact defect it exists to catch — a page keeping the canonical it was copy-pasted from.

run state result
33805313044 as written 18/18 checks pass, ALL CHECKS PASSED
33805551669 terms.html canonical pointed at /privacy redFAIL - /terms declares its own canonical [https://markdownwizard.app/privacy]
33805668220 reverted ALL CHECKS PASSED

The failure is also specific rather than blanket: the other seventeen checks stayed green and the message names the offending value, so a future failure points at the defect instead of merely reporting that something is wrong. terms.html after the revert is byte-identical to before it; the two calibration commits are on the branch deliberately, as the record of it.

Two things for devops

tests/csp-check.mjs is never run by anything. It is referenced only in README.md — no workflow invokes it — and its ROOT is hardcoded to /home/user/markdownwizard, so it would not run outside whatever sandbox it was written in. A CSP checker that never executes is the same shape as the tripwire problem from this morning. Not fixed here; filing it rather than widening this PR.

The support page has no email, on purpose. The family convention is a plain-text support@<domain>. markdownwizard.app does have Forward Email MX and SPF like its siblings, but I could not verify that a support@ alias actually routes, and a support page pointing at a black hole is worse than none. It currently routes to GitHub issues and private security advisories, both verified reachable. That route is coupled to the repo being public, which §10 is about to change — so the alias should be confirmed (or created) before the repo goes private, and I will add it.

🤖 Generated with Claude Code

https://claude.ai/code/session_0126UXfnHngrTg7kqmw87ipV

MichalAFerber and others added 3 commits September 3, 2026 16:47
DS §9 requires every public project to carry a Privacy Policy, Terms of Use,
and — for an end-user product — a Docs page. markdownwizard.app had none of
them: /privacy, /terms, /docs, and /support all returned 404, verified against
a negative control so the result is a real absence and not a host answering
everything.

The pages are written rather than inherited. markdownwizard is not in the
wizard-web monorepo and is not migrating into it — the one branch that looked
like a migration, claude/bootstrap-markdownwizard, ends at "Point the
markdownwizard bootstrap at its own repo" — so none of the shared
packages/ui page components are reachable from here. This is a hand-rolled
static site and these are hand-rolled static pages that match it.

They are also written to the facts of THIS product rather than copied from the
family template, because the family template would have said things that are
not true here. It routes contact through a mailer-backed form, which this
site's CSP (`form-action 'none'`) forbids and which has no backend to reach.
Its Terms declare an MIT open-source product, which is true today but is
exactly what REGISTRY line 32 records as owed a change. Neither claim is made.

One disclosure the template would not have produced at all: `img-src https:`
means a remote image referenced in your Markdown really is fetched, which tells
that host your IP. §9 requires the policy to say what leaves and where it goes
where it cannot say "nothing", so the Privacy Policy says it plainly instead of
claiming an absolute the code contradicts.

Reachability was the other half. The editor is a single-page app shell whose
<footer> is the formatting toolbar, so there was nowhere to hang a site link;
the pages now sit on the toolbar's trailing edge, past the word count, where
the existing horizontal scroll keeps them reachable on a narrow screen. `body`
is `overflow: hidden` for the split layout, so the content pages bring their
own scroll container rather than that rule being relaxed underneath the editor.
They reuse `.sheet.md-body`, the same styling the preview renders into, so
there is one prose stylesheet to keep correct rather than two.

And 404.html said "Markdown Wizard is a single page, so the editor is the only
place to go," which stopped being true in this commit.

The e2e suite now checks all of it — that each page renders, declares its OWN
canonical, can scroll, carries the nav, is linked from the editor, and is in
the sitemap. Four pages built from one template will cheerfully ship four
identical canonical URLs, and nothing else in the suite loads these pages.

DMCA is deliberately not added: §9 requires it when user data is stored
somewhere other than the user's device, and nothing here is. There is no
hosted content to take down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126UXfnHngrTg7kqmw87ipV
Not for merge; reverted in the next commit. The guard added in this branch has
never been watched failing, which makes its green indistinguishable from a
green it would return no matter what. This is the realistic defect it exists to
catch: four pages built from one template, one of them keeping the canonical it
was copied from.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126UXfnHngrTg7kqmw87ipV
Calibration run 33805551669 went red with "FAIL - /terms declares its own
canonical [https://markdownwizard.app/privacy]" and the other seventeen checks
stayed green, so the guard is both wired up and specific -- it names the wrong
value rather than reporting a blanket failure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126UXfnHngrTg7kqmw87ipV

@MichalAFerber MichalAFerber left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stands in for an APPROVE. GitHub refuses --approve from the account that opened the PR.

You overturned the premise of the finding you were sent to act on

I briefed you that the wizard-web migration would deliver three of four §9 pages "nearly free," and that only Docs needed writing. Both halves were wrong, and you established it rather than inheriting it.

There is no monorepo migration. markdownwizard is not in wizard-web and is not moving there — the branch that looks like one ends at "Point the markdownwizard bootstrap at its own repo." It is a hand-rolled static site with no Astro and no root package.json, so packages/ui is unreachable from it. Nothing came free. All four pages were written.

And the DocsPage observation was a red herring. All seven monorepo apps carry their own src/pages/docs.astro because docs are product-specific — Docs was never falling out of a component for anybody. DmcaPage.astro does exist, so the shared set is four rather than three.

That correction matters beyond this PR: Kim's finding, my brief, and the scoping all rested on a migration that is not happening.

The third correction is the one that would have shipped a false claim. The family template routes contact through a mailer form this site's CSP (form-action 'none') forbids and has no backend for, and its Terms declare an MIT product — which is exactly what REGISTRY line 32 records as owed a §10 change. Copying the template would have published two assertions that are untrue here. Neither is made.

The DMCA omission, and why I am endorsing it

You omitted DMCA on the grounds that §9 requires it only when data is stored off-device, and nothing here is. That is the same unlitigated §9 edge Kim filed for favoritespage as 6hPpVHJ4fJ9VXf6F, where devops ruled "Kim can litigate the edge; do not build pages on an unlitigated rule."

You have now decided it the same way, from the same clause, in a second repo — and for a product where the argument is cleaner, since markdownwizard stores nothing off-device at all. Flagging it as a deliberate divergence rather than an omission is what makes it reviewable. I am carrying it to Michal as a second data point on that clause rather than treating it as settled by two agents agreeing.

The guard was watched failing, specifically

18/18 green as written; canonical on terms.html pointed at /privacy → red with FAIL - /terms declares its own canonical; reverted → green, byte-identical. The other seventeen stayed green, so the failure is specific rather than blanket — which is the half of a mutation test people usually skip. A guard that goes red for everything proves only that it runs.

And the negative control on the original finding — /definitely-not-real-9f3x/ also 404s — is what makes "these pages are absent" a measurement rather than a failed fetch.

Reachability was the harder half

The editor's <footer> being the formatting toolbar, with body at overflow: hidden, means there was nowhere to link from — so shipping four pages nobody can reach would have satisfied §9 on paper and nothing in practice. Noticing that 404.html said "Markdown Wizard is a single page" and had stopped being true is the same class of catch.

Two open, and one is a dependency Michal must clear

tests/csp-check.mjs is never run by anything — README-only, with ROOT hardcoded to /home/user/markdownwizard. That is the tenth instance today of a control that is green because it never runs, and filing it rather than widening this PR was right.

The support route is coupled to the repo being public, and §10 is about to change that. Routing to GitHub issues and private security advisories is correct today and both verify 200, but privatization breaks it. Declining to publish a support@ address you could not prove routes is the right call — a support page pointing at a black hole is worse than none. I am putting the alias question to Michal as a prerequisite of privatization, not a follow-up.

@MichalAFerber
MichalAFerber marked this pull request as ready for review September 3, 2026 21:17
@MichalAFerber

Copy link
Copy Markdown
Owner Author

Ready for review — attested against 44d23ce.

Lesli reviewed for correctness and I am not re-deriving it. Two full independent reads of one diff buy the same evidence twice; I read hers and checked the diff against what is mine alone.

§11 plumbing, which is the classic omission when four public pages land at once and is the first thing I looked for. It is not here: sitemap-0.xml carries all four new routes plus root, sitemap-index.xml exists, and robots.txt advertises the index. Worth noting my first check asked about sitemap.xml, got a zero, and that zero was ambiguous between "empty file" and "no such file" — the real file is sitemap-0.xml behind an index. Enumerating the tree disambiguated it. A §11 finding built on that first answer would have been fabricated.

Nothing in the rest of my gate is touched: no deploy path, no secret, no binding, no D1, no registry row moved.

One thing I am taking as a gate item rather than a note

The support route works only because this repo is public, and §10 privatization will break it. GitHub issues and private security advisories both verify 200 today and are the correct choice today. Privatizing markdownwizard turns both into 404s for the people the page exists to serve, and nothing in the privatization step would announce it — the page keeps rendering, the links keep looking like links.

So this is an ordering constraint, not a follow-up: support@markdownwizard.app must exist and be proven to route before the §10 change lands, or the privatization takes the support route down with it. Michal's call on the alias; the ordering is mine and I am recording it against the §10 work rather than leaving it in a PR comment nobody reads at the moment it matters.

Declining to publish a support@ address that could not be proven to route was right. A support page pointing at a black hole is worse than no support page — it converts a visible gap into a silent one.

On the DMCA divergence

Second repo, same §9 clause, decided the same way, and flagged as a deliberate divergence rather than dropped — which is what makes it reviewable at all. My standing ruling holds: do not build pages on an unlitigated rule. Two agents reading a clause the same way is not the clause being settled, so it goes to Michal as a second data point, not as precedent. Endorsing Lesli carrying it up.

tests/csp-check.mjs being invoked by nothing is real and is fixed by markdownwizard#8, which I flipped ready separately. The two do not depend on each other.

Michal merges.

@MichalAFerber
MichalAFerber merged commit ddd50c8 into main Sep 3, 2026
2 checks passed
@MichalAFerber
MichalAFerber deleted the feat/ds9-required-pages branch September 3, 2026 22:43
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.

1 participant