Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
a0dcf4f
fix(public): replace residual editorial and submission-prep copy on t…
nish3451 Aug 11, 2026
3f32219
test(public): guard the four app pages against editorial and submissi…
nish3451 Aug 11, 2026
f27567f
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
94628be
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
245e197
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
8504728
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
69bce84
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
0d85016
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
13a651e
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
a7eb673
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 12, 2026
84f3d53
merge main into fix/public-app-pages-editorial-copy-voice-lane1: reso…
nish3451 Aug 18, 2026
b940e00
Merge branch 'main' into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 19, 2026
9c5bb0a
Merge origin/main into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 19, 2026
4980660
Merge origin/main into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 19, 2026
c9d06f0
Assert the entity-encoded support address, not the plaintext one
nish3451 Aug 19, 2026
ef7735e
Merge origin/main into fix/public-app-pages-editorial-copy-voice-lane1
nish3451 Aug 19, 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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,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/test-operator-check-strictness.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 && node scripts/test-deploy-public-site-workflow.mjs && node scripts/test-pages-release.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/test-operator-check-strictness.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 && node scripts/test-deploy-public-site-workflow.mjs && node scripts/test-pages-release.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/test-operator-check-strictness.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 && node scripts/test-deploy-public-site-workflow.mjs && node scripts/test-pages-release.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/test-operator-check-strictness.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 && node scripts/test-deploy-public-site-workflow.mjs && node scripts/test-pages-release.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"
},
Expand Down
2 changes: 1 addition & 1 deletion public/drishti/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ <h2>Built around rituals, streaks, and compassion.</h2>
<section class="app-strip reveal delay-2">
<div class="app-strip-grid">
<article>
<p class="eyebrow">Where to go next</p>
<p class="eyebrow">Help and next steps</p>
<ul class="plain-list">
<li><a href="/drishti/support/">Drishti support</a></li>
<li><a href="/drishti/privacy/">Drishti privacy</a></li>
Expand Down
32 changes: 16 additions & 16 deletions public/drishti/privacy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@
<section class="page-hero-card reveal">
<div>
<p class="eyebrow">Drishti privacy</p>
<h1>Drishti’s privacy page is already in place before launch.</h1>
<h1>Drishti privacy for mindful screen time.</h1>
<p class="page-lead">
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.
</p>
<div class="meta-row">
<span class="chip accent">Effective date: March 14, 2026</span>
Expand All @@ -96,23 +97,23 @@ <h1>Drishti’s privacy page is already in place before launch.</h1>
</div>

<aside class="page-card dark">
<p class="eyebrow">Current release scope</p>
<p class="eyebrow">What this page covers</p>
<ul class="plain-list">
<li>Drishti is not publicly released yet.</li>
<li>This page reflects the currently planned launch configuration.</li>
<li>What information Tiny Studio may receive and how it is used.</li>
<li>Retention and the choices you have.</li>
<li>Support questions can be sent to Tiny Studio’s support inbox.</li>
</ul>
</aside>
</section>

<section class="card-grid">
<article class="info-card reveal delay-1">
<p class="eyebrow">At launch</p>
<h2>No third-party advertising or cross-app tracking is disclosed here.</h2>
<p class="eyebrow">Advertising and tracking</p>
<h2>No third-party advertising or cross-app tracking.</h2>
<p>
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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

State the actual privacy practices.

“Does not describe” only limits the page or policy text. It does not state whether the app uses targeted advertising, cross-app tracking, or sells personal information. Use direct statements of the actual practices after confirming them with the product and privacy owners.

  • public/drishti/privacy/index.html#L109-L116: state whether Drishti uses targeted advertising, cross-app tracking, sign-in, and payments.
  • public/promptly/privacy/index.html#L108-L115: state whether Promptly uses targeted advertising or cross-app tracking, and whether it sells personal information.
📍 Affects 2 files
  • public/drishti/privacy/index.html#L109-L116 (this comment)
  • public/promptly/privacy/index.html#L108-L115
🤖 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/drishti/privacy/index.html` around lines 109 - 116, Replace the
qualifying “does not describe” language in
public/drishti/privacy/index.html:109-116 with direct, product-owner-confirmed
statements covering Drishti’s use of targeted advertising, cross-app tracking,
sign-in, and website payments. Apply the same root change in
public/promptly/privacy/index.html:108-115 by stating Promptly’s actual
targeted-advertising and cross-app-tracking practices and whether it sells
personal information; update both pages’ corresponding privacy card content
without inferring unconfirmed practices.

</p>
</article>

Expand Down Expand Up @@ -149,10 +150,9 @@ <h2>To respond, improve, and keep records when necessary.</h2>
<article>
<p class="eyebrow">Important note</p>
<p class="section-copy">
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.
</p>
</article>
</div>
Expand Down
2 changes: 1 addition & 1 deletion public/promptly/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ <h2>Reminders, proof, and calendar clarity.</h2>
<section class="app-strip reveal delay-2">
<div class="app-strip-grid">
<article>
<p class="eyebrow">Where to go next</p>
<p class="eyebrow">Help and next steps</p>
<ul class="plain-list">
<li><a href="/promptly/support/">Promptly support</a></li>
<li><a href="/promptly/privacy/">Promptly privacy</a></li>
Expand Down
33 changes: 16 additions & 17 deletions public/promptly/privacy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@
<section class="page-hero-card reveal">
<div>
<p class="eyebrow">Promptly privacy</p>
<h1>Promptly’s privacy page is already public ahead of release.</h1>
<h1>Promptly privacy for bookings, reminders, and client data.</h1>
<p class="page-lead">
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.
</p>
<div class="meta-row">
<span class="chip accent">Effective date: March 14, 2026</span>
Expand All @@ -96,23 +96,23 @@ <h1>Promptly’s privacy page is already public ahead of release.</h1>
</div>

<aside class="page-card dark">
<p class="eyebrow">Current release scope</p>
<p class="eyebrow">What this page covers</p>
<ul class="plain-list">
<li>Promptly is not publicly released yet.</li>
<li>This page reflects the currently planned launch configuration.</li>
<li>What data Promptly handles and how it is used.</li>
<li>Retention, service providers, and your choices.</li>
<li>Privacy and support questions can be sent to Tiny Studio’s support inbox.</li>
</ul>
</aside>
</section>

<section class="card-grid">
<article class="info-card reveal delay-1">
<p class="eyebrow">At launch</p>
<h2>No third-party advertising or cross-app tracking is planned.</h2>
<p class="eyebrow">Advertising and tracking</p>
<h2>No third-party advertising or cross-app tracking.</h2>
<p>
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.
</p>
</article>

Expand Down Expand Up @@ -150,7 +150,7 @@ <h2>Booking links, notes, payment proof, and support records.</h2>
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.

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 Update the effective date for the revised privacy claims

This changes an intended safeguard (should not receive) into a categorical statement about current data handling, while the page still says the policy became effective on March 14, 2026; the Drishti page likewise gains new current-state claims without changing its date. When this August revision is published, users cannot tell when these materially different disclosures took effect, so update the displayed effective date (or add a revision date) on both privacy pages.

Useful? React with 👍 / 👎.

</p>
</article>
<article>
Expand All @@ -170,10 +170,9 @@ <h2>Booking links, notes, payment proof, and support records.</h2>
<article>
<p class="eyebrow">Important note</p>
<p class="section-copy">
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.
</p>
</article>
<article>
Expand Down
96 changes: 96 additions & 0 deletions scripts/test-public-app-copy-voice.mjs
Original file line number Diff line number Diff line change
@@ -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"
Comment on lines +24 to +28

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 Discover new app pages before checking copy

When another app product or privacy page is added, this hard-coded array never discovers it, so that page can contain every forbidden editorial fragment while both npm test and npm run ci still pass—the opposite of the stated guard on lines 21–23. Enumerate eligible app pages from public/, or add an assertion that every app product/privacy page is represented here.

Useful? React with 👍 / 👎.

]

// 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",
Comment on lines +42 to +44

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 Match full editorial phrases instead of common words

Because the test applies html.includes to each entry, ordinary visitor-facing copy containing words such as “already,” “launch,” or “planned” will fail the blocking npm test and npm run ci chains even when none of the old editorial phrasing has returned—for example, an “Already have early access?” support prompt. Match the complete legacy phrases or otherwise scope these checks so valid product copy does not break CI.

Useful? React with 👍 / 👎.

"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"]

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 Check the actual support routes

These requirements only look for the support email address, although the guard is intended to preserve each app's public support path. If /drishti/support/ or /promptly/support/ is removed from a page, the unchanged email in its footer still makes both npm test and npm run ci pass; require the corresponding app-specific support URL instead.

Useful? React with 👍 / 👎.

}
Comment on lines +59 to +64

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 | 🟠 Major | ⚡ Quick win

Validate the app-specific support routes.

PAGE_REQUIREMENTS checks support@tinystudio.in, but it does not check /drishti/support/ or /promptly/support/. An edit can remove an app support route and still pass this test. Add the expected app-specific support route for each product page.

🤖 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/test-public-app-copy-voice.mjs` around lines 59 - 64, Update
PAGE_REQUIREMENTS to include the expected app-specific support route for each
Drishti and Promptly page, adding /drishti/support/ to Drishti entries and
/promptly/support/ to Promptly entries while preserving the existing title and
email requirements.


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}`)

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 Scope product identity checks to the app content

Because this searches the entire HTML document, the shared metadata and footer already contain each expected app name; even deleting all Drishti- or Promptly-specific visitor-facing content from <main> would leave this check passing. Check the relevant heading or normalized main content instead so the advertised product-identity guard detects an over-aggressive rewrite.

Useful? React with 👍 / 👎.

}
}

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}"`)

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 Compare editorial fragments case-insensitively

Because String.prototype.includes is case-sensitive, capitalizing a listed lowercase fragment lets the prohibited copy pass the blocking guard. For example, reintroducing “Already public ahead of release” or an “At Launch” heading passes both npm test and npm run ci, despite being the exact editorial voice this test is intended to reject; normalize both the page text and fragments to a common case before comparing them.

Useful? React with 👍 / 👎.

Comment on lines +78 to +80

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 Normalize rendered text before checking fragments

Because this applies includes directly to raw HTML, a prohibited visitor-facing phrase passes whenever normal markup changes its source representation—for example, App&nbsp;Store, Current release\n scope, or text split by an inline element all render as the banned phrase but are not matched. Extract or normalize rendered text, including entities and whitespace, before performing these checks so harmless HTML formatting cannot bypass the CI guard.

Useful? React with 👍 / 👎.

}
}

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)
Loading