From 046a58bbf9cb6ee6b794374919b7f3a7e83979c1 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Mon, 3 Aug 2026 14:10:21 +0000 Subject: [PATCH] test(web): fail the build when marketing copy drifts from shipped providers The last three PRs all fixed the same class of bug. /credential-sharing and README.md are hand-written copy; the providers they advertise are a real registry in @logicsrc/plugin-credential-sharing. Nothing connected the two, so the `team` provider shipped on 2026-07-13 and three weeks later both surfaces still described a five-provider tool with no mention of teams. The docs were right the whole time -- only the pages people actually land on had gone stale, which is worse, because it reads as "the product cannot do this" rather than as a documentation gap. Assert it instead. For every provider in the registry, the Credential Sharing section and the README must say something that counts as advertising it. The registry's own `name` cannot be the proof -- `env` is "Local .env file" and `team` is "LogicSRC Team Vault", neither of which is how the copy reads -- so each provider declares its own pattern, and a provider with no declaration fails too. That way adding a provider forces a deliberate answer about the customer-facing copy. Verified against the bug it is meant to catch: reverting the team copy reproduces "These providers ship but /credential-sharing never mentions them: team", and reverting the README line reproduces the same for sh1pt and team. Co-Authored-By: Claude Opus 5 (1M context) --- .../contract/marketing-drift.contract.test.ts | 85 +++++++++++++++++++ apps/logicsrc-web/package.json | 1 + package-lock.json | 1 + 3 files changed, 87 insertions(+) create mode 100644 apps/logicsrc-web/contract/marketing-drift.contract.test.ts diff --git a/apps/logicsrc-web/contract/marketing-drift.contract.test.ts b/apps/logicsrc-web/contract/marketing-drift.contract.test.ts new file mode 100644 index 0000000..8789d3f --- /dev/null +++ b/apps/logicsrc-web/contract/marketing-drift.contract.test.ts @@ -0,0 +1,85 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; +import { credentialProviders } from "@logicsrc/plugin-credential-sharing"; +import { renderPageMarkup } from "../src/lib/page-markup"; + +/** + * Guards the marketing page against the product. + * + * /credential-sharing is hand-written copy in page-markup.ts, while the + * providers it advertises are a real registry in the plugin. Nothing connected + * the two: the `team` provider shipped on 2026-07-13 and three weeks later the + * page still described a five-provider tool with no mention of teams, which is + * long enough for a reader to conclude the capability did not exist. The docs + * were correct the whole time -- only the surfaces people actually land on had + * drifted. These tests turn that drift into a failing build. + */ + +// A shipped provider id -> proof that the customer-facing copy mentions it. +// The registry's own `name` is not usable as the proof: `env` is "Local .env +// file" and `team` is "LogicSRC Team Vault", neither of which is how the copy +// reads. So each provider declares what "advertised" looks like for it, and +// the first test below makes adding a provider without an entry a failure. +const MARKETING_PROOF: Record = { + env: /\.env/, + doppler: /Doppler/, + railway: /Railway/, + "github-secrets": /GitHub Secrets/, + sh1pt: /sh1pt/, + team: /[Tt]eam vault/ +}; + +const REPO_ROOT = resolve(process.cwd(), "../.."); + +/** Just the Credential Sharing band, so a stray match elsewhere cannot pass. */ +function credentialSection(): string { + const markup = renderPageMarkup(); + const start = markup.indexOf('
", start); + return markup.slice(start, end); +} + +describe("marketing copy tracks the shipped credential providers", () => { + it("every shipped provider declares what advertising it looks like", () => { + const missing = credentialProviders + .filter((provider) => !MARKETING_PROOF[provider.id]) + .map((provider) => provider.id); + + expect( + missing, + `Add these provider ids to MARKETING_PROOF, then make sure the marketing page and README actually say so: ${missing.join(", ")}` + ).toEqual([]); + }); + + it("the credential sharing section names every shipped provider", () => { + const section = credentialSection(); + const unadvertised = credentialProviders + .filter((provider) => { + const proof = MARKETING_PROOF[provider.id]; + return proof ? !proof.test(section) : false; + }) + .map((provider) => provider.id); + + expect( + unadvertised, + `These providers ship but /credential-sharing never mentions them: ${unadvertised.join(", ")}. Update renderPageMarkup in src/lib/page-markup.ts.` + ).toEqual([]); + }); + + it("the README names every shipped provider", () => { + const readme = readFileSync(resolve(REPO_ROOT, "README.md"), "utf8"); + const unadvertised = credentialProviders + .filter((provider) => { + const proof = MARKETING_PROOF[provider.id]; + return proof ? !proof.test(readme) : false; + }) + .map((provider) => provider.id); + + expect( + unadvertised, + `These providers ship but README.md never mentions them: ${unadvertised.join(", ")}.` + ).toEqual([]); + }); +}); diff --git a/apps/logicsrc-web/package.json b/apps/logicsrc-web/package.json index b02743f..1c9dae6 100644 --- a/apps/logicsrc-web/package.json +++ b/apps/logicsrc-web/package.json @@ -24,6 +24,7 @@ "sanitize-html": "^2.17.5" }, "devDependencies": { + "@logicsrc/plugin-credential-sharing": "file:../../plugins/credential-sharing", "@playwright/test": "^1.57.0", "@types/node": "^24.10.1", "@types/react": "^19.2.0", diff --git a/package-lock.json b/package-lock.json index c021d60..04f173a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -148,6 +148,7 @@ "sanitize-html": "^2.17.5" }, "devDependencies": { + "@logicsrc/plugin-credential-sharing": "file:../../plugins/credential-sharing", "@playwright/test": "^1.57.0", "@types/node": "^24.10.1", "@types/react": "^19.2.0",