Skip to content
This repository was archived by the owner on Sep 28, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1aa08c5
fix(public): render the signup rejection signal on the homepage
nish3451 Aug 11, 2026
2b24065
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 11, 2026
ea2e470
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
b98de30
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
6d09442
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
745e3f8
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
afbbee3
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
1eb7ced
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
b67e0af
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
e1a3609
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
41ad5c4
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
2aa7b9b
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
a0934f3
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
a35fc49
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
239c11d
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
334d811
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
4e52cbb
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 12, 2026
4184cac
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
8b2bcc7
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
8605a61
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
0c6f353
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
b07b208
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
aa0b80d
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
82d0471
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
95069d8
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
8dd42d1
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
92e4dba
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
214d962
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
b91d03e
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
5e4a84b
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
fbb3a31
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
0c57442
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
a6a5b12
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
562d708
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
139291e
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
2d0571e
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
4d4a764
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
d639aa3
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
d3cb29a
Merge branch 'main' into fix/signup-signal-error-banner
nish3451 Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions public/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
box-shadow:0 6px 18px -8px rgba(25,20,16,.6);
transition:transform var(--mid) var(--ease),box-shadow var(--mid) var(--ease)}
form button:hover{transform:translateY(-1.5px)}
.signal{display:block;margin:18px auto 0;max-width:500px;padding:10px 18px;font-size:14px;line-height:1.6;
color:var(--sig);background:rgba(255,253,248,.74);border:1px solid rgba(176,74,46,.35);
border-radius:12px;text-align:center}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve a visible focus indicator on the alert

When a sighted keyboard user submits an email rejected by the server, index.js moves focus to this banner, but this rule removes the only focus indicator without providing an alternative. The user therefore cannot visually determine where focus landed after the redirect; keep the outline or replace it with an equally visible :focus treatment.

Useful? React with 👍 / 👎.

.signal[hidden]{display:none}
.signal:focus{outline:none}
.micro{margin-top:16px;font-size:13.5px;color:var(--muted)}
.meta{margin:54px auto 0;max-width:900px;display:grid;grid-template-columns:repeat(3,1fr);
border-top:1px solid var(--hair);padding-top:22px}
Expand Down
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<div class="orn"><i></i><span class="sc">The appraisal</span><b></b><i></i></div>
<h1>Most of them leave <em>before they ever get in touch.</em></h1>
<p class="sub">We read the one page your revenue depends on the way a customer with intent reads it, and show you the exact points at which they go.</p>
<p class="signal" id="signal-invalid" role="alert" tabindex="-1" hidden>We could not save that request — the email address did not pass. Check it and try again.</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Provide rejection feedback without JavaScript

When JavaScript is disabled or index.js fails to load, an invalid HTML form submission still redirects through htmlRedirect() to /?signal=invalid, but this element remains hidden forever, leaving the same silent bounce this change is intended to fix. The native form and Worker explicitly support no-JavaScript submissions, so the redirected response needs a server-rendered or <noscript> rejection message rather than relying exclusively on script to remove hidden.

Useful? React with 👍 / 👎.

<form class="lead two" id="start" action="/api/signups" method="post">
<input type="text" name="website" required inputmode="url" autocomplete="url" placeholder="yourwebsite.com" aria-label="Your website domain" pattern="(https?://)?([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,}(:[0-9]+)?(/[^\s]*)?" title="Enter your domain, like example.com">
<input type="email" name="email" required placeholder="Your work email" aria-label="Your work email">
Expand Down
18 changes: 18 additions & 0 deletions public/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,21 @@
}
document.readyState==='loading'?document.addEventListener('DOMContentLoaded',run):run();
})();

// Signup rejection signal: the worker 303-redirects failed signups back to
// /?signal=invalid (the homepage form and the /audit form both post to
// /api/signups). No page code used to read that signal, so a visitor whose
// email the server rejected (the server regex is stricter than the browser's
// type=email check — e.g. "a@b" passes client-side but not server-side) was
// silently bounced to the homepage with no explanation. Reveal the banner,
// move focus to it for assistive tech, then strip the query so a refresh
// (or a copied link) does not re-show the error.
(function(){
var match=(location.search||'').match(/[?&]signal=([^&]+)/);
if(!match)return;
Comment on lines +30 to +31

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate the signal value before showing rejection

The handler checks only that a nonempty signal parameter exists, so /?signal=saved or /?signal=anything displays the invalid-email alert. This particularly affects successful-submission URLs retained in history from when htmlRedirect() used the saved signal; validate that the decoded parameter is exactly invalid before revealing the banner.

Useful? React with 👍 / 👎.

var banner=document.getElementById('signal-invalid');
if(!banner)return;
banner.hidden=false;
banner.focus();
if(history.replaceState)history.replaceState(null,'',location.pathname+location.hash);
})();
40 changes: 40 additions & 0 deletions scripts/check-site.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,46 @@ for (const [pageName, pageHtml] of [["homepage", siteHome], ["audit page", siteA
}
}

// Signup rejection-signal guard (dogfood: failed-signup dead end). The worker
// 303-redirects a rejected signup back to /?signal=invalid (the server email
// regex is stricter than the browser's type=email check — e.g. "a@b" passes
// client-side but not server-side), so the homepage must render that state:
// a role=alert banner, hidden until index.js reads the signal, moves focus
// into it, and strips the query so a refresh or a copied link does not
// re-show the error. These are STATIC SOURCE GUARDS, not behavioral tests.
const signalBanner = siteHome.match(/<p\b[^>]*id="signal-invalid"[^>]*>/i)?.[0] || "";
if (!signalBanner) {
failures.push("Homepage must carry the id=\"signal-invalid\" signup rejection banner.");
} else {
if (!/\brole="alert"/i.test(signalBanner)) {
failures.push("Signup rejection banner must expose role=\"alert\".");
}
if (!/\bhidden(?:\s|>|=)/i.test(signalBanner)) {
failures.push("Signup rejection banner must start hidden (revealed only by the signal handler).");
}
}
const homeScript = read("public/index.js");
if (!homeScript.includes("signal-invalid")) {
failures.push("index.js must reference the signal-invalid banner.");
}
if (!/signal=([^&]+)/.test(homeScript)) {
failures.push("index.js must read the ?signal= query parameter.");
Comment on lines +315 to +319

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exercise the rejection handler instead of matching tokens

These checks never verify that the banner is revealed or focused, so a regression such as changing banner.hidden=false to banner.hidden=true while leaving the query parsing and element lookup intact still passes npm test; the worker test only covers the redirect, meaning invalid submissions can silently lose their feedback again with a green suite. Add a behavioral DOM test or source assertions that specifically cover the reveal and focus operations.

Useful? React with 👍 / 👎.

}
if (!homeScript.includes("replaceState")) {
failures.push("index.js must strip the signal query with history.replaceState after revealing the banner.");
}
const homeCss = read("public/index.css");
if (!/\.signal\b/.test(homeCss)) {
failures.push("index.css must style the signup rejection banner (.signal).");
}
if (!/\.signal\[hidden\]\s*\{[^}]*display:\s*none/i.test(homeCss)) {
failures.push(".signal[hidden] must force display:none so the banner stays hidden until revealed.");
}
if (!worker.includes("htmlRedirect(url, \"invalid\")")) {
failures.push("Worker must keep 303-redirecting rejected signups to /?signal=invalid (the homepage banner renders it).");
}


if (!retiredDesk.includes("role=\"tabpanel\"") || !retiredDesk.includes("aria-labelledby=\"output-tab-pipelineBrief\"")) {
failures.push("Agent output must expose a proper tabpanel relationship.");
}
Expand Down
28 changes: 28 additions & 0 deletions scripts/test-agent-worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,34 @@ test("legacy /api/agent-audit still labels its rows with the retired self-serve
assert.equal(runInsert.values[2], "agent-self-serve", "the legacy run keeps its own source label");
});

test("signup handler redirects a rejected email to /?signal=invalid so the homepage can render it", async () => {
// The browser's type=email accepts "a@b", but the worker's stricter regex
// requires a dot in the domain. The rejection must 303 back to the homepage
// with ?signal=invalid (rendered by public/index.js), not fail silently.
const db = new FakeDB();
const env = { DB: db, AI: new FakeAI("") };
const res = await worker.fetch(
new Request("https://tinystudio.io/api/signups", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://tinystudio.io",
"Accept": "text/html",
"User-Agent": "tinystudio-worker-test"
},
body: new URLSearchParams({ website: "example.com", email: "a@b" }).toString()
}),
env
);

assert.equal(res.status, 303);
const location = new URL(res.headers.get("Location"));
assert.equal(location.pathname, "/");
assert.equal(location.searchParams.get("signal"), "invalid");
const insert = db.calls.find((call) => call.sql.includes("INSERT INTO email_signups"));
assert.equal(insert, undefined, "rejected signup must not persist a row");
});

test("worker serves the same-origin font promotion script (render-blocking fix b8f6046e942a)", async () => {
// The production CSP (script-src 'self', no unsafe-inline) blocks inline
// onload handlers, so the pages promote the preloaded Google Fonts css2
Expand Down
Loading