Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions public/agents.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@
<body>
<div class="wrap">
<nav>
<a class="logo" href="index.html">Tiny<span>Studio</span></a>
<a class="logo" href="/">Tiny<span>Studio</span></a>
<div class="navlinks">
<a href="audit.html">The appraisal</a>
<a href="agents.html" aria-current="page">The desk</a>
<a href="pricing.html">Pricing</a>
<a class="navcta" href="index.html#start">Request the appraisal</a>
<a href="/audit">The appraisal</a>
<a href="/agents" aria-current="page">The desk</a>
<a href="/pricing">Pricing</a>
<a class="navcta" href="/#start">Request the appraisal</a>
Comment on lines +65 to +70

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

for file in public/index.html public/audit.html public/agents.html public/pricing.html public/specimen.html; do
  if rg -n 'og:url|`@id`|^[[:space:]]*"url"' "$file" | rg -q 'tinystudio\.io/[^"]*\.html'; then
    printf 'stale public URL metadata: %s\n' "$file" >&2
    exit 1
  fi
done

Repository: nish3451/TinyStudio.io

Length of output: 205


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- repository root ---'
find . -maxdepth 1 -type f -printf '%f\n' | sort

printf '%s\n' '--- plan candidates ---'
fd -HI -t f '(^|/)(plan|PLAN|readme|README)([^/]*|/.*)$' . | head -80

printf '%s\n' '--- tracked target files ---'
git ls-files public/index.html public/audit.html public/agents.html public/pricing.html public/specimen.html

printf '%s\n' '--- metadata and navigation references ---'
rg -n -C 3 'og:url|application/ld\+json|`@id`|^[[:space:]]*"url"|href="/(audit|agents|pricing)|canonical|tinystudio\.io' public/index.html public/audit.html public/agents.html public/pricing.html public/specimen.html

Repository: nish3451/TinyStudio.io

Length of output: 38301


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- repository guidance ---'
cat AGENTS.md
printf '%s\n' '--- project overview ---'
cat README.md

printf '%s\n' '--- plan references to routes and metadata ---'
rg -n -i -C 3 'clean route|pretty route|canonical|metadata|og:url|agents|audit|pricing|specimen' specs/*/plan.md

printf '%s\n' '--- deployment and route-related files ---'
git ls-files | rg '(^|/)(wrangler|worker|server|functions|routes|public|src|app)' | head -160
cat wrangler.jsonc

Repository: nish3451/TinyStudio.io

Length of output: 7647


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- plan files ---'
for file in specs/*/plan.md; do
  printf '\n### %s\n' "$file"
  cat "$file"
done

printf '%s\n' '--- worker outline ---'
ast-grep outline src/worker.js

printf '%s\n' '--- worker route handling ---'
rg -n -C 8 'pathname|/audit|/agents|/pricing|/specimen|\.html|ASSETS|fetch\(' src/worker.js

printf '%s\n' '--- sitemap URLs ---'
cat public/sitemap.xml

Repository: nish3451/TinyStudio.io

Length of output: 19864


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import json
import re

pages = {
    "public/index.html": "/",
    "public/audit.html": "/audit",
    "public/agents.html": "/agents",
    "public/pricing.html": "/pricing",
    "public/specimen.html": "/specimen",
}

for filename, route in pages.items():
    text = Path(filename).read_text()
    html_urls = re.findall(r'https://tinystudio\.io/[^"\s<]+\.html(?:#\w+)?', text)
    og = re.findall(r'<meta property="og:url" content="([^"]+)"', text)
    jsonld = re.findall(r'<script type="application/ld\+json">\s*(.*?)\s*</script>', text, re.S)
    webpages = []
    for block in jsonld:
        graph = json.loads(block).get("`@graph`", [])
        webpages.extend(item for item in graph if item.get("`@type`") == "WebPage")

    print(filename)
    print("  expected public route:", route)
    print("  extensionless route exposed by worker:", route in {
        "/", "/audit", "/agents", "/pricing", "/specimen"
    })
    print("  og:url:", og)
    print("  WebPage `@id`:", [item.get("`@id`") for item in webpages])
    print("  WebPage url:", [item.get("url") for item in webpages])
    print("  .html metadata URLs:", html_urls)
PY

printf '%s\n' '--- route and metadata references ---'
rg -n -i 'canonical|clean route|extensionless|\.html|href="/(audit|agents|pricing|specimen)' README.md AGENTS.md specs public src wrangler.jsonc \
  -g '!public/*.js' -g '!public/*.css' -g '!public/*.png' -g '!public/*.svg'

printf '%s\n' '--- focused change summary ---'
git diff --stat -- public/index.html public/audit.html public/agents.html public/pricing.html public/specimen.html src/worker.js wrangler.jsonc

Repository: nish3451/TinyStudio.io

Length of output: 19590


Align public routes and metadata.

The worker serves both route forms, but navigation uses extensionless paths while og:url and JSON-LD WebPage URLs use .html in public/audit.html, public/agents.html, public/pricing.html, and public/specimen.html. Choose one canonical route scheme and apply it to the navigation, metadata, and related public references.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/agents.html` around lines 65 - 70, Choose a single canonical route
scheme for the public pages, then apply it consistently to the navigation links
in the public HTML files and all related og:url and JSON-LD WebPage URL values,
including audit, agents, pricing, and specimen. Update other public references
to use the same scheme while preserving the worker’s support for both route
forms.

</div>
</nav>

Expand Down Expand Up @@ -154,7 +154,7 @@ <h2>Every week the page undersells you is a week of traffic you already paid for

<footer>
<span class="sc">TinyStudio · tinystudio.io</span>
<span class="sc"><a href="audit.html">The appraisal</a> · <a href="pricing.html">Pricing</a></span>
<span class="sc"><a href="/audit">The appraisal</a> · <a href="/pricing">Pricing</a></span>
<span class="sc">No revenue, ranking or booking guarantees. Only the work.</span>
</footer>
</div>
Expand Down
14 changes: 7 additions & 7 deletions public/audit.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@
<body>
<div class="wrap">
<nav>
<a class="logo" href="index.html">Tiny<span>Studio</span></a>
<a class="logo" href="/">Tiny<span>Studio</span></a>
<div class="navlinks">
<a href="audit.html" aria-current="page">The appraisal</a>
<a href="specimen.html">Specimen</a><a href="agents.html">The desk</a>
<a href="pricing.html">Pricing</a>
<a href="/audit" aria-current="page">The appraisal</a>
<a href="/specimen">Specimen</a><a href="/agents">The desk</a>
<a href="/pricing">Pricing</a>
<a class="navcta" href="#start">Request the appraisal</a>
</div>
</nav>
Expand Down Expand Up @@ -133,18 +133,18 @@ <h2>What lands in your inbox</h2>

<section>
<h2>Then it is entirely your call</h2>
<p class="lede">Most of what we find is fixable by a competent developer in an afternoon — including one who is not us. If you would rather have it closed than merely found, the desk runs at $2,500 a month on a three-month minimum, with a delivery guarantee behind it. <a class="xa1" href="pricing.html">See the terms →</a></p>
<p class="lede">Most of what we find is fixable by a competent developer in an afternoon — including one who is not us. If you would rather have it closed than merely found, the desk runs at $2,500 a month on a three-month minimum, with a delivery guarantee behind it. <a class="xa1" href="/pricing">See the terms →</a></p>
</section>


<section id="confidential">
<h2>Why there are no logos on this site</h2>
<p class="lede">We do not publish client work. No logos, no case studies, no testimonials, no "as seen at." If you engage us, nobody learns it from us — not on a site, not in a pitch deck, not in a conversation with your competitor down the road.<br><br><b>Market research is the opposite, and we share it freely.</b> The <span data-study="readable_word">eighty-nine</span>-site study reads public homepages of businesses that are not our clients and have no relationship with us — you are welcome to all of it, industry by industry. What we never share is anything belonging to someone who paid us. <a class="xa1" href="specimen.html">Read the specimen &rarr;</a></p>
<p class="lede">We do not publish client work. No logos, no case studies, no testimonials, no "as seen at." If you engage us, nobody learns it from us — not on a site, not in a pitch deck, not in a conversation with your competitor down the road.<br><br><b>Market research is the opposite, and we share it freely.</b> The <span data-study="readable_word">eighty-nine</span>-site study reads public homepages of businesses that are not our clients and have no relationship with us — you are welcome to all of it, industry by industry. What we never share is anything belonging to someone who paid us. <a class="xa1" href="/specimen">Read the specimen &rarr;</a></p>
</section>

<footer>
<span class="sc">TinyStudio · tinystudio.io</span>
<span class="sc"><a href="agents.html">The desk</a> · <a href="pricing.html">Pricing</a></span>
<span class="sc"><a href="/agents">The desk</a> · <a href="/pricing">Pricing</a></span>
<span class="sc">No revenue, ranking or booking guarantees. Only the work.</span>
</footer>
</div>
Expand Down
6 changes: 3 additions & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
<body>
<div class="wrap">
<nav>
<a class="logo" href="index.html">Tiny<span>Studio</span></a>
<div class="navlinks"><a href="audit.html">The appraisal</a><a href="agents.html">The desk</a><a href="pricing.html">Pricing</a><a class="navcta" href="#start">Request the appraisal</a></div>
<a class="logo" href="/">Tiny<span>Studio</span></a>
<div class="navlinks"><a href="/audit">The appraisal</a><a href="/agents">The desk</a><a href="/pricing">Pricing</a><a class="navcta" href="#start">Request the appraisal</a></div>
</nav>

<header>
Expand Down Expand Up @@ -238,7 +238,7 @@ <h2>Before you ask</h2>

<section id="confidential">
<h2>Why there are no logos on this site</h2>
<p class="lede">We do not publish client work. No logos, no case studies, no testimonials, no "as seen at." If you engage us, nobody learns it from us — not on a site, not in a pitch deck, not in a conversation with your competitor down the road.<br><br><b>Market research is the opposite, and we share it freely.</b> The <span data-study="readable_word">eighty-nine</span>-site study reads public homepages of businesses that are not our clients and have no relationship with us — you are welcome to all of it, industry by industry. What we never share is anything belonging to someone who paid us. <a class="xi19" href="specimen.html">Read the specimen &rarr;</a></p>
<p class="lede">We do not publish client work. No logos, no case studies, no testimonials, no "as seen at." If you engage us, nobody learns it from us — not on a site, not in a pitch deck, not in a conversation with your competitor down the road.<br><br><b>Market research is the opposite, and we share it freely.</b> The <span data-study="readable_word">eighty-nine</span>-site study reads public homepages of businesses that are not our clients and have no relationship with us — you are welcome to all of it, industry by industry. What we never share is anything belonging to someone who paid us. <a class="xi19" href="/specimen">Read the specimen &rarr;</a></p>
</section>

<footer>
Expand Down
16 changes: 8 additions & 8 deletions public/pricing.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@
<body>
<div class="wrap">
<nav>
<a class="logo" href="index.html">Tiny<span>Studio</span></a>
<a class="logo" href="/">Tiny<span>Studio</span></a>
<div class="navlinks">
<a href="audit.html">The appraisal</a>
<a href="agents.html">The desk</a>
<a href="pricing.html" aria-current="page">Pricing</a>
<a class="navcta" href="index.html#start">Request the appraisal</a>
<a href="/audit">The appraisal</a>
<a href="/agents">The desk</a>
<a href="/pricing" aria-current="page">Pricing</a>
<a class="navcta" href="/#start">Request the appraisal</a>
</div>
</nav>

Expand Down Expand Up @@ -121,7 +121,7 @@ <h2>Before you ask</h2>
<div class="q"><h3>Why only six audits a month?</h3>
<p>They are done by hand and we are also delivering paid work. Six is what we can do properly. When the month is full you go on the list for the next one.</p></div>
<div class="q"><h3>Who actually does the work?</h3>
<p>Seven specialist agents do the research, drafting and checking. A person reviews fit, every claim, all client-facing work, delivery and renewal. Automation never sends, publishes, spends or approves. <a class="xp1" href="agents.html">See the desk →</a></p></div>
<p>Seven specialist agents do the research, drafting and checking. A person reviews fit, every claim, all client-facing work, delivery and renewal. Automation never sends, publishes, spends or approves. <a class="xp1" href="/agents">See the desk →</a></p></div>
</section>

<div class="band">
Expand All @@ -133,12 +133,12 @@ <h2>The appraisal costs you an email. The rest is a decision you can make later.

<section id="confidential">
<h2>Why there are no logos on this site</h2>
<p class="lede">We do not publish client work. No logos, no case studies, no testimonials, no "as seen at." If you engage us, nobody learns it from us — not on a site, not in a pitch deck, not in a conversation with your competitor down the road.<br><br><b>Market research is the opposite, and we share it freely.</b> The <span data-study="readable_word">eighty-nine</span>-site study reads public homepages of businesses that are not our clients and have no relationship with us — you are welcome to all of it, industry by industry. What we never share is anything belonging to someone who paid us. <a class="xp1" href="specimen.html">Read the specimen &rarr;</a></p>
<p class="lede">We do not publish client work. No logos, no case studies, no testimonials, no "as seen at." If you engage us, nobody learns it from us — not on a site, not in a pitch deck, not in a conversation with your competitor down the road.<br><br><b>Market research is the opposite, and we share it freely.</b> The <span data-study="readable_word">eighty-nine</span>-site study reads public homepages of businesses that are not our clients and have no relationship with us — you are welcome to all of it, industry by industry. What we never share is anything belonging to someone who paid us. <a class="xp1" href="/specimen">Read the specimen &rarr;</a></p>
</section>

<footer>
<span class="sc">The Tiny Studio</span>
<span class="sc"><a href="audit.html">The appraisal</a> · <a href="agents.html">The desk</a></span>
<span class="sc"><a href="/audit">The appraisal</a> · <a href="/agents">The desk</a></span>
<span class="sc">No revenue, ranking or booking guarantees. Only the work.</span>
</footer>
</div>
Expand Down
12 changes: 6 additions & 6 deletions public/specimen.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@
<body>
<div class="wrap">
<nav>
<a class="logo" href="index.html">Tiny<span>Studio</span></a>
<a class="logo" href="/">Tiny<span>Studio</span></a>
<div class="navlinks">
<a href="audit.html">The appraisal</a>
<a href="agents.html">The desk</a>
<a href="pricing.html">Pricing</a>
<a class="navcta" href="index.html#start">Request the appraisal</a>
<a href="/audit">The appraisal</a>
<a href="/agents">The desk</a>
<a href="/pricing">Pricing</a>
<a class="navcta" href="/#start">Request the appraisal</a>
</div>
</nav>

Expand Down Expand Up @@ -131,7 +131,7 @@ <h2>Confidentiality</h2>

<footer>
<span class="sc">TinyStudio · tinystudio.io</span>
<span class="sc"><a href="audit.html">The appraisal</a> · <a href="pricing.html">Pricing</a></span>
<span class="sc"><a href="/audit">The appraisal</a> · <a href="/pricing">Pricing</a></span>
<span class="sc">No revenue, ranking or booking guarantees. Only the work.</span>
</footer>
</div>
Expand Down
39 changes: 39 additions & 0 deletions scripts/check-site.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,45 @@ for (const [pageName, pageHtml, pageUrl] of structuredDataPages) {
}
}

// ---- Internal page links (dogfood 996dffe45ef7) ---------------------------
// The leak audit this site sells flags a homepage whose internal links do not
// point at the final destination URL: the dogfood run reported every .html
// navigation target on home ("index.html" -> "/", "audit.html" -> "/audit",
// "agents.html" -> "/agents", "pricing.html" -> "/pricing", "specimen.html" ->
// "/specimen") as a redirecting internal link. The five public pages must
// therefore point every page link at the clean URL the worker serves, never
// at a .html file that resolves to it. These are STATIC SOURCE GUARDS (regex
// over the served files): CI has no browser, so they assert the .html target
// shape cannot return, not that the redirects are absent on the network.
const internalLinkPages = [
["homepage", siteHome],
["audit page", siteAudit],
["desk page", read("public/agents.html")],
["pricing page", read("public/pricing.html")],
["specimen page", read("public/specimen.html")]
Comment on lines +1259 to +1261

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 Include the confirmation page in the clean-link guard

After a successful HTML signup, the worker redirects users to /brief-requested, but that served page still links to index.html, audit.html, agents.html, and pricing.html (including its back link). Because internalLinkPages stops at the specimen page, npm test passes while clicks from the confirmation page continue through the redirects this change is intended to eliminate; migrate public/brief-requested.html to clean URLs and include it in this guard.

Useful? React with 👍 / 👎.

];

const htmlPageTargets = {
"index.html": "/",
"audit.html": "/audit",
"agents.html": "/agents",
"pricing.html": "/pricing",
"specimen.html": "/specimen"
};

for (const [pageName, pageHtml] of internalLinkPages) {
const anchors = [...pageHtml.matchAll(/<a\b[^>]*>/gi)].map((match) => match[0]);
for (const anchor of anchors) {
const href = anchor.match(/\bhref="([^"]*)"/i)?.[1] ?? "";
const target = href.split("#")[0];
if (Object.prototype.hasOwnProperty.call(htmlPageTargets, target)) {
failures.push(
`Internal page link on ${pageName} must point at the clean destination ${JSON.stringify(htmlPageTargets[target])} (found ${JSON.stringify(href)}).`
);
Comment on lines +1272 to +1280

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize href values before checking old page targets.

The guard only matches href="..." and only compares bare audit.html-style values. Valid internal links such as href='/audit.html', href="/audit.html?source=nav", and href=./audit.html can bypass this check. Normalize same-origin URL paths before comparing them with htmlPageTargets, and add regression cases for these forms.

Proposed guard hardening
   for (const [pageName, pageHtml] of internalLinkPages) {
     const anchors = [...pageHtml.matchAll(/<a\b[^>]*>/gi)].map((match) => match[0]);
     for (const anchor of anchors) {
-      const href = anchor.match(/\bhref="([^"]*)"/i)?.[1] ?? "";
-      const target = href.split("#")[0];
+      const hrefMatch = anchor.match(
+        /(?:^|\s)href\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'`=<>]+))/i
+      );
+      const href = hrefMatch?.[1] ?? hrefMatch?.[2] ?? hrefMatch?.[3] ?? "";
+      let target;
+      try {
+        const url = new URL(href, "https://tinystudio.io/");
+        if (url.origin !== "https://tinystudio.io") continue;
+        target = url.pathname.replace(/^\/+/, "");
+      } catch {
+        continue;
+      }
       if (Object.prototype.hasOwnProperty.call(htmlPageTargets, target)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const [pageName, pageHtml] of internalLinkPages) {
const anchors = [...pageHtml.matchAll(/<a\b[^>]*>/gi)].map((match) => match[0]);
for (const anchor of anchors) {
const href = anchor.match(/\bhref="([^"]*)"/i)?.[1] ?? "";
const target = href.split("#")[0];
if (Object.prototype.hasOwnProperty.call(htmlPageTargets, target)) {
failures.push(
`Internal page link on ${pageName} must point at the clean destination ${JSON.stringify(htmlPageTargets[target])} (found ${JSON.stringify(href)}).`
);
for (const [pageName, pageHtml] of internalLinkPages) {
const anchors = [...pageHtml.matchAll(/<a\b[^>]*>/gi)].map((match) => match[0]);
for (const anchor of anchors) {
const hrefMatch = anchor.match(
/(?:^|\s)href\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'`=<>]+))/i
);
const href = hrefMatch?.[1] ?? hrefMatch?.[2] ?? hrefMatch?.[3] ?? "";
let target;
try {
const url = new URL(href, "https://tinystudio.io/");
if (url.origin !== "https://tinystudio.io") continue;
target = url.pathname.replace(/^\/+/, "");
} catch {
continue;
}
if (Object.prototype.hasOwnProperty.call(htmlPageTargets, target)) {
failures.push(
`Internal page link on ${pageName} must point at the clean destination ${JSON.stringify(htmlPageTargets[target])} (found ${JSON.stringify(href)}).`
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/check-site.mjs` around lines 1244 - 1252, Update the anchor href
extraction and target comparison in the internalLinkPages validation loop to
accept single-quoted, double-quoted, and unquoted href attributes, then
normalize same-origin paths by removing leading ./ or / and excluding query
strings and fragments before checking htmlPageTargets. Preserve the existing
failure message and add regression cases covering /audit.html,
/audit.html?source=nav, and ./audit.html.

}
}
}

for (const migration of ["migrations/0002_agent_runs.sql", "migrations/0003_agent_usage_limits.sql"]) { if (!existsSync(new URL(`../${migration}`, import.meta.url))) {
failures.push(`Missing migration: ${migration}`);
continue;
Expand Down
Loading