diff --git a/.agent/context/20260912T172804Z-clarify-payment-services.md b/.agent/context/20260912T172804Z-clarify-payment-services.md new file mode 100644 index 0000000..e2191c7 --- /dev/null +++ b/.agent/context/20260912T172804Z-clarify-payment-services.md @@ -0,0 +1,66 @@ +# Session: clarify payment service labels + +## Date/time + +2026-09-12 17:28 UTC. + +## User goal and original request + +Rename the website surfaces that currently imply two external API products. The +first flow is a direct Arc Testnet USDC payment with a team-operated sample +result. The second flow buys a dataset from OneShot's own x402 seller. + +## Assumptions and acceptance + +- Change user-facing copy and matching tests only. +- Preserve tool identifiers, API contracts, payment behavior, recipients, and + settlement logic. +- Label the team-operated x402 seller explicitly. +- Keep the direct payment purpose as the existing `report_subject` payload field. + +## Plan and decisions + +- Rename `API services` to `Payment services`. +- Rename `Company research service` to `Direct Arc payment`. +- Rename `Company or domain` to `Payment purpose`. +- Rename `Circle Dataset API` to `OneShot x402 Dataset` and identify it as a + team-operated demo. + +## Files/components touched + +- `apps/web/src/App.tsx` +- `apps/web/src/components/JobWorkspace.tsx` +- `apps/web/src/components/workspace-copy.ts` +- Focused web unit and browser tests. + +## Commands/checks + +- `pnpm --filter @oneshot/web exec vitest run --config vitest.config.ts --pool=threads --maxWorkers=1 --no-file-parallelism` — PASS (18 files, 83 tests). +- `pnpm --filter @oneshot/web typecheck` — PASS after clearing incomplete generated output left by a full disk. +- `pnpm lint` — PASS. +- `pnpm format:check` — PASS. +- `pnpm --filter @oneshot/web test:browser` — PASS (8 Playwright tests). +- `git diff --check` — PASS. + +## External-doc findings + +None. This change follows current repository behavior. + +## Unresolved questions + +None for this copy-only scope. + +## Branch/commit/PR state + +- Branch: `fix/clarify-payment-services` +- Base: `origin/develop` at `ff146e49407b078183ed619ff3f3b2e11b17da2c`. +- Commit and PR: pending. + +## Gate state + +- Gate A: explicitly waived by the user on 2026-09-12. +- Gate B: explicitly waived by the user on 2026-09-12. + +## Handoff/next steps + +Commit the validated candidate, push the branch, and open a PR for human review. diff --git a/apps/web/browser/p5.spec.ts b/apps/web/browser/p5.spec.ts index 390bf5d..aad7611 100644 --- a/apps/web/browser/p5.spec.ts +++ b/apps/web/browser/p5.spec.ts @@ -98,7 +98,7 @@ async function mockJobApi( async function unlockWorkspace(page: Page): Promise { await page.getByText('Machine token (advanced)').click(); await page.getByLabel('Machine token').fill('browser-memory-token'); - await expect(page.getByRole('tab', { name: 'API services' })).toBeVisible(); + await expect(page.getByRole('tab', { name: 'Payment services' })).toBeVisible(); } test.describe('resumable job workspace', () => { @@ -123,8 +123,8 @@ test.describe('resumable job workspace', () => { const calls = await mockJobApi(page, { startDelayMs: 1000, resumeDelayMs: 1000 }); await page.goto('/app'); await unlockWorkspace(page); - await page.getByRole('tab', { name: 'API services' }).click(); - await page.getByLabel('Company or domain').fill('acme.com'); + await page.getByRole('tab', { name: 'Payment services' }).click(); + await page.getByLabel('Payment purpose').fill('acme.com'); await page .getByLabel('Service destination wallet') .fill('0x1111111111111111111111111111111111111111'); @@ -132,7 +132,7 @@ test.describe('resumable job workspace', () => { await page.getByText('Request key (advanced)').click(); await expect(page.locator('#generated-task-key')).toHaveValue(/report-acme-com-/u); await page - .getByRole('region', { name: 'Company research service' }) + .getByRole('region', { name: 'Direct Arc payment' }) .getByRole('button', { name: 'Review payment details' }) .click(); await expect.poll(() => calls.filter((call) => call === 'POST /v1/jobs/quote')).toHaveLength(1); @@ -140,7 +140,7 @@ test.describe('resumable job workspace', () => { await expect(page.getByRole('heading', { name: 'Review before approval' })).toBeVisible(); await expect(page.getByText('Nothing has been paid yet.')).toBeVisible(); const approve = page - .getByRole('region', { name: 'Company research service' }) + .getByRole('region', { name: 'Direct Arc payment' }) .locator('button') .last(); await approve.click(); @@ -233,19 +233,19 @@ for (const theme of ['light', 'dark'] as const) { expect(identity).not.toBeNull(); expect(header?.x).toBeCloseTo(identity!.x, 0); expect(header?.width).toBeCloseTo(identity!.width, 0); - for (const label of ['Overview', 'API services', 'Requests', 'Payment proof']) { + for (const label of ['Overview', 'Payment services', 'Requests', 'Payment proof']) { await page.getByRole('tab', { name: label, exact: true }).click(); await page.getByRole('tab', { name: label, exact: true }).hover(); await page.getByRole('tab', { name: label, exact: true }).focus(); - if (label === 'API services') { + if (label === 'Payment services') { await page.getByText('Request key (advanced)').click(); - await page.getByLabel('Company or domain').fill('acme.com'); + await page.getByLabel('Payment purpose').fill('acme.com'); await page .getByLabel('Service destination wallet') .fill('0x1111111111111111111111111111111111111111'); await page.getByLabel('Amount (USDC)').fill('2.5'); await page - .getByRole('region', { name: 'Company research service' }) + .getByRole('region', { name: 'Direct Arc payment' }) .getByRole('button', { name: 'Review payment details' }) .click(); await expect(page.getByRole('heading', { name: 'Review before approval' })).toBeVisible(); diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 8a2f1fe..4a8bd68 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -79,8 +79,8 @@ function LandingPage(props: { readonly theme: Theme; readonly onToggleTheme: ()

RESUMABLE PAID SERVICES / ARC TESTNET

Resume the job, not the payment.

- Approve one company-data report. If an agent restarts, the original task, payment - evidence and supplier result stay together. + Approve one direct Arc payment or paid x402 request. If an agent restarts, the original + task, payment evidence and result stay together.

One job. Many retries. At most one committed settlement. Team-operated testnet @@ -149,7 +149,7 @@ function CabinetPage(props: { const [activityError, setActivityError] = useState(null); const labels = { overview: 'Overview', - services: 'API services', + services: 'Payment services', requests: 'Requests', protection: 'Payment proof', } as const; @@ -203,9 +203,9 @@ function CabinetPage(props: { the permitted amount and recipient.

  • - Prepare a request. Open API services. For the team report, enter a - subject, recipient and amount, then review payment details. Circle Dataset API gets - its price from the service. + Prepare a request. Open Payment services. For a direct Arc payment, + enter its purpose, recipient and amount, then review the details. OneShot x402 Dataset + gets its price from the team-operated demo seller.
  • Approve deliberately. Read “Recipient receives”, the destination and @@ -213,14 +213,14 @@ function CabinetPage(props: { payment request.
  • - Read the result. Open Requests for a team report. For Circle Dataset - API, use Check payment status in its service card. Inspect the actual payment state - and result. + Read the result. Open Requests for a direct Arc payment. For OneShot + x402 Dataset, use Check payment status in its service card. Inspect the actual payment + state and result.
  • - Demonstrate recovery. For a team report, resume the existing result - from Requests. For Circle, replay the same request only when its payment is confirmed. - An uncertain payment needs investigation, not a new key. + Demonstrate recovery. For a direct Arc payment, resume the existing + sample result from Requests. For x402, replay the same request only when its payment is + confirmed. An uncertain payment needs investigation, not a new key.
  • diff --git a/apps/web/src/components/JobWorkspace.tsx b/apps/web/src/components/JobWorkspace.tsx index d9cbacd..1bf0ce2 100644 --- a/apps/web/src/components/JobWorkspace.tsx +++ b/apps/web/src/components/JobWorkspace.tsx @@ -259,18 +259,18 @@ export function JobWorkspace(props: { return (

    -

    API SERVICE

    -

    Company research service

    +

    DIRECT PAYMENT

    +

    Direct Arc payment

    - Prepare a team-operated company report request. You choose the destination and payment - amount; preview confirms those details before you approve a payment. + Send USDC directly to a reviewed recipient on Arc Testnet. OneShot keeps the payment and + its team-operated sample result bound to one request across retries.

    - + +
    -

    CONNECTED API SERVICE

    -

    Circle Dataset API

    +

    TEAM-OPERATED X402 DEMO

    +

    OneShot x402 Dataset

    Arc Testnet

    - Get a dataset result through Circle’s payment rail. OneShot keeps one request key so a retry - reuses the original payment instead of charging twice. The connected wallet signs the exact - payment to the API seller; OneShot never substitutes its own wallet. + Buy a demo dataset from OneShot’s own seller through Circle x402. OneShot keeps one request + key so a retry reuses the original payment instead of charging twice. When a connected + wallet is used, it signs the exact payment to the API seller; OneShot never substitutes its + own wallet.

    {props.userWallet && (

    @@ -828,7 +829,7 @@ export function JobList(props: { {loading ? (

    Checking requests…

    ) : jobs.length === 0 ? ( -

    No requests yet. Open API services to start a supported request.

    +

    No requests yet. Open Payment services to start a supported request.

    ) : (
      {jobs.map((job, index) => { diff --git a/apps/web/src/components/workspace-copy.ts b/apps/web/src/components/workspace-copy.ts index 1b0c1b5..a069530 100644 --- a/apps/web/src/components/workspace-copy.ts +++ b/apps/web/src/components/workspace-copy.ts @@ -78,9 +78,9 @@ export function deliveryStatusCopy(state: DeliveryState): StatusCopy { export function serviceLabel(toolId: string): string { switch (toolId) { case 'circle-x402-api-v1': - return 'Circle Dataset API'; + return 'OneShot x402 Dataset'; case 'team-report-v1': - return 'Company research service'; + return 'Direct Arc payment'; default: return 'Paid API service'; } diff --git a/apps/web/test/app-composition.test.tsx b/apps/web/test/app-composition.test.tsx index de5e83e..459f8dd 100644 --- a/apps/web/test/app-composition.test.tsx +++ b/apps/web/test/app-composition.test.tsx @@ -41,7 +41,7 @@ describe('Gate P5 shell composition', () => { />, ); expect(screen.getByRole('heading', { name: 'Your payment workspace', level: 1 })).toBeTruthy(); - expect(screen.getByRole('tab', { name: 'API services' })).toBeTruthy(); + expect(screen.getByRole('tab', { name: 'Payment services' })).toBeTruthy(); expect(screen.getByRole('tab', { name: 'Requests' })).toBeTruthy(); expect(screen.getByRole('tab', { name: 'Payment proof' })).toBeTruthy(); }); @@ -67,7 +67,7 @@ describe('Gate P5 shell composition', () => { expect(screen.getAllByRole('tab').map((tab) => tab.textContent)).toEqual([ 'Overview', - 'API services', + 'Payment services', 'Requests', 'Payment proof', ]); @@ -77,7 +77,7 @@ describe('Gate P5 shell composition', () => { expect(screen.queryByRole('tab', { name: 'Team & access' })).toBeNull(); }); - it('gives API services and Requests distinct responsibilities', async () => { + it('gives Payment services and Requests distinct responsibilities', async () => { const user = userEvent.setup(); const jobClient = { async list() { @@ -120,14 +120,14 @@ describe('Gate P5 shell composition', () => { />, ); - await user.click(screen.getByRole('tab', { name: 'API services' })); - expect(screen.getByRole('heading', { name: 'Company research service' })).toBeTruthy(); + await user.click(screen.getByRole('tab', { name: 'Payment services' })); + expect(screen.getByRole('heading', { name: 'Direct Arc payment' })).toBeTruthy(); expect(screen.queryByRole('heading', { name: 'Requests and results', level: 2 })).toBeNull(); await user.click(screen.getByRole('tab', { name: 'Requests' })); expect( await screen.findByRole('heading', { name: 'Requests and results', level: 2 }), ).toBeTruthy(); - expect(screen.getByText(/Open API services to start/u)).toBeTruthy(); + expect(screen.getByText(/Open Payment services to start/u)).toBeTruthy(); }); it('mounts A05, B05, and C05 without a settlement bypass', async () => { diff --git a/apps/web/test/components.test.tsx b/apps/web/test/components.test.tsx index 69218be..5dc1606 100644 --- a/apps/web/test/components.test.tsx +++ b/apps/web/test/components.test.tsx @@ -171,7 +171,7 @@ describe('JobWorkspace payment inputs', () => { }; render( undefined} />); - await user.type(screen.getByLabelText('Company or domain'), 'acme.com'); + await user.type(screen.getByLabelText('Payment purpose'), 'acme.com'); await user.type( screen.getByLabelText('Service destination wallet'), '0x2222222222222222222222222222222222222222',