diff --git a/AGENTS.md b/AGENTS.md index eababcbd2..ab46e6fc9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,8 @@ Vocs-powered documentation site for Tempo protocol. Use this guidance when creat - `src/lib/docs-sections.ts` owns the section labels, landing routes, and route ownership; `vocs.config.ts` owns the sidebar within each section. - Primary sections: Get Started (`/get-started`), Accounts (`/docs/accounts`), Earn (`/docs/earn`), Routes (`/docs/routes`), Zones (`/docs/zones`), and Machine Payments (`/docs/agents`). Get Started also owns the SDK, CLI, wallet CLI, and server handler pages (`/docs/sdk`, `/docs/cli`, `/docs/wallet`, `/docs/server`) as its Tempo CLI and Tempo SDK sections. -- Tempo EVM (`/docs/development`) covers wallets and signing keys, transactions, fees, TIP-20 Tokens, contracts, exchange, network details, nodes, protocol specifications, and changelog. +- Tempo EVM (`/docs/development`) covers wallets and signing keys, transactions, fees, TIP-20 Tokens, contracts, exchange, network details, and protocol specifications. +- Nodes (`/docs/guide/node`) covers node installation, RPC and validator operations, security, monitoring, failover, and operator updates. Changelog (`/docs/protocol/upgrades`) covers network upgrades and node releases (`/docs/changelog`). Both have their own sidebars and shared navigation links beside API reference. - API reference (`/docs/api`) is its own top-nav link, matching Console's "API Docs" link. Partners (`/docs/partners`) contains third-party integrations. The old APIs & SDKs hub (`/docs/tools`) redirects to `/docs/sdk`. - Keep Earn and Routes beta notices and the Zones limited-preview notice on their overview pages. The earlier `/docs/guide/private-zones` sandbox is a separate integration. - `SKILL.md` is the docs site's published skill. The installable plugin's skill lives in `tempoxyz/plugins`; update it separately when routing changes. diff --git a/SKILL.md b/SKILL.md index e3e623879..600e85327 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,6 +1,6 @@ --- name: tempo-docs -description: Find and explain official Tempo documentation for stablecoin accounts and payments, Earn vaults, Routes, Zones, Machine Payments, Tempo EVM, APIs, SDKs, CLI tools, and ecosystem integrations. Use for Tempo integration questions, code examples, protocol behavior, and documentation lookup. +description: Find and explain official Tempo documentation for stablecoin accounts and payments, Earn vaults, Routes, Zones, Machine Payments, Tempo EVM, APIs, SDKs, CLI tools, node operations, changelog, and ecosystem integrations. Use for Tempo integration questions, code examples, protocol behavior, and documentation lookup. --- # Tempo Docs @@ -19,8 +19,10 @@ All links below use the canonical docs mount at `https://tempo.xyz/developers`. | [Routes](https://tempo.xyz/developers/docs/routes.md) | Accept cross-network deposits, quote transfers, or track delivery. Routes is in beta; check access on the introduction page. | | [Zones](https://tempo.xyz/developers/docs/zones.md) | Connect to a Zone and work with private balances, deposits, or withdrawals. Zones is in limited preview; check access on the introduction page. | | [Machine Payments](https://tempo.xyz/developers/docs/agents.md) | Pay for APIs, accept API payments, discover services, or integrate MPP. | -| [Tempo EVM](https://tempo.xyz/developers/docs/development.md) | Build wallet support, manage signing keys, use transactions and fees, issue TIP-20 Tokens, deploy contracts, exchange stablecoins, run nodes, or read protocol specifications and changelog. | +| [Tempo EVM](https://tempo.xyz/developers/docs/development.md) | Build wallet support, manage signing keys, use transactions and fees, issue TIP-20 Tokens, deploy contracts, exchange stablecoins, or read protocol specifications. | | [API reference](https://tempo.xyz/developers/docs/api.md) | Find Tempo API endpoints by product, authentication, Console management endpoints, JSON-RPC, and the indexer. | +| [Nodes](https://tempo.xyz/developers/docs/guide/node.md) | Install, run, secure, monitor, troubleshoot, and upgrade RPC, standby, or validator nodes. | +| [Changelog](https://tempo.xyz/developers/docs/protocol/upgrades.md) | Track network upgrades, protocol activation, integration impact, and node software releases. | | [Partners](https://tempo.xyz/developers/docs/partners.md) | Find third-party wallets, bridges, RPC infrastructure, data services, and integrations. | Accounts covers application payment workflows; Tempo EVM covers the chain capabilities behind them. Routes covers cross-network movement. The API reference supplements product guides with exact endpoints and schemas. SDKs and the CLI are under Get Started. diff --git a/e2e/docs-workspace.test.ts b/e2e/docs-workspace.test.ts index 1cb7a6aff..7a7ed264c 100644 --- a/e2e/docs-workspace.test.ts +++ b/e2e/docs-workspace.test.ts @@ -9,7 +9,7 @@ const sectionLabels = [ 'Machine Payments', 'Tempo EVM', ] -const utilityLabels = ['API reference', 'Partners'] +const utilityLabels = ['API reference', 'Nodes', 'Changelog', 'Partners'] const externalTools = [ ['MPP', 'https://mpp.dev/'], ['Mercator', 'https://mercator.sh/'], @@ -200,7 +200,6 @@ for (const section of [ '/docs/guide/issuance/create-a-stablecoin', '/docs/guide/stablecoin-dex/executing-swaps', '/docs/guide/issuance/manage-stablecoin', - '/docs/guide/node/installation', ], }, { @@ -234,7 +233,16 @@ for (const section of [ paths: ['/docs/api/console', '/docs/api/console/api-keys', '/docs/api/team'], }, { - label: 'Tempo EVM (Changelog)', + label: 'Nodes', + overview: '/docs/guide/node', + paths: [ + '/docs/guide/node/installation', + '/docs/guide/node/validator-keys', + '/docs/guide/node/network-upgrades', + ], + }, + { + label: 'Changelog', overview: '/docs/protocol/upgrades', paths: ['/docs/protocol/upgrades/t11', '/docs/changelog'], }, @@ -344,23 +352,84 @@ test('Earn task pages and API reference remain distinct', async ({ page }) => { await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('API reference') }) -test('Changelog lives inside Tempo EVM and retains upgrade URLs', async ({ page }) => { +test('Changelog has its own sidebar and retains upgrade and release URLs', async ({ page }) => { await page.goto('/docs/development') const sidebar = page.locator('[data-v-gutter-left] [data-v-sidebar-container]') - await expect(page.locator('.docs-section-utilities').getByText('Changelog')).toHaveCount(0) - await sidebar.getByRole('button', { name: 'Changelog', exact: true }).click() - await sidebar.locator('a[href="/docs/protocol/upgrades"]').click() - await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('Tempo EVM') + await expect(sidebar.getByRole('button', { name: 'Changelog', exact: true })).toHaveCount(0) + await expect(sidebar.getByRole('button', { name: 'Run a node', exact: true })).toHaveCount(0) + await page + .locator('.docs-section-utilities') + .getByRole('link', { name: 'Changelog', exact: true }) + .click() + await expect(page).toHaveURL(/\/docs\/protocol\/upgrades\/?$/) + await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('Changelog') + await expect(sidebar.locator('a[href="/docs/development"]')).toHaveCount(0) await sidebar.locator('a[href="/docs/protocol/upgrades/t11"]').click() await expect(page.getByRole('heading', { level: 1 })).toHaveText('T11 Network Upgrade') expect((await page.reload())?.status()).toBe(200) - await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('Tempo EVM') + await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('Changelog') await sidebar.getByRole('link', { name: 'Node releases', exact: true }).click() await expect(page).toHaveURL(/\/docs\/changelog\/?$/) - await sidebar.locator('a[href="/docs/development"]').click() - await expect(page).toHaveURL(/\/docs\/development\/?$/) + await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('Changelog') + await sidebar.getByRole('link', { name: 'Operator updates', exact: true }).click() + await expect(page).toHaveURL(/\/docs\/guide\/node\/network-upgrades\/?$/) + await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('Nodes') + await expect(sidebar.getByRole('link', { name: 'Run a node', exact: true })).toBeVisible() }) +for (const mobile of [false, true]) { + test(`${mobile ? 'mobile' : 'desktop'} Nodes navigation opens its own operating guides`, async ({ + page, + }) => { + await page.setViewportSize(mobile ? { width: 390, height: 844 } : { width: 1440, height: 1000 }) + await page.goto('/docs/development') + if (mobile) { + await page.getByRole('button', { name: 'Open menu', exact: true }).click() + await page + .getByRole('dialog', { name: 'Documentation navigation', exact: true }) + .getByRole('link', { name: 'Nodes', exact: true }) + .click() + } else { + await page + .locator('.docs-section-utilities') + .getByRole('link', { name: 'Nodes', exact: true }) + .click() + } + await expect(page).toHaveURL(/\/docs\/guide\/node\/?$/) + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Run a node') + if (mobile) + await page.getByRole('button', { name: 'Open docs navigation', exact: true }).click() + const sidebar = mobile + ? page.getByRole('dialog', { name: 'Documentation', exact: true }) + : page.locator('[data-v-gutter-left] [data-v-sidebar-container]') + await expect(sidebar.getByRole('link', { name: 'Installation', exact: true })).toBeVisible() + await expect(sidebar.getByRole('link', { name: 'Operator updates', exact: true })).toBeVisible() + await expect(sidebar.locator('a[href="/docs/development"]')).toHaveCount(0) + await developerDisclosure(sidebar, 'Run a validator', mobile).click() + await sidebar.getByRole('link', { name: 'Managing validator keys', exact: true }).click() + await expect(page).toHaveURL(/\/docs\/guide\/node\/validator-keys\/?$/) + expect((await page.reload())?.status()).toBe(200) + if (mobile) + await page.getByRole('button', { name: 'Open docs navigation', exact: true }).click() + await expectOnlyActiveLeaf(sidebar, '/docs/guide/node/validator-keys') + await expect( + sidebar.getByRole('link', { name: 'Validator failover', exact: true }), + ).toBeVisible() + await sidebar.getByRole('link', { name: 'Operator updates', exact: true }).click() + await expect(page).toHaveURL(/\/docs\/guide\/node\/network-upgrades\/?$/) + if (mobile) { + await page.getByRole('button', { name: 'Open menu', exact: true }).click() + await expect( + page + .getByRole('dialog', { name: 'Documentation navigation', exact: true }) + .getByRole('link', { name: 'Nodes', exact: true }), + ).toHaveAttribute('aria-current', 'page') + } else { + await expect(page.locator('.docs-section-nav [aria-current="page"]')).toHaveText('Nodes') + } + }) +} + test('API reference is a keyboard-accessible utility and the old tools hub redirects', async ({ page, }) => { @@ -399,16 +468,12 @@ for (const path of ['/', '/blog']) { await expect(link).toHaveAttribute('target', '_blank') await expect(link).toHaveAttribute('rel', /noopener/) } - await menu.getByRole('link', { name: 'Tempo EVM', exact: true }).click() - await expect(page).toHaveURL(/\/docs\/development\/?$/) + await menu.getByRole('link', { name: 'Changelog', exact: true }).click() + await expect(page).toHaveURL(/\/docs\/protocol\/upgrades\/?$/) await expect(menu).toBeHidden() await expect(page.locator('body')).not.toHaveCSS('overflow', 'hidden') await page.getByRole('button', { name: 'Open docs navigation', exact: true }).click() const drawer = page.getByRole('dialog', { name: 'Documentation', exact: true }) - await drawer - .locator('summary') - .filter({ hasText: /^Changelog$/ }) - .click() await drawer.locator('a[href="/docs/protocol/upgrades/t12"]').click() await expect(page).toHaveURL(/\/docs\/protocol\/upgrades\/t12\/?$/) await expect(drawer).toBeHidden() @@ -732,7 +797,7 @@ test('mobile article drawer traps focus and survives client navigation', async ( const drawer = page.getByRole('dialog', { name: 'Documentation', exact: true }) const close = drawer.getByRole('button', { name: 'Close docs navigation', exact: true }) await expect(close).toBeFocused() - const specifications = drawer.locator('summary').filter({ hasText: /^Changelog$/ }) + const specifications = drawer.getByRole('link', { name: 'All specifications', exact: true }) await page.keyboard.press('Shift+Tab') await expect(specifications).toBeFocused() await page.keyboard.press('Tab') diff --git a/src/components/DocsHeader.test.ts b/src/components/DocsHeader.test.ts index 4b6edc98d..c4cd481c8 100644 --- a/src/components/DocsHeader.test.ts +++ b/src/components/DocsHeader.test.ts @@ -61,7 +61,7 @@ describe('getActiveDocsSection', () => { it('keeps section navigation active under the developers mount and trailing slashes', () => { expect(getActiveDocsSection('/developers/docs/api/transfers/')?.id).toBe('api') expect(getActiveDocsSection('/developers/get-started/')?.id).toBe('overview') - expect(getActiveDocsSection('/developers/docs/protocol/upgrades/t12/')?.id).toBe('developers') + expect(getActiveDocsSection('/developers/docs/protocol/upgrades/t12/')?.id).toBe('changelog') expect(getActiveDocsSection('/developers/docs/protocol/')?.label).toBe('Tempo EVM') }) @@ -138,13 +138,15 @@ describe('getActiveDocsSection', () => { ['/docs/protocol/tip403/spec', 'developers'], ['/docs/protocol/exchange/spec', 'developers'], ['/docs/protocol/blockspace/payment-lane-specification', 'developers'], - ['/docs/guide/node/installation', 'developers'], - ['/docs/protocol/upgrades', 'developers'], - ['/docs/protocol/upgrades/t11', 'developers'], - ['/docs/protocol/upgrades/t12', 'developers'], - ['/docs/guide/node/upgrade-cadence', 'developers'], - ['/docs/guide/node/network-upgrades', 'developers'], - ['/docs/changelog', 'developers'], + ['/docs/guide/node', 'nodes'], + ['/docs/guide/node/installation', 'nodes'], + ['/docs/guide/node/validator-keys', 'nodes'], + ['/docs/protocol/upgrades', 'changelog'], + ['/docs/protocol/upgrades/t11', 'changelog'], + ['/docs/protocol/upgrades/t12', 'changelog'], + ['/docs/guide/node/upgrade-cadence', 'nodes'], + ['/docs/guide/node/network-upgrades', 'nodes'], + ['/docs/changelog', 'changelog'], ['/docs/api/indexer-api', 'api'], ['/docs/protocol/rpc/eth_getBalance', 'developers'], ['/docs/sdk/typescript', 'overview'], diff --git a/src/lib/ai-docs-dev.test.ts b/src/lib/ai-docs-dev.test.ts index a5323cebf..f7c52f711 100644 --- a/src/lib/ai-docs-dev.test.ts +++ b/src/lib/ai-docs-dev.test.ts @@ -107,6 +107,8 @@ describe('development AI Markdown', () => { expect(output.indexOf('')).toBeGreaterThan(second) expect(output.slice(0, second)).toContain('') expect(output).toContain('## API reference') + expect(output).toContain('## Nodes') + expect(output).toContain('## Changelog') expect(() => renderAiDevFull(full.replace('# Deposit', '## Deposit'))).toThrow( 'page headings for 2 entries', ) diff --git a/src/lib/ai-docs.test.ts b/src/lib/ai-docs.test.ts index a724fdec9..304f0384f 100644 --- a/src/lib/ai-docs.test.ts +++ b/src/lib/ai-docs.test.ts @@ -20,6 +20,10 @@ const input = `# Tempo Docs - [Machine Payments](/docs/agents): Pay for APIs. - [API reference](/docs/api/reference): REST schemas. - [Keys](/docs/accounts/keys): Chain signing permissions. +- [Install a node](/docs/guide/node/installation): Install the binary. +- [Operator updates](/docs/guide/node/network-upgrades): Required operator actions. +- [T12](/docs/protocol/upgrades/t12): Protocol upgrade. +- [Node releases](/docs/changelog): Software releases. - [Home](/index): Site home. ` @@ -50,6 +54,28 @@ describe('AI documentation navigation', () => { expect(renderAiIndex(output)).toBe(output) }) + it('groups node operations and release history in their own sections and page context', () => { + const sections = new Map( + renderAiIndex(input) + .split(/^## /m) + .slice(1) + .map((section) => [section.split('\n')[0], section]), + ) + expect(sections.get('Nodes')).toContain('[Install a node](/docs/guide/node/installation.md)') + expect(sections.get('Nodes')).toContain( + '[Operator updates](/docs/guide/node/network-upgrades.md)', + ) + expect(sections.get('Changelog')).toContain('[T12](/docs/protocol/upgrades/t12.md)') + expect(sections.get('Changelog')).toContain('[Node releases](/docs/changelog.md)') + expect(sections.get('Tempo EVM')).not.toMatch(/\[Install a node\]|\[T12\]|\[Node releases\]/) + for (const route of ['/docs/guide/node/validator-keys', '/docs/guide/node/network-upgrades']) + expect(renderAiPage('# Guide', route)).toContain('> Section: [Nodes](/docs/guide/node.md)') + for (const route of ['/docs/protocol/upgrades/t12', '/docs/changelog']) + expect(renderAiPage('# Changes', route)).toContain( + '> Section: [Changelog](/docs/protocol/upgrades.md)', + ) + }) + it('fails when a new docs route has no navigation owner', () => { expect(() => renderAiIndex('- [Unassigned](/docs/new-product)')).toThrow( 'no AI navigation home', diff --git a/src/lib/ai-docs.ts b/src/lib/ai-docs.ts index c550c3682..2d640ca19 100644 --- a/src/lib/ai-docs.ts +++ b/src/lib/ai-docs.ts @@ -7,7 +7,7 @@ import { } from './docs-sections' export const aiDocsDescription = - 'Build stablecoin products with Accounts, Earn, Routes, Zones, and Machine Payments, or build on Tempo EVM. Find API references, SDKs, CLI tools, and partner integrations.' + 'Build stablecoin products with Accounts, Earn, Routes, Zones, and Machine Payments, or build on Tempo EVM. Find API references, SDKs, CLI tools, node operations, changelog, and partner integrations.' const sectionDescriptions: Partial> = { overview: @@ -22,8 +22,12 @@ const sectionDescriptions: Partial> = { 'machine-payments': 'Pay for APIs, accept API payments, find services, and use MPP (https://mpp.dev).', developers: - 'Build on Tempo EVM: wallets and keys, transactions and fees, TIP-20 Tokens, contracts, exchange, network details, nodes, protocol specifications, and changelog.', + 'Build on Tempo EVM: wallets and keys, transactions and fees, TIP-20 Tokens, contracts, exchange, network details, and protocol specifications.', api: 'Tempo API reference: authentication, conventions, endpoints grouped by product, Console management endpoints, JSON-RPC, and the indexer. An API key authenticates API requests; an account signing key authorizes transactions.', + nodes: + 'Run and maintain Tempo RPC, standby, and validator nodes: installation, network identity, security, keys, monitoring, failover, troubleshooting, and required operator updates.', + changelog: + 'Track network upgrades, protocol activation and integration impact, and node software releases. Use Nodes for upgrade procedures and operator guidance.', ecosystem: 'Find wallet providers, bridges, RPC infrastructure, data services, and other third-party integrations.', } diff --git a/src/lib/docs-sections.ts b/src/lib/docs-sections.ts index 4abba2dd9..a997acf2b 100644 --- a/src/lib/docs-sections.ts +++ b/src/lib/docs-sections.ts @@ -6,6 +6,8 @@ export type DocsSection = { | 'routes' | 'zones' | 'developers' + | 'nodes' + | 'changelog' | 'machine-payments' | 'api' | 'tools' @@ -74,11 +76,9 @@ export const docsSections: DocsSection[] = [ '/docs/development', '/docs/quickstart', '/docs/protocol', - '/docs/changelog', '/docs/guide/tempo-transaction', '/docs/guide/issuance', '/docs/guide/stablecoin-dex', - '/docs/guide/node', ], }, ] @@ -90,6 +90,18 @@ export const docsUtilitySections: DocsSection[] = [ href: '/docs/api', matches: ['/docs/api'], }, + { + id: 'nodes', + label: 'Nodes', + href: '/docs/guide/node', + matches: ['/docs/guide/node'], + }, + { + id: 'changelog', + label: 'Changelog', + href: '/docs/protocol/upgrades', + matches: ['/docs/protocol/upgrades', '/docs/changelog'], + }, { id: 'ecosystem', label: 'Partners', diff --git a/src/lib/sidebar-coverage.test.ts b/src/lib/sidebar-coverage.test.ts index fcb18a532..eccfcff52 100644 --- a/src/lib/sidebar-coverage.test.ts +++ b/src/lib/sidebar-coverage.test.ts @@ -84,6 +84,11 @@ describe('specification navigation ownership', () => { ['/docs/protocol/transactions/AccountKeychain', 'developers', '/docs/development'], ['/docs/protocol/tip20/virtual-addresses', 'accounts', '/docs/accounts'], ['/docs/protocol/tip403/receive-policies', 'accounts', '/docs/accounts'], + ['/docs/guide/node/installation', 'nodes', '/docs/guide/node'], + ['/docs/guide/node/validator-keys', 'nodes', '/docs/guide/node'], + ['/docs/guide/node/network-upgrades', 'nodes', '/docs/guide/node'], + ['/docs/protocol/upgrades/t12', 'changelog', '/docs/protocol/upgrades'], + ['/docs/changelog', 'changelog', '/docs/protocol/upgrades'], ...['', '/architecture', '/accounts', '/bridging', '/rpc', '/execution', '/proving'].map( (suffix) => [`/docs/protocol/zones${suffix}`, 'zones', '/docs/zones'], ), diff --git a/vocs.config.ts b/vocs.config.ts index 344944715..03621f29f 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -699,11 +699,10 @@ export default defineConfig({ }, ] const nodeSidebar = { - text: 'Run a node', - collapsed: true, + text: 'Nodes', items: [ { - text: 'Overview', + text: 'Run a node', link: '/docs/guide/node', }, { @@ -724,6 +723,7 @@ export default defineConfig({ }, { text: 'Run a validator', + collapsed: true, items: [ { text: 'Overview', @@ -771,10 +771,13 @@ export default defineConfig({ text: 'Upgrade a node', link: '/docs/guide/node/upgrade-cadence', }, + { + text: 'Operator updates', + link: '/docs/guide/node/network-upgrades', + }, ], } const changelogSidebar = [ - { text: '← Back to Tempo EVM', link: '/docs/development' }, { text: 'Changelog', items: [ @@ -1016,17 +1019,7 @@ export default defineConfig({ { text: 'TIPs', link: 'https://tips.sh/' }, ], }, - { - text: 'Run a node', - collapsed: true, - items: nodeSidebar.items.map((item) => ({ - ...item, - ...(item.text === 'Overview' ? { text: 'Introduction' } : {}), - ...(item.items ? { collapsed: true } : {}), - })), - }, { text: 'All specifications', link: '/docs/protocol' }, - { ...changelogSidebar[1], collapsed: true }, ], }, ] @@ -1039,6 +1032,8 @@ export default defineConfig({ routes: routesSidebar, zones: zonesSidebar, developers: developersSidebar, + nodes: [nodeSidebar], + changelog: changelogSidebar, 'machine-payments': machinePaymentsSidebar, api: [{ text: 'API reference', items: [{ text: 'Introduction', link: '/docs/api' }] }], tools: toolsSidebar,