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
14 changes: 14 additions & 0 deletions src/pages/ForAgentsPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ describe('ForAgentsPage', () => {
expect(cta).toBeTruthy()
})

it('playground response sample matches the real /db/new wire shape (2026-06-11 display-detail audit)', () => {
renderPage()
const text = document.body.textContent ?? ''
// The page promises "what hits the wire — no mocks": the public host is
// pg.instanode.dev (POSTGRES_PUBLIC_HOST) — "shared-1.instanode.dev"
// never existed — and role/db names use the provisioner's canonical
// usr_/db_ prefixes. /db/new returns 201 (synchronous), not 200.
expect(text).toContain('@pg.instanode.dev:5432')
expect(text).not.toContain('shared-1.instanode.dev')
expect(text).toContain('postgres://usr_')
expect(text).not.toContain('"res_')
expect(text).toContain('201 · 1.4s · application/json')
})

it('copies the command and flips the button label to "copied"', async () => {
copyMock.mockResolvedValue(true)
renderPage()
Expand Down
14 changes: 11 additions & 3 deletions src/pages/ForAgentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,16 @@ const PLAYGROUND_CURL = `curl -X POST https://api.instanode.dev/db/new \\
// provisioning response (CLAUDE.md convention #11) — a caller that omits
// `env` lands in `development`, the lowest-stakes bucket. The earlier
// sample dropped it, so the page misrepresented the wire shape.
// Display-detail accuracy (2026-06-11): the page promises "what hits the
// wire — no mocks", so the shapes must match prod: `token` is a UUID
// (openapi DBProvisionResponse format:uuid, not the old fabricated
// "res_…"), the public host is pg.instanode.dev (POSTGRES_PUBLIC_HOST;
// "shared-1.instanode.dev" never existed), and role/db names follow the
// provisioner's canonical usr_{token[:12]}/db_{token[:12]} scheme.
const PLAYGROUND_RESPONSE = `{
"ok": true,
"token": "res_2RtL9k4mP",
"connection_url": "postgres://u_3jX:••••@shared-1.instanode.dev:5432/db_2rtL9k4mp",
"token": "9b2f61ce-4a3d-4e8b-b150-7c2f0a4d9e21",
"connection_url": "postgres://usr_9b2f61ce4a3d:••••@pg.instanode.dev:5432/db_9b2f61ce4a3d",
"tier": "anonymous",
"env": "development",
"limits": { "storage_mb": 10, "connections": 2 },
Expand Down Expand Up @@ -172,7 +178,9 @@ export function ForAgentsPage() {
<div className="fa-play-head">
<span className="fa-play-dot fa-play-dot--res" />
response
<span className="fa-play-meta">200 · 1.4s · application/json</span>
{/* Display-detail accuracy (2026-06-11): /db/new returns
201 Created (synchronous provisioning), not 200. */}
<span className="fa-play-meta">201 · 1.4s · application/json</span>
</div>
<pre className="public-code"><code>{highlightJson(PLAYGROUND_RESPONSE)}</code></pre>
</div>
Expand Down
56 changes: 45 additions & 11 deletions src/pages/MarketingPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,26 +88,60 @@ describe('MarketingPage — homepage nav drift (T18 P1-2)', () => {
})

describe('MarketingPage — claim consistency (T18 P1-4 / P1-6)', () => {
it("'Seven services' headline matches the MCP tools card (both say seven, listing webhook)", () => {
it('services headline + MCP tools card count match the rendered SERVICES cards (registry-derived, not hand-typed)', () => {
const { container } = render(
<MemoryRouter initialEntries={['/']}>
<MarketingPage />
</MemoryRouter>,
)
const text = container.textContent ?? ''
// Headline says "Seven services. One bundle."
expect(text).toMatch(/Seven services\. One bundle\./)
// MCP tools card lists the seven provisioning tools (must still list
// webhook — anti-regression for the dropped-webhook bug) AND, per the
// 2026-06-03 gap fix, also surfaces the stack/deployment management tools
// (the MCP server registers more than seven; "Seven tools registered" was
// an understatement).
expect(text).not.toMatch(/Six provisioning tools/)
expect(text).toMatch(/Seven provisioning tools/)
expect(text).toMatch(/webhook/)
// 2026-06-11 display-detail audit: the headline said "Seven services"
// above EIGHT rendered cards (vector joined SERVICES on 2026-05-20 but
// the count copy was never bumped), and the MCP card said "Seven
// provisioning tools" while omitting `vector` (mcp registers
// create_vector). Per rule 18, derive the expected count from the live
// registry (the rendered service cards) instead of a hand-typed word so
// adding service #9 without bumping the copy fails this test.
const cardCount = container.querySelectorAll('.mkt-service-card').length
expect(cardCount).toBeGreaterThan(0)
const COUNT_WORDS = [
'Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight',
'Nine', 'Ten', 'Eleven', 'Twelve',
]
const word = COUNT_WORDS[cardCount]
expect(word).toBeDefined()
expect(text).toContain(`${word} services. One bundle.`)
expect(text).toContain(`${word} provisioning tools`)
// The MCP tools card must list every provisioning service shown in the
// cards row — anti-regression for the dropped-webhook (T18 P1-4) and
// dropped-vector (2026-06-11) bugs — plus the management tools.
for (const svc of ['postgres', 'vector', 'redis', 'mongo', 'queue', 'storage', 'webhook', 'deploy']) {
expect(text).toContain(svc)
}
expect(text).toMatch(/list_deployments/)
})

it('mock provision visual shows real prod facts: nyc3 region, 201 for /db/new, usr_/db_ name prefixes', () => {
const { container } = render(
<MemoryRouter initialEntries={['/']}>
<MarketingPage />
</MemoryRouter>,
)
const html = container.innerHTML
// 2026-06-11 display-detail audit: prod runs on DigitalOcean nyc3 —
// "iad-1" was a fabricated region we never ran in.
expect(html).toContain('nyc3 · us-east')
expect(html).not.toContain('iad-1')
// POST /db/new provisions synchronously → 201 Created (openapi.json);
// "202 accepted" belongs to /deploy/new + /stacks/new only.
expect(html).toContain('201 created')
expect(html).not.toContain('202 accepted')
// Claim-card sample URL uses the provisioner's canonical usr_/db_
// prefixes, not the fabricated u_/d_ ones.
expect(html).toContain('usr_xY9')
expect(html).not.toContain('postgres://u_xY9')
})

it("Deploy service card claims a build window consistent with content/llms.txt (~60s, not '<10s')", () => {
const { container } = render(
<MemoryRouter initialEntries={['/']}>
Expand Down
44 changes: 32 additions & 12 deletions src/pages/MarketingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -383,11 +383,15 @@ export function MarketingPage() {
</div>
</header>

{/* ---------- seven services row ---------- */}
{/* ---------- eight services row ---------- */}
<section className="mkt-section" id="services">
<div className="mkt-wrap">
<div className="mkt-section-head">
<div className="mkt-section-tag">Seven services. One bundle.</div>
{/* Display-detail accuracy (2026-06-11): Vector (pgvector, /vector/new)
joined the SERVICES array on 2026-05-20 but this headline kept
saying "Seven" above eight rendered cards. The count below is
asserted against SERVICES.length in MarketingPage.test.tsx. */}
<div className="mkt-section-tag">Eight services. One bundle.</div>
<h2 className="mkt-section-title">
The whole stack, <span className="mkt-accent">not the pieces.</span>
</h2>
Expand Down Expand Up @@ -447,7 +451,11 @@ export function MarketingPage() {
<div className="r"><span className="k">auth</span><span className="v dim">none — fingerprinted</span></div>
<div className="r"><span className="k">body</span><span className="v dim">{'{}'}</span></div>
<div className="r" style={{ marginTop: 8 }}>
<span className="k">status</span><span className="v ok">202 accepted</span>
{/* Display-detail accuracy (2026-06-11): POST /db/new provisions
synchronously and returns 201 Created (see /openapi.json) —
the old "202 accepted" implied an async contract the db
endpoint does not have (202 is /deploy/new + /stacks/new). */}
<span className="k">status</span><span className="v ok">201 created</span>
</div>
</div>
</article>
Expand All @@ -460,8 +468,14 @@ export function MarketingPage() {
stored credentials, a connection string in 1.4 s. 24 h TTL on the free tier.
</p>
<div className="mkt-how-visual" aria-hidden="true">
<div className="r"><span className="k">service</span><span className="v">postgres 16.2</span></div>
<div className="r"><span className="k">region</span><span className="v dim">iad-1 · us-east</span></div>
{/* Display-detail accuracy (2026-06-11): prod customer Postgres runs
the pgvector pg16 image (major version is the honest claim — the
patch level tracks the image) in DigitalOcean nyc3, not the old
fabricated "iad-1" (a region we never ran in). nyc3 is US-East
(NYC) — matches the Changelog's nyc3 cutover entry and the
Privacy page's "US-region cloud infrastructure". */}
<div className="r"><span className="k">service</span><span className="v">postgres 16</span></div>
<div className="r"><span className="k">region</span><span className="v dim">nyc3 · us-east</span></div>
<div className="r"><span className="k">size</span><span className="v">10 MB / 2 conn</span></div>
<div className="r"><span className="k">expires</span><span className="v dim">in 24h</span></div>
<div className="r" style={{ marginTop: 8 }}>
Expand Down Expand Up @@ -493,8 +507,8 @@ export function MarketingPage() {
Obfuscation" feature scans every served HTML response
and replaces any string that looks like name@domain.tld
with a "[email protected]" placeholder + JS-decode shim.
The sample URL below has `u_xY9...@pg.instanode.dev`,
which matches CF's regex (the `u_xY9` part is a valid
The sample URL below has `usr_xY9...@pg.instanode.dev`,
which matches CF's regex (the `usr_xY9` part is a valid
email local-part), so the live homepage rendered
"postgres://[email protected]:5432/d_..." — confusing
for visitors and broken for the hero claim card.
Expand All @@ -507,7 +521,11 @@ export function MarketingPage() {
className="url"
dangerouslySetInnerHTML={{
__html:
'<!--email_off-->postgres://u_xY9...@pg.instanode.dev:5432/d_...<!--/email_off-->',
// Display-detail accuracy (2026-06-11): the provisioner's
// canonical naming is usr_{token}/db_{token} (see
// provisioner internal/backend/postgres/backend.go) — the
// old `u_…`/`d_…` prefixes were fabricated.
'<!--email_off-->postgres://usr_xY9...@pg.instanode.dev:5432/db_...<!--/email_off-->',
}}
/>
<div className="claim-btn">Claim these resources →</div>
Expand Down Expand Up @@ -701,12 +719,14 @@ export function MarketingPage() {
{'\n '}{'}'}
{'\n'}{'}'}
</div>
{/* T18 P1-4 (2026-05-20): list the same seven tools as
the "Seven services. One bundle." headline (and the
{/* T18 P1-4 (2026-05-20): list the same tools as the
"Eight services. One bundle." headline (and the
SERVICES array). The earlier "Six tools" copy dropped
webhook — a real MCP tool — and contradicted the same
page. */}
<p>Seven provisioning tools: <code>postgres</code>, <code>redis</code>, <code>mongo</code>, <code>queue</code>, <code>storage</code>, <code>webhook</code>, <code>deploy</code> — plus stack &amp; deployment management (<code>create_stack</code>, <code>list_stacks</code>, <code>update_stack_env</code>, <code>list_deployments</code>, <code>get_deployment</code>, <code>redeploy</code>, <code>delete_deployment</code>, …).</p>
page. 2026-06-11: count bumped seven → eight and
`vector` added — mcp registers create_vector (shipped
with /vector/new) but this card still omitted it. */}
<p>Eight provisioning tools: <code>postgres</code>, <code>vector</code>, <code>redis</code>, <code>mongo</code>, <code>queue</code>, <code>storage</code>, <code>webhook</code>, <code>deploy</code> — plus stack &amp; deployment management (<code>create_stack</code>, <code>list_stacks</code>, <code>update_stack_env</code>, <code>list_deployments</code>, <code>get_deployment</code>, <code>redeploy</code>, <code>delete_deployment</code>, …).</p>
</div>
</div>

Expand Down
4 changes: 3 additions & 1 deletion src/pages/StatusPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ export function StatusPage() {
{/* FIX-G (2026-05-14): "Subscribe via RSS" link removed — /status.rss
404s on the API. See IncidentsPage for the matching cleanup. */}
<section className="public-section status-links">
<a href="https://github.com/instanode" className="status-link">GitHub status</a>
{/* Display-detail accuracy (2026-06-11): github.com/instanode is an
unrelated third-party org — ours is InstaNode-dev. */}
<a href="https://github.com/InstaNode-dev" className="status-link">GitHub status</a>
<span className="status-link-sep">·</span>
<a href="/incidents" className="status-link">Incident log</a>
</section>
Expand Down
Loading