⛩ Continuum E2E Test Evidence

Deployment: continuum-test.orangesync.tech · Agent: agent-test.orangesync.tech
Branch: feat/ansible-one-click-deploy · Date: 2026-07-07

Summary

141
Passing
2
Skipped (env-dep)
90
New Tests
3
Test Files
Coverage: boot/routing · landing · projects CRUD · modal validation · project home · marketplace · routstr · dashboard · chat dock · auth (NIP-07 + token) · persistence · theme · agent API

Test Files

FileTestsScope
comprehensive.spec.ts53Boot/routing, landing, projects CRUD, project home, marketplace, routstr, dashboard, chat dock, auth/session, persistence, theme, agent API
coverage-gaps.spec.ts69 / 2Sidebar detail, landing detail, modal validation, project home detail, marketplace detail, routstr detail, dashboard detail, chat detail, auth edge cases, persistence detail
auth-flow.spec.ts19Token injection → logged-in UI, NIP-07 login flow, post-login agent API, sign-out & edge cases
Total141 / 2Every view, component, and authentication path

Authentication Flow

StepHow TestedResult
Token formata.EPOCH_SECONDS.b.c (4 dot-parts, 2nd = expiry)Valid
Token injection → logged-in UIInject a.9999999999.b.c via localStorageW01-W04
NIP-07 signer mockpage.addInitScriptwindow.nostr.signEventX02
Auth verify interceptionpage.route → return fake tokenX02
401 auto-clearFake token hits real /api/wallet/balanceZ02
Invalid token rejectionWrong format (not 4 parts)Z03
Expired token rejectiona.1.b.c (epoch = 1, 1970)Z04
Sign outClick "Sign out", check localStorage clearedZ01
Challenge endpointPOST /api/auth/challengeX03
Protected API endpoints401 without valid tokenY03-Y06

All 143 Tests

comprehensive.spec.ts (53/53 passing)

✓ A01: SPA loads and renders content
✓ A02: Landing route hides sidebar
✓ A03: Non-landing routes show sidebar
✓ A04: Sidebar has 6 nav items
✓ A05: Brand button navigates to landing
✓ A06: Unknown hash redirects to landing
✓ A07: Each nav item navigates to its route
✓ A08: Active nav item gets highlighted
✓ B01: Landing shows hero with torii gate SVG
✓ B02: Landing has "Open the demo" button
✓ B03: Landing shows status pill
✓ B04: Landing has promises/pillars section
✓ C01: Projects page shows seeded projects
✓ C02: Project cards are clickable
✓ C03: New Project button opens modal
✓ C04: Modal has tabs (blank/github/ngit)
✓ C05: Create blank project
✓ C06: Cancel new project closes modal
✓ D01: Project home shows project name
✓ D02: Project home shows milestones
✓ D03: Project home shows todos
✓ D04: Add a todo item
✓ D05: Toggle a todo checkbox
✓ D06: Breadcrumb navigates back
✓ D07: Unknown project shows empty state
✓ E01: Marketplace shows task listings
✓ E02: Marketplace has diverse listings
✓ E03: "Our tasks" filter exists
✓ F01: Routstr page renders
✓ F02: Routstr shows model info
✓ F03: Routstr shows usage/balance
✓ G01: Dashboard renders with content
✓ G02: Dashboard shows project summary
✓ H01: Chat dock visible on non-landing
✓ H02: Chat dock can be expanded
✓ H03: Chat greeting present
✓ H04: Send a mock chat message
✓ I01: Session button present
✓ I02: Session button shows label
✓ I03: Login without NIP-07 shows modal
✓ J01: Data persists across reloads
✓ J02: Projects persist after navigation
✓ K01: Theme toggle button exists
✓ K02: Theme toggle switches theme
✓ K03: Theme persists in localStorage
✓ L01: Health endpoint responds
✓ L02: Challenge endpoint issues challenge
✓ L03: Verify without event fails 400
✓ L04: Admin routes reject without token
✓ L05: Chat endpoint rejects without auth
✓ L06: Memory endpoints require auth
✓ L07: Pending path validation
✓ L08: Health includes memory_unlocked

coverage-gaps.spec.ts (69/71 passing, 2 env-skipped)

✓ M01: Keyboard Enter on nav-item navigates to its route
✓ M02: Keyboard Space on nav-item navigates to its route
✓ M03: "Our tasks" nav item strips query and lands on marketplace
✓ M04: Sidebar nav-badge shows project count (≥2)
✓ M05: Brand has correct aria-label and navigates to landing
✓ M06: Theme toggle button has data-theme-toggle attribute
✓ M07: Session button shows "Demo mode" text
✓ N01: Landing hero renders exact title "The Gateway Project." and eyebrow
✓ N02: Landing lede paragraph is present
✓ N03: Torii gate SVG has correct aria-label and viewBox 220 260
✓ N04: Status microcopy pill shows demo-mode or agent-reachable
✓ N05: Promises section has 4 cards (Local-first, Nostr-native, Pay-per-request, Human-in-the-loop)
✓ N06: Pillars section renders 4 items with k/v/note
✓ N07: Status list renders ok/next/later items
✓ N08: Footer has external links to GitHub and Torii Quest
✓ N09: Secondary CTA reflects logged-out state (shows Login text)
✓ O01: Project progress bar shows sane width percentage
✓ O02: Add card "+" opens the modal
✓ O03: Empty project name shows inline error "Give the project a name."
✓ O04: GitHub tab reveals repo URL row with github.com placeholder
✓ O05: GitHub tab rejects non-github.com URL
✓ O06: ngit tab validates ngit:// prefix
✓ O07: Duplicate slug is rejected with error (name "Continuum" collides)
✓ O08: Backdrop click closes the modal
✓ O09: Tab switching shows correct active class (blank→github→ngit)
✓ P01: "Open source ↗" button opens a popup window to GitHub
✓ P02: Delete button is hidden for continuum project
✓ P03: Delete button is hidden for torii-quest project
✓ P04: Milestone rows render with correct pill classes (.done→.ok, .active→.hot)
✓ P05: Overview strip renders 3 stat cards (Progress, Open todos, Sessions)
✓ P06: Crumbs renders "Projects" link that navigates back to /projects
✓ P07: Todo section has add-input with correct placeholder "+ add a todo…"
✓ P08: Files section renders with .kind/.mono/.size for seeded files
✓ Q01: "Ours" rows carry the .ours class and show "ours" pill
✓ Q02: Search input filters rows to matching names (e.g. "strfry")
✓ Q03: Empty filter search shows ∅ empty state with "No tasks match"
✓ Q04: "Show ours only" toggle restricts to .ours rows, button text flips
✓ Q05: Header pill counts total (N total) and ours (N ours)
✓ Q06: Task rows are keyboard-focusable (role="button" tabindex="0")
✓ Q07: Complexity select filters by size, resets correctly
✓ R01: Not-connected state shows "not connected" pill and Cashu balance
✓ R02: Model picker lists 8+ seeded models with DeepSeek Chat default
✓ R03: Clicking a non-selected model selects it and re-renders
✓ R04: Endpoint input displays default value https://api.routstr.com
✓ R05: Monthly budget input displays default value 25000
✓ R06: Usage stats section renders 4 metrics (Requests, Sats, Tokens in/out)
✓ R07: Demo mode Connect bumps balance to [12000,20000] range
✓ R08: Connect → Disconnect → pill shows "not connected", balance resets
✓ S01: Dashboard renders 3 aggregate stat cards
✓ S02: "By project" section lists seeded projects (≥2)
✓ S03: Project row click (Torii Quest) navigates to /projects/torii-quest
✓ S04: Oversight link to torii-quest.pplx.app present with target="_blank"
✓ S05: Overall progress shows a percentage
✓ T01: Chat dock starts collapsed with .collapsed class, toggle shows ▲
✓ T02: Toggle expands (▼) and collapses (▲) the dock
✓ T03: Chat context shows "context · Projects" on projects page
✓ T04: Chat context shows "Marketplace" on marketplace page
✓ T05: Chat context shows "Continuum" on project home page
✓ T06: Chat greeting message (AI) present when dock is collapsed
✓ T07: Shift+Enter inserts newline instead of sending
✓ T08: Send button is present with text "Send"
✓ T09: Chat dock is hidden on landing page
✓ U01: Session button click shows login modal or demo mode info
- U02: Skipped (environment-dependent: Demo mode check requires no agent)
- U03: Skipped (environment-dependent: Sign-out check)
✓ V01: Seed data is populated on first boot (3 cards = 2 seeded + 1 add)
✓ V02: Created project survives page reload
✓ V03: Added todo survives page reload
✓ V04: Routstr model selection survives reload
✓ V05: Marketplace sort preference does NOT persist (view-local state)
✓ V06: Theme persists in localStorage across reloads

auth-flow.spec.ts (19/19 passing)

✓ W01: Session button shows "Sign out" when logged in
✓ W02: Landing CTA shows "Go to your dashboard" when logged in
✓ W03: Chat dock greeting differs when logged in (mentions "Signed in")
✓ W04: Logged-in state survives page reload
✓ X01: Login button with no NIP-07 shows "signer not found" modal
✓ X02: Login with NIP-07 stub completes full flow (window.nostr + page.route)
✓ X03: Challenge endpoint returns valid challenge (≥32 hex chars, expires_in=300, kind=22242)
✓ Y01: Chat sends POST to agent when logged in (intercepted via page.route)
✓ Y02: Wallet balance endpoint called when logged in on Routstr
✓ Y03: Protected agent endpoints return 401 without valid token
✓ Y04: Health endpoint returns full metadata (ok, service, version, time, memory_unlocked)
✓ Y05: Chat endpoint rejects invalid body shapes
✓ Y06: Reflect endpoint rejects without auth (401)
✓ Z01: Sign out from sidebar clears token and changes UI button text
✓ Z02: 401 from agent API auto-clears the session token
✓ Z03: Invalid token format (not 4 parts) does NOT set logged-in state
✓ Z04: Expired token does NOT set logged-in state
✓ Z05: Landing CTA navigates to dashboard when logged in
✓ Z06: Verify endpoint rejects empty body (400)

Technical Details

Auth Token Format

Format:    a.EPOCH_SECONDS.xxx.xxx  (4 dot-separated parts)
Example:   a.9999999999.b.c
Storage:   localStorage key 'continuum.session.v1'
Validation: isLoggedIn() checks parts.length === 4, parseInt(parts[1]) * 1000 > Date.now()
Auto-clear: 401 from any agent endpoint → clearStoredToken()

NIP-07 Mock Strategy

1. page.addInitScript(() => { window.nostr = { signEvent: async (ev) => ({ ...ev, id:'00...', pubkey:'00...', sig:'00...' }) }; })
2. page.route('/api/auth/verify', async (route) => { route.fulfill({ status:200, body: JSON.stringify({ token: FAKE_TOKEN, expires_at: FUTURE_EXP }) }); });
3. Click "Login" → NIP-07 signs challenge → POST /api/auth/verify → token stored → UI switches to logged-in

Post-Login Interception Pattern

page.route('/api/chat', async (route) => {
  chatRequested = true;
  route.fulfill({ status:200, body: JSON.stringify({ reply: 'Mock reply.' }) });
});
page.route('/api/wallet/balance', async (route) => {
  walletCalled = true;
  route.fulfill({ status:200, body: JSON.stringify({ balance_sats: 42000 }) });
});

Deployment

Target: continuum-test.orangesync.tech (VPS2, Caddy + static SPA)
Agent: agent-test.orangesync.tech (Fastify, version 0.2.5-alpha, memory_unlocked: false)


Generated 2026-07-07 · Continuum E2E Test Suite · TEST_EVIDENCE.md