Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -94,9 +94,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-theme-color.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 && node scripts/test-pr-duplicates.mjs && node scripts/test-public-support-contact-voice.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-theme-color.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 && node scripts/test-pr-duplicates.mjs && node scripts/test-public-support-contact-voice.mjs && node scripts/test-public-app-copy-voice.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-theme-color.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 && node scripts/test-pr-duplicates.mjs && node scripts/test-public-support-contact-voice.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-theme-color.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 && node scripts/test-pr-duplicates.mjs && node scripts/test-public-support-contact-voice.mjs && node scripts/test-public-app-copy-voice.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 @@ -189,7 +189,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
2 changes: 1 addition & 1 deletion public/promptly/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,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
2 changes: 1 addition & 1 deletion public/promptly/privacy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,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 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&#64;tinystudio.in"],
"public/drishti/privacy/index.html": ["Drishti", "support&#64;tinystudio.in"],
"public/promptly/index.html": ["Promptly", "support&#64;tinystudio.in"],
"public/promptly/privacy/index.html": ["Promptly", "support&#64;tinystudio.in"]
}
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)