From a0dcf4f2f8980c9393a33ac702f6568fe4a31a8a Mon Sep 17 00:00:00 2001
From: Nish <257724087+nish3451@users.noreply.github.com>
Date: Wed, 12 Aug 2026 04:42:11 +0530
Subject: [PATCH 1/3] fix(public): replace residual editorial and
submission-prep copy on the four app pages
The Drishti and Promptly product pages still carried the editor-facing
'Where to go next' eyebrow (residual from PR #28), and both app privacy
pages still carried submission-prep copy as visible hero/body copy:
H1s spoke to the launch calendar ('already in place before launch',
'already public ahead of release'), the leads referenced the 'current
planned launch scope' and 'final App Store privacy disclosures before
release', the aside was labeled 'Current release scope' with 'not
publicly released yet', the first card was headed 'At launch', and the
'Important note' told readers both the page and the App Store privacy
answers should be updated before the next submission.
Rewrite those passages as visitor-facing copy that describes what each
page covers and the choices available, without referencing the
submission process. Heading levels, links, meta tags, chips, and
JSON-LD are unchanged.
---
public/drishti/index.html | 2 +-
public/drishti/privacy/index.html | 32 ++++++++++++++---------------
public/promptly/index.html | 2 +-
public/promptly/privacy/index.html | 33 +++++++++++++++---------------
4 files changed, 34 insertions(+), 35 deletions(-)
diff --git a/public/drishti/index.html b/public/drishti/index.html
index 9ef4bf15..16757bbe 100644
--- a/public/drishti/index.html
+++ b/public/drishti/index.html
@@ -187,7 +187,7 @@
Drishti’s privacy page is already in place before launch.
+
Drishti privacy for mindful screen time.
- This is the app-specific privacy destination for Drishti. It
- reflects the current planned launch scope and should stay aligned
- with the final App Store privacy disclosures before release.
+ This page explains what information Tiny Studio may receive
+ through Drishti, how it is used, and the choices you have. Drishti
+ is Tiny Studio’s mindful screen time app for interrupting the
+ scroll reflex.
Effective date: March 14, 2026
@@ -96,10 +97,10 @@
Drishti’s privacy page is already in place before launch.
@@ -107,12 +108,12 @@
Drishti’s privacy page is already in place before launch.
-
At launch
-
No third-party advertising or cross-app tracking is disclosed here.
+
Advertising and tracking
+
No third-party advertising or cross-app tracking.
- This page does not currently describe targeted advertising,
- cross-app tracking, a public sign-in system, or website payments
- for Drishti.
+ This page does not describe targeted advertising, cross-app
+ tracking, a public sign-in system, or website payments for
+ Drishti.
@@ -149,10 +150,9 @@
To respond, improve, and keep records when necessary.
Important note
- If Drishti launches with analytics, crash reporting, accounts,
- sync, or another external service not described here, both this
- page and the App Store privacy answers should be updated before
- that version is submitted.
+ If Drishti adds analytics, crash reporting, accounts, sync, or
+ another external service not described here, this page will be
+ updated to describe it.
Promptly’s privacy page is already public ahead of release.
+
Promptly privacy for bookings, reminders, and client data.
- This is the app-specific privacy destination for Promptly. It
- reflects the current planned launch scope and should stay aligned
- with the final App Store privacy disclosures before release.
+ This page explains what data Promptly handles, how it is used, and
+ the choices you have. Promptly is Tiny Studio’s booking and
+ no-show prevention app for solo professionals.
Effective date: March 14, 2026
@@ -96,10 +96,10 @@
Promptly’s privacy page is already public ahead of release.
@@ -107,12 +107,12 @@
Promptly’s privacy page is already public ahead of release.
-
At launch
-
No third-party advertising or cross-app tracking is planned.
+
Advertising and tracking
+
No third-party advertising or cross-app tracking.
- Promptly is described as a booking workflow for solo professionals.
- This policy does not describe targeted advertising, cross-app
- tracking, or selling personal information.
+ Promptly is a booking workflow for solo professionals. This policy
+ does not describe targeted advertising, cross-app tracking, or
+ selling personal information.
@@ -150,7 +150,7 @@
Booking links, notes, payment proof, and support records.
support, security, legal, or accounting reasons. Service providers
may support hosting, storage, authentication or OTP delivery,
notifications, calendar connectivity, support, and payment-proof
- handling. Promptly should not receive full payment-card numbers.
+ handling. Promptly does not receive full payment-card numbers.
@@ -170,10 +170,9 @@
Booking links, notes, payment proof, and support records.
Important note
- If Promptly launches with analytics, crash reporting, new payment
- processors, expanded account sync, or another external service not
- described here, both this page and the App Store privacy answers
- should be updated before that version is submitted.
+ If Promptly adds analytics, crash reporting, new payment
+ processors, expanded account sync, or another external service
+ not described here, this page will be updated to describe it.
From 3f3221910654dc6f59655734e8a4feda24f56c55 Mon Sep 17 00:00:00 2001
From: Nish <257724087+nish3451@users.noreply.github.com>
Date: Wed, 12 Aug 2026 04:49:01 +0530
Subject: [PATCH 2/3] test(public): guard the four app pages against editorial
and submission-prep copy
The residual editorial copy on the app product and privacy pages was
cleaned up without a regression guard (the earlier footer-copy guard
proposal never merged). Add test-public-app-copy-voice.mjs, wired into
npm test and npm run ci, which fails if any of the four app pages
re-introduces the editorial/submission-prep phrasing (launch-calendar
H1s, planned-scope leads, release-scope asides, at-launch cards,
'Where to go next' eyebrows, submission notes) or drops the
visitor-facing product identity and public support route.
---
package.json | 4 +-
scripts/test-public-app-copy-voice.mjs | 96 ++++++++++++++++++++++++++
2 files changed, 98 insertions(+), 2 deletions(-)
create mode 100644 scripts/test-public-app-copy-voice.mjs
diff --git a/package.json b/package.json
index 6c96a39a..5733d834 100644
--- a/package.json
+++ b/package.json
@@ -86,9 +86,9 @@
"product:truth": "node scripts/check-product-truth.mjs",
"mobbin:run": "node scripts/run-design-system-proving-lab.mjs",
"mobbin:check": "node scripts/run-design-system-proving-lab.mjs --dry-run",
- "ci": "node scripts/test-service-engine.mjs && node scripts/test-sales-intake-contract.mjs && node scripts/test-active-offer-projection.mjs && node scripts/test-active-operator-surfaces.mjs && node scripts/test-direction-proof-gate.mjs && node scripts/test-client-readiness-contract.mjs && node scripts/test-validated-service-client.mjs && node scripts/test-client-acceptance-gates.mjs && node scripts/check-product-truth.mjs && node scripts/check-human-service-kit.mjs && node scripts/test-design-system-proving-lab.mjs && node scripts/check-retention-automation.mjs && node scripts/test-retention-automation.mjs && node scripts/check-agency-defaults.mjs && node scripts/check-outbound-claim-safety.mjs && node scripts/check-outbound-send-readiness.mjs && node scripts/test-outbound-send-readiness.mjs && node scripts/test-public-conversion-signal.mjs && node scripts/test-public-structured-data.mjs && node scripts/test-public-brand-disambiguation.mjs && node scripts/test-public-heading-hierarchy.mjs && node scripts/test-public-link-targets.mjs && node scripts/test-public-social-preview.mjs && node scripts/test-public-soft-404.mjs && node scripts/test-public-deploy-bundle.mjs && find scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
+ "ci": "node scripts/test-service-engine.mjs && node scripts/test-sales-intake-contract.mjs && node scripts/test-active-offer-projection.mjs && node scripts/test-active-operator-surfaces.mjs && node scripts/test-direction-proof-gate.mjs && node scripts/test-client-readiness-contract.mjs && node scripts/test-validated-service-client.mjs && node scripts/test-client-acceptance-gates.mjs && node scripts/check-product-truth.mjs && node scripts/check-human-service-kit.mjs && node scripts/test-design-system-proving-lab.mjs && node scripts/check-retention-automation.mjs && node scripts/test-retention-automation.mjs && node scripts/check-agency-defaults.mjs && node scripts/check-outbound-claim-safety.mjs && node scripts/check-outbound-send-readiness.mjs && node scripts/test-outbound-send-readiness.mjs && node scripts/test-public-conversion-signal.mjs && node scripts/test-public-structured-data.mjs && node scripts/test-public-brand-disambiguation.mjs && node scripts/test-public-heading-hierarchy.mjs && node scripts/test-public-link-targets.mjs && node scripts/test-public-social-preview.mjs && node scripts/test-public-soft-404.mjs && node scripts/test-public-app-copy-voice.mjs && node scripts/test-public-deploy-bundle.mjs && find scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
"check": "npm test",
- "test": "node scripts/test-service-engine.mjs && node scripts/test-sales-intake-contract.mjs && node scripts/test-active-offer-projection.mjs && node scripts/test-active-operator-surfaces.mjs && node scripts/test-direction-proof-gate.mjs && node scripts/test-client-readiness-contract.mjs && node scripts/test-validated-service-client.mjs && node scripts/test-client-acceptance-gates.mjs && node scripts/check-product-truth.mjs && node scripts/check-human-service-kit.mjs && node scripts/test-design-system-proving-lab.mjs && node scripts/check-retention-automation.mjs && node scripts/test-retention-automation.mjs && node scripts/check-agency-defaults.mjs && node scripts/check-outbound-claim-safety.mjs && node scripts/check-outbound-send-readiness.mjs && node scripts/test-outbound-send-readiness.mjs && node scripts/test-public-conversion-signal.mjs && node scripts/test-public-structured-data.mjs && node scripts/test-public-brand-disambiguation.mjs && node scripts/test-public-heading-hierarchy.mjs && node scripts/test-public-link-targets.mjs && node scripts/test-public-social-preview.mjs && node scripts/test-public-soft-404.mjs && node scripts/test-public-deploy-bundle.mjs && find scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
+ "test": "node scripts/test-service-engine.mjs && node scripts/test-sales-intake-contract.mjs && node scripts/test-active-offer-projection.mjs && node scripts/test-active-operator-surfaces.mjs && node scripts/test-direction-proof-gate.mjs && node scripts/test-client-readiness-contract.mjs && node scripts/test-validated-service-client.mjs && node scripts/test-client-acceptance-gates.mjs && node scripts/check-product-truth.mjs && node scripts/check-human-service-kit.mjs && node scripts/test-design-system-proving-lab.mjs && node scripts/check-retention-automation.mjs && node scripts/test-retention-automation.mjs && node scripts/check-agency-defaults.mjs && node scripts/check-outbound-claim-safety.mjs && node scripts/check-outbound-send-readiness.mjs && node scripts/test-outbound-send-readiness.mjs && node scripts/test-public-conversion-signal.mjs && node scripts/test-public-structured-data.mjs && node scripts/test-public-brand-disambiguation.mjs && node scripts/test-public-heading-hierarchy.mjs && node scripts/test-public-link-targets.mjs && node scripts/test-public-social-preview.mjs && node scripts/test-public-soft-404.mjs && node scripts/test-public-app-copy-voice.mjs && node scripts/test-public-deploy-bundle.mjs && find scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
"site:publish": "node scripts/publish-public-site.mjs",
"site:publish:check": "node scripts/publish-public-site.mjs --prepare-only"
},
diff --git a/scripts/test-public-app-copy-voice.mjs b/scripts/test-public-app-copy-voice.mjs
new file mode 100644
index 00000000..2abf3f5f
--- /dev/null
+++ b/scripts/test-public-app-copy-voice.mjs
@@ -0,0 +1,96 @@
+import { readFileSync } from "node:fs"
+import { fileURLToPath } from "node:url"
+import { dirname, join } from "node:path"
+
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..")
+const read = (p) => readFileSync(join(ROOT, p), "utf8")
+
+let failures = 0
+let checks = 0
+const ok = (cond, msg) => {
+ checks++
+ if (cond) console.log(` ok ${msg}`)
+ else {
+ failures++
+ console.error(` FAIL ${msg}`)
+ }
+}
+
+// The four public app pages (Promptly and Drishti product + privacy pages).
+// PR #28 cleaned the product and support pages and this change cleans the
+// residual product-page eyebrows and the app privacy pages; kept as an
+// explicit list so a new app page with the old editorial voice fails CI
+// instead of being silently missed.
+const APP_PAGES = [
+ "public/drishti/index.html",
+ "public/drishti/privacy/index.html",
+ "public/promptly/index.html",
+ "public/promptly/privacy/index.html"
+]
+
+// The old copy spoke to Tiny Studio's own team or Apple's reviewer instead of
+// visitors: H1s on the launch calendar ("already in place before launch",
+// "already public ahead of release"), leads about the "current planned launch
+// scope" and "final App Store privacy disclosures", a "Current release scope"
+// aside ("not publicly released yet"), "At launch" cards, editor-facing
+// "Where to go next" eyebrows, and notes that the page and App Store privacy
+// answers should be updated before the next submission. Any of these
+// fragments anywhere on an app page means the visitor-facing rewrite has
+// regressed.
+const EDITORIAL_FRAGMENTS = [
+ "Where to go next",
+ "already",
+ "launch",
+ "planned",
+ "not publicly released",
+ "App Store",
+ "submission",
+ "in the meantime",
+ "current build",
+ "should stay aligned",
+ "should be updated before",
+ "before that version is submitted",
+ "Current release scope"
+]
+
+// The replacement copy names the actual app and keeps the public support
+// route. Each page must keep both so an over-aggressive rewrite that deletes
+// product identity or the support path fails CI.
+const PAGE_REQUIREMENTS = {
+ "public/drishti/index.html": ["Drishti", "support@tinystudio.in"],
+ "public/drishti/privacy/index.html": ["Drishti", "support@tinystudio.in"],
+ "public/promptly/index.html": ["Promptly", "support@tinystudio.in"],
+ "public/promptly/privacy/index.html": ["Promptly", "support@tinystudio.in"]
+}
+
+console.log("test-public-app-copy-voice: the four public app pages carry only visitor-facing copy")
+
+console.log("A. every app page keeps visitor-facing product and support copy")
+for (const page of APP_PAGES) {
+ const html = read(page)
+ for (const fragment of PAGE_REQUIREMENTS[page]) {
+ ok(html.includes(fragment), `${page} names ${fragment}`)
+ }
+}
+
+console.log("B. no app page carries editorial or submission-prep phrasing")
+for (const page of APP_PAGES) {
+ const html = read(page)
+ for (const fragment of EDITORIAL_FRAGMENTS) {
+ ok(!html.includes(fragment), `${page} avoids "${fragment}"`)
+ }
+}
+
+console.log("C. npm test/ci wiring")
+const pkg = JSON.parse(read("package.json"))
+ok(
+ pkg.scripts.test.includes("test-public-app-copy-voice.mjs"),
+ "npm test runs the public app copy voice test"
+)
+ok(
+ pkg.scripts.ci.includes("test-public-app-copy-voice.mjs"),
+ "npm run ci runs the public app copy voice test"
+)
+
+console.log(`\n${checks} checks, ${failures} failures`)
+process.exit(failures === 0 ? 0 : 1)
From c9d06f0840e8f5b1fe72fe487c78e65ceff620b6 Mon Sep 17 00:00:00 2001
From: Nish <257724087+nish3451@users.noreply.github.com>
Date: Wed, 19 Aug 2026 16:06:54 +0530
Subject: [PATCH 3/3] Assert the entity-encoded support address, not the
plaintext one
PR #123 replaced the literal support@tinystudio.in in the public HTML with
support@tinystudio.in so Cloudflare Email Address Obfuscation cannot
rewrite it into a [email protected] placeholder, and its live check now
asserts the plaintext form is absent. This guard still required the plaintext
form, so the two contradicted each other. Updated to match the landed
encoding.
---
scripts/test-public-app-copy-voice.mjs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/scripts/test-public-app-copy-voice.mjs b/scripts/test-public-app-copy-voice.mjs
index 2abf3f5f..9c97b68f 100644
--- a/scripts/test-public-app-copy-voice.mjs
+++ b/scripts/test-public-app-copy-voice.mjs
@@ -57,10 +57,10 @@ const EDITORIAL_FRAGMENTS = [
// route. Each page must keep both so an over-aggressive rewrite that deletes
// product identity or the support path fails CI.
const PAGE_REQUIREMENTS = {
- "public/drishti/index.html": ["Drishti", "support@tinystudio.in"],
- "public/drishti/privacy/index.html": ["Drishti", "support@tinystudio.in"],
- "public/promptly/index.html": ["Promptly", "support@tinystudio.in"],
- "public/promptly/privacy/index.html": ["Promptly", "support@tinystudio.in"]
+ "public/drishti/index.html": ["Drishti", "support@tinystudio.in"],
+ "public/drishti/privacy/index.html": ["Drishti", "support@tinystudio.in"],
+ "public/promptly/index.html": ["Promptly", "support@tinystudio.in"],
+ "public/promptly/privacy/index.html": ["Promptly", "support@tinystudio.in"]
}
console.log("test-public-app-copy-voice: the four public app pages carry only visitor-facing copy")