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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ build never reaches a production installer.
| `insta branch` | `create` · `list` · `switch` · `delete` · `merge` |
| `insta service` (`services`, `svc`) | `add` · `list` · `remove` · `rename` |
| `insta secrets` | Write `.env`, plus `list` · `set` · `unset` · `bind` · `unbind` · `bindings` · `sources` · `tree` |
| `insta domain` | Bought or bring-your-own: `attach` · `check` · `detach`; buy through InstaCloud: `search` · `buy` · `list` · `status` · `records …` |
| `insta domain` | Bought or bring-your-own: `attach` · `check` · `detach`. Domains already bought through InstaCloud: `list` · `status` · `records …` |
| `insta compute` | `start` · `stop` · `suspend` · `restart` · `status` · `scale` · `limits` · `volume` · `always-on` · `exec` · `ssh` · `repo` · `connect-repo` · `watch-paths` · `disconnect-repo` · `logs` · `metrics` |
| `insta postgres` | `url` (print the DSN) · `connect` (psql) · `stats` · `limits` · `restart` · `volume` · `always-on` · `private-access` · `public-access` · `logs` · `metrics` — every verb takes `[service]` |
| `insta redis` · `mysql` · `mongodb` | `query` · `status` · `limits` · `volume` · `always-on` · `logs` · `metrics` |
Expand Down
23 changes: 10 additions & 13 deletions src/commands/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export async function domainSearch(keyword: string, opts: { tlds?: string; org?:
const r = await api.request<{ results: Quote[] }>('GET', `/orgs/${orgId}/domains/search?${qs}`)
if (opts.json) return printJson(r)
for (const line of searchLines(r.results)) info(line)
const buyable = r.results.find((x) => x.purchasable)
if (buyable) info(`buy one: insta domain buy ${buyable.domainName}`)
}

// ---- buy / attach ----
Expand Down Expand Up @@ -125,17 +123,17 @@ function domainLines(d: Purchased, linked = true): string[] {
return out
}

function orderStatusLines(o: Order, linked = true): string[] {
function orderStatusLines(o: Order): string[] {
const out = [`order ${o.id}: ${o.domainName} — ${o.status}${o.failedReason ? ` — ${o.failedReason}` : ''}`]
if (linked && o.status === 'canceled') out.push(` the checkout closed without payment — order again: insta domain buy ${o.domainName}`)
if (o.status === 'canceled') out.push(' the checkout closed without payment')
return out
}

export async function domainList(opts: { org?: string; json?: boolean }, deps?: DomainDeps): Promise<void> {
const { api, orgId } = await orgDeps(opts, deps)
const r = await api.request<{ items: Purchased[] }>('GET', `/orgs/${orgId}/domains`)
if (opts.json) return printJson(r)
if (!r.items.length) return info('no domains bought through InstaCloud in this org (search: insta domain search <keyword>)')
if (!r.items.length) return info('no domains bought through InstaCloud in this org')
for (const d of r.items) for (const line of domainLines(d, !opts.org)) info(line)
}

Expand All @@ -150,7 +148,7 @@ export async function domainStatus(name: string, opts: { org?: string; json?: bo
const order = orders.find((o) => o.domainName === host) ?? null
if (!domain && !order) die(`${host} was not bought through this org`)
if (opts.json) return printJson({ domain, order })
for (const line of domain ? domainLines(domain, !opts.org) : orderStatusLines(order!, !opts.org)) info(line)
for (const line of domain ? domainLines(domain, !opts.org) : orderStatusLines(order!)) info(line)
}

// ---- nameservers and transferring out ----
Expand Down Expand Up @@ -382,10 +380,9 @@ export function zoneRecordLines(records: ZoneRecord[]): string[] {
* into the zone itself — apexes included — instead of printing them for hand-copying. The zone is
* SEEDED by the provider's record scan, which is a heuristic: the review-then-switch contract
* (printed, and enforced by nothing else) is to compare `zone records` against the domain's current
* DNS and add what is missing at the CURRENT provider — re-running delegate re-imports — before
* re-pointing. A domain carrying live MX records is refused outright: a DNS move that can drop
* mail is never done implicitly. 202 approval_required in agent mode (zone.delegate); org admin
* either way.
* DNS before re-pointing. A domain carrying live MX records is refused outright: a DNS move that
* can drop mail is never done implicitly. 202 approval_required in agent mode (zone.delegate); org
* admin either way.
*/
const orgArgOf = (opts: RecordsOpts): string => (opts.org ? ` --org ${opts.org}` : '')

Expand All @@ -402,7 +399,7 @@ export async function zoneDelegate(domainName: string, opts: RecordsOpts, deps?:
if (opts.json) return printJson(res.body)
const z = res.body as OrgZone
for (const line of zoneLines(z, opts.org)) info(line)
info(`the zone was seeded by a provider scan — a heuristic. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, add anything missing at your CURRENT provider (re-running delegate re-imports), and only then switch the nameservers.`)
info(`the zone was seeded by a provider scan, which can miss records. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, and only then switch the nameservers.`)

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: This removes the only recovery instruction for records the scan misses. zoneRecords only reads and prints records, so users cannot correct the managed zone there; retain the add-at-provider and re-run-delegate step before telling them to switch nameservers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/commands/domain.ts, line 402:

<comment>This removes the only recovery instruction for records the scan misses. `zoneRecords` only reads and prints records, so users cannot correct the managed zone there; retain the add-at-provider and re-run-delegate step before telling them to switch nameservers.</comment>

<file context>
@@ -402,7 +399,7 @@ export async function zoneDelegate(domainName: string, opts: RecordsOpts, deps?:
   const z = res.body as OrgZone
   for (const line of zoneLines(z, opts.org)) info(line)
-  info(`the zone was seeded by a provider scan — a heuristic. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, add anything missing at your CURRENT provider (re-running delegate re-imports), and only then switch the nameservers.`)
+  info(`the zone was seeded by a provider scan, which can miss records. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, and only then switch the nameservers.`)
 }
 
</file context>
Suggested change
info(`the zone was seeded by a provider scan, which can miss records. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, and only then switch the nameservers.`)
info(`the zone was seeded by a provider scan, which can miss records. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, add anything missing at your current provider and re-run \`insta domain zone delegate ${z.domainName}${orgArgOf(opts)}\` to re-import, and only then switch the nameservers.`)

}

export async function zoneList(opts: RecordsOpts, deps?: DomainDeps): Promise<void> {
Expand All @@ -421,7 +418,7 @@ export async function zoneList(opts: RecordsOpts, deps?: DomainDeps): Promise<vo
}
} while (cursor)
if (opts.json) return printJson(r)
if (!r.items.length) return info('no delegated zones — start one: insta domain zone delegate <domain>')
if (!r.items.length) return info('no delegated zones')
for (const z of r.items) for (const line of zoneLines(z, opts.org)) info(line)
}

Expand All @@ -430,7 +427,7 @@ export async function zoneRecords(domainName: string, opts: RecordsOpts, deps?:
const r = await api.request<{ items: ZoneRecord[] }>('GET', `${zonePath(orgId, domainName)}/records`)
if (opts.json) return printJson(r)
for (const line of zoneRecordLines(r.items)) info(line)
if (r.items.length) info(`every type shows here (the scan is a heuristic) — add anything missing at your current DNS provider and re-run \`insta domain zone delegate ${domainName.trim().toLowerCase()}${orgArgOf(opts)}\` to re-import before switching nameservers`)
if (r.items.length) info('every type shows here. The provider scan can miss records, so compare them against your current DNS before switching nameservers')
}

/**
Expand Down
13 changes: 7 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,12 @@ cron.command('preview <expression>').description('Validate an expression and pri
.option('--json').action(guard((expression, o) => cronCmd.cronPreview(expression, o)))

// ---- domain (bought here, or bring your own; hostnames on compute services; DNS of bought zones) ----
const dom = program.command('domain').description('Domains: buy through InstaCloud or bring your own — attach / check / detach hostnames on compute services; DNS records of bought domains')
dom.command('search <keyword>').description('Search purchasable names with prices (a label like "myapp" or a full name like "myapp.com")')
const dom = program.command('domain').description('Domains: attach, check and detach hostnames on compute services, and manage domains already bought through InstaCloud')
// search, buy and zone delegate are hidden while the platform pauses new orders and BYO delegation.
dom.command('search <keyword>', { hidden: true }).description('Search purchasable names with prices (a label like "myapp" or a full name like "myapp.com")')
.option('--tlds <list>', 'comma-separated TLDs to include').option('--org <id>', "target org (default: linked project's org)").option('--json')
.action(guard((keyword, o) => domainCmd.domainSearch(keyword, o)))
dom.command('buy <name>').description('Buy a domain — pay at the printed Stripe Checkout link. It serves nothing until you attach it (gated: domain.purchase)')
dom.command('buy <name>', { hidden: true }).description('Buy a domain — pay at the printed Stripe Checkout link. It serves nothing until you attach it (gated: domain.purchase)')
.option('--years <n>', 'registration term in years (default 1)')
.option('--no-open', 'print the checkout URL instead of opening a browser').option('--json')
.action(guard((name, o) => domainCmd.domainBuy(name, o)))
Expand Down Expand Up @@ -321,14 +322,14 @@ ns.command('reset <domain>').description("Put the zone back on the registrar's o
.option('--org <id>', "target org (default: linked project's org)").option('--json')
.action(guard((domain, o) => domainCmd.domainNameserversReset(domain, o)))

const zone = dom.command('zone').description("Bring-your-own domains served from an InstaCloud-managed zone (nameserver delegation): point the domain's registrar at the pair `zone delegate` answers, and every attach's records — apexes included — are published for you. The alternative stays available: `insta domain attach` alone prints records to paste into your own zone")
zone.command('delegate <domain>').description("Delegate a domain you own elsewhere: builds its managed zone (seeded by a provider record scan — a HEURISTIC, so review `zone records` and add anything missing at your current provider BEFORE switching nameservers; re-running delegate re-imports) and answers the two nameservers to set at your registrar. A domain carrying live MX records is refused — move mail first or keep the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
const zone = dom.command('zone').description("Bring-your-own domains already delegated to an InstaCloud-managed zone: list them, review their records, or release them. For a hostname under a domain you own elsewhere, `insta domain attach` prints the records to add in your own DNS")
zone.command('delegate <domain>', { hidden: true }).description("Delegate a domain you own elsewhere: builds its managed zone (seeded by a provider record scan — a HEURISTIC, so review `zone records` against your current DNS BEFORE switching nameservers) and answers the two nameservers to set at your registrar. A domain carrying live MX records is refused — move mail first or keep the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")

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: The agent-facing CLI reference still directs agents to this paused command, so hiding it from CLI help does not stop agents following the documented workflow. AGENTS.md requires syncing skills/insta/cli-reference.md; update that reference with this command change.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/index.ts, line 326:

<comment>The agent-facing CLI reference still directs agents to this paused command, so hiding it from CLI help does not stop agents following the documented workflow. `AGENTS.md` requires syncing `skills/insta/cli-reference.md`; update that reference with this command change.</comment>

<file context>
@@ -321,14 +322,14 @@ ns.command('reset <domain>').description("Put the zone back on the registrar's o
-const zone = dom.command('zone').description("Bring-your-own domains served from an InstaCloud-managed zone (nameserver delegation): point the domain's registrar at the pair `zone delegate` answers, and every attach's records — apexes included — are published for you. The alternative stays available: `insta domain attach` alone prints records to paste into your own zone")
-zone.command('delegate <domain>').description("Delegate a domain you own elsewhere: builds its managed zone (seeded by a provider record scan — a HEURISTIC, so review `zone records` and add anything missing at your current provider BEFORE switching nameservers; re-running delegate re-imports) and answers the two nameservers to set at your registrar. A domain carrying live MX records is refused — move mail first or keep the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
+const zone = dom.command('zone').description("Bring-your-own domains already delegated to an InstaCloud-managed zone: list them, review their records, or release them. For a hostname under a domain you own elsewhere, `insta domain attach` prints the records to add in your own DNS")
+zone.command('delegate <domain>', { hidden: true }).description("Delegate a domain you own elsewhere: builds its managed zone (seeded by a provider record scan — a HEURISTIC, so review `zone records` against your current DNS BEFORE switching nameservers) and answers the two nameservers to set at your registrar. A domain carrying live MX records is refused — move mail first or keep the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
   .option('--org <id>', "target org (default: linked project's org)").option('--json')
   .action(guard((domain, o) => domainCmd.zoneDelegate(domain, o)))
</file context>

.option('--org <id>', "target org (default: linked project's org)").option('--json')
.action(guard((domain, o) => domainCmd.zoneDelegate(domain, o)))
zone.command('list').description("The org's delegated zones: which are still waiting for the nameserver switch and which are serving")
.option('--org <id>', "target org (default: linked project's org)").option('--json')
.action(guard((o) => domainCmd.zoneList(o)))
zone.command('records <domain>').description("Every record in the delegated zone — the pre-switch review. The scan seeds common records but is not exhaustive: compare against your current DNS and add what is missing at your CURRENT provider, then re-run `zone delegate` to re-import")
zone.command('records <domain>').description("Every record in the delegated zone, for review before you switch nameservers. The provider scan seeds common records but is not exhaustive, so compare it against your current DNS first")
.option('--org <id>', "target org (default: linked project's org)").option('--json')
.action(guard((domain, o) => domainCmd.zoneRecords(domain, o)))
zone.command('release <domain>').description("Release a delegated zone: the platform's records are pruned and the zone deleted. Point the nameservers back at your own provider; hostnames re-verify on the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
Expand Down
22 changes: 11 additions & 11 deletions test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ describe('domain search', () => {
expect(calls[0]).toMatchObject({ method: 'GET', path: '/orgs/org1/domains/search?q=MyApp&tlds=com%2Cdev' })
expect(out()).toContain('myapp.com $15.59 (renews $23.99/yr)')
expect(out()).toContain('myapp.dev unavailable — Domain unavailable')
expect(out()).toContain('buy one: insta domain buy myapp.com')
// New orders are paused, so the results name no buy command.
expect(out()).not.toContain('insta domain buy')
})
it('--org overrides the linked org; --json is the platform body', async () => {
const { deps: d, calls } = deps({ '/domains/search': { results: [quote] } })
Expand Down Expand Up @@ -228,21 +229,20 @@ describe('domain list / status', () => {
expect(out()).not.toContain('order o1')
expect(out()).toContain('api.myapp.com active → api')
})
it('a canceled checkout says how to order again', async () => {
// New orders are paused (platform #689), so a canceled checkout names no way to order again.
it('a canceled checkout says why and does not send the reader to buy again', async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Removing this test drops the only coverage for domainStatus with --org, even though that path is unchanged. Keep a test that checks a canceled status with --org queries the named org and prints the canceled line.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/domain.test.ts, line 233:

<comment>Removing this test drops the only coverage for `domainStatus` with `--org`, even though that path is unchanged. Keep a test that checks a canceled status with `--org` queries the named org and prints the canceled line.</comment>

<file context>
@@ -228,21 +229,20 @@ describe('domain list / status', () => {
   })
-  it('a canceled checkout says how to order again', async () => {
+  // New orders are paused (platform #689), so a canceled checkout names no way to order again.
+  it('a canceled checkout says why and does not send the reader to buy again', async () => {
     const { deps: d } = deps({ '/domains/orders': { items: [{ ...order, status: 'canceled', failedReason: 'checkout expired before payment' }] }, '/orgs/org1/domains': { items: [] } })
     await domainStatus('myapp.com', {}, d)
</file context>

const { deps: d } = deps({ '/domains/orders': { items: [{ ...order, status: 'canceled', failedReason: 'checkout expired before payment' }] }, '/orgs/org1/domains': { items: [] } })
await domainStatus('myapp.com', {}, d)
expect(out()).toContain('canceled — checkout expired before payment')
expect(out()).toContain('order again: insta domain buy myapp.com')
})
// `buy` and `attach` take no --org: they act on the linked project's org, so naming another one
// would send the reader to a command that acts somewhere else.
it('drops the follow-up commands when --org names an org they do not act on', async () => {
const routes = { '/domains/orders': { items: [{ ...order, status: 'canceled', failedReason: 'x' }] }, '/orgs/org9/domains': { items: [] } }
const { deps: d } = deps(routes)
await domainStatus('myapp.com', { org: 'org9' }, d)
expect(out()).toContain('canceled')
expect(out()).toContain('the checkout closed without payment')
expect(out()).not.toContain('insta domain buy')
})
it('an empty list says so without pointing at search', async () => {
const { deps: d } = deps({ '/orgs/org1/domains': { items: [] } })
await domainList({}, d)
expect(out()).toContain('no domains bought through InstaCloud in this org')
expect(out()).not.toContain('insta domain search')
})
it('status of a name never bought here fails plainly, --json included', async () => {
const { deps: d } = deps({ '/domains/orders': { items: [] }, '/orgs/org1/domains': { items: [] } })
await expect(domainStatus('other.com', { json: true }, d)).rejects.toThrow('exit 1')
Expand Down
20 changes: 20 additions & 0 deletions test/help-surface.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ describe('group shapes', () => {
expect(run(['compute', 'scale', '--help']).stdout).toContain('Usage: insta compute scale [options] [count] [service]')
expect(run(['storage', 'set-access', '--help']).stdout).toContain('Usage: insta storage set-access [options] <access>')
}, 30_000)
// New domain orders and BYO delegation are paused (platform #689, #687). Hidden, not removed,
// so re-enabling them is unhiding. The Usage line proves each path resolved to itself.
it('hides domain search, buy and zone delegate from help but keeps them registered', () => {
const domainHelp = run(['domain', '--help']).stdout
const domain = commandNames(domainHelp)
expect(domain).not.toContain('search')
expect(domain).not.toContain('buy')
expect(domain).toContain('zone')
expect(commandNames(run(['domain', 'zone', '--help']).stdout)).toEqual(['list', 'records', 'release'])
expect(`${run(['--help']).stdout}${domainHelp}`).not.toMatch(/\bbuy\b|zone delegate/i)
for (const [path, usage] of [
[['domain', 'search'], 'Usage: insta domain search [options] <keyword>'],
[['domain', 'buy'], 'Usage: insta domain buy [options] <name>'],
[['domain', 'zone', 'delegate'], 'Usage: insta domain zone delegate [options] <domain>'],
] as const) {
const r = run([...path, '--help'])
expect(r.status, path.join(' ')).toBe(0)
expect(r.stdout, path.join(' ')).toContain(usage)
}
}, 30_000)
it('reads source-build output under `build logs`, not a top-level `build-logs`', () => {
expect(commandNames(run(['build', '--help']).stdout)).toContain('logs')
const r = run(['build', 'logs', '--help'])
Expand Down
13 changes: 7 additions & 6 deletions test/zone.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ describe('domain zone delegate', () => {
// The review-then-switch contract is the safety of the whole flow — it prints every time.
expect(out()).toContain('insta domain zone records byo.example')
expect(out()).toContain('only then switch the nameservers')
// BYO delegation is paused (platform #687), so re-running delegate is no longer a step.
expect(out()).not.toContain('re-running delegate')
})
it('under --org naming ANOTHER org the call goes projectless — the wrong project must not sign', async () => {
const { deps: d, calls } = deps({}, { status: 200, body: awaiting })
Expand Down Expand Up @@ -72,6 +74,7 @@ describe('domain zone list', () => {
const { deps: d2 } = deps({ '/zones': { items: [] } })
await zoneList({}, d2)
expect(out()).toContain('no delegated zones')
expect(out()).not.toContain('zone delegate')
})
it('combines pages into the existing JSON response and stops on the terminal cursor', async () => {
const second = { ...awaiting, domainName: 'second.example' }
Expand All @@ -93,7 +96,7 @@ describe('domain zone list', () => {
})

describe('domain zone records', () => {
it('lists EVERY type (the review must see CAA) and prints the add-then-reimport reminder', async () => {
it('lists EVERY type (the review must see CAA) and prints the compare-before-switching reminder', async () => {
const { deps: d, calls } = deps({
'/zones/byo.example/records': { items: [
{ type: 'CNAME', host: '@', answer: 'edge.example', proxied: true },
Expand All @@ -105,11 +108,9 @@ describe('domain zone records', () => {
expect(calls[0]).toMatchObject({ method: 'GET', path: '/orgs/org1/zones/byo.example/records' })
expect(out()).toContain('CAA')
expect(out()).toContain('(proxied)')
// The instruction is copy-pasteable VERBATIM: domain and org scope included.
expect(out()).toContain('re-run `insta domain zone delegate byo.example` to re-import')
stdout.length = 0
await zoneRecords('Byo.Example', { org: 'org9' }, d)
expect(out()).toContain('re-run `insta domain zone delegate byo.example --org org9` to re-import')
expect(out()).toContain('compare them against your current DNS before switching nameservers')
// BYO delegation is paused (platform #687): the reminder no longer sends anyone to re-run it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The removed zoneRecords('Byo.Example', { org: 'org9' }) case was the only test of --org routing and domain normalization for zone records. Keep a call-path assertion for it, such as checking the GET path is /orgs/org9/zones/byo.example/records, even though the reminder assertion is gone.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/zone.test.ts, line 112:

<comment>The removed `zoneRecords('Byo.Example', { org: 'org9' })` case was the only test of `--org` routing and domain normalization for `zone records`. Keep a call-path assertion for it, such as checking the GET path is `/orgs/org9/zones/byo.example/records`, even though the reminder assertion is gone.</comment>

<file context>
@@ -105,11 +108,9 @@ describe('domain zone records', () => {
-    await zoneRecords('Byo.Example', { org: 'org9' }, d)
-    expect(out()).toContain('re-run `insta domain zone delegate byo.example --org org9` to re-import')
+    expect(out()).toContain('compare them against your current DNS before switching nameservers')
+    // BYO delegation is paused (platform #687): the reminder no longer sends anyone to re-run it.
+    expect(out()).not.toContain('zone delegate')
   })
</file context>

expect(out()).not.toContain('zone delegate')
})
it('says plainly when the scan has not landed yet', () => {
expect(zoneRecordLines([])[0]).toContain('run this again in a moment')
Expand Down
Loading