# BLUEPRINT: Stack Spent # Version: 2.1.0 # URL: https://stackspent.app # Updated: 2026-04-29 ## IDENTITY name: Stack Spent description: | Done in the moment. Never think about it again. Stack Spent is an AI expense tracker built around a single idea: the only moment you will ever actually deal with a receipt is the moment it exists. Snap at the fuel pump, the hardware store, the job site — the AI reads the merchant, amount, date, and taxes automatically. When tax season arrives, everything is already there. This is not a simpler QuickBooks. It does not ask you to change your behaviour or dedicate time to bookkeeping. It captures business activity as it naturally happens and gets out of your way. Stack Spent is completely standalone — it works on its own with no other apps required. It is also part of the Stack suite (Stack Billing, Stack Slip, Stack Tax, and more). Each suite app is built the same way: one thing, takes seconds, standalone. If a user has multiple Stack apps, they share the same login and data flows automatically between them. No exports. No manual syncing. category: finance contact: https://stackspent.app/faq ## AUTH provider: firebase methods: email-password, google-oauth ## ACCESS last-resort: ui ## SELECTOR NORMALIZATION description: | Some `data-agent-id` values are slugged in the app. Expand placeholders before CLICK/WAIT: - **Category** (`button-category-option-*`): lowercase, replace whitespace with `-`, strip characters except a-z, 0-9, hyphen. Empty → `category`. - **Project name** (`button-project-option-*`, `button-project-edit-*`, `button-project-delete-*`): same rules as category (lowercase, hyphenate, strip non-alphanumeric). - **Currency** (`button-currency-option-*`): ISO 4217 code lowercase (e.g. usd, cad). ## PRODUCT-NOTES localization: | The app shell runs a client-side translation layer (`GlobalTranslation`) so UI copy can follow the user or device language, in addition to the explicit Language control under Settings. ## ROUTES purpose: | Canonical map of client-side routes for discovery, support, and automation. "Admin email" means the signed-in user passes `isAdminEmail` in the client; others are redirected to `/`. - path: / auth: optional guest: Marketing landing—positioning, feature grid, StackApps ecosystem links (`button-invoicing-wizard`, `button-packingslip-wizard`, `button-stackapps`), CTAs `button-get-started` / `button-cta-bottom` → `/login`. signed_in: Home—`input-camera` / `button-camera`, Manual entry dialog (`button-manual-entry`), Batch upload (`button-batch-upload`), queue badge `text-queue-status`. - path: /login auth: public summary: Email/password and Google sign-in; success returns to `/`. - path: /shoebox auth: required summary: Expense list, Needs Review, filters, exports, project manager, custom categories, search, delete/review flows. When offline captures are pending or failed, `text-sync-status` with `button-clear-all`, `button-clear-failed`, `button-retry-failed`. Narrow screens use `button-export-mobile` for CSV menus. Manual entry opens with `button-add-expense`; submit the form with `button-submit-manual-entry`. While the dialog is open, header nav is blocked—close with `button-close-dialog` (or Escape) before `link-insights` / `link-shoebox` / etc. - path: /insights auth: required summary: Totals, tax rollups, category and project breakdowns (`text-insights-title`). - path: /settings auth: required summary: Profile email, Google unlink, theme/language/date format, insurance section, password & email forms, admin card (if eligible), destructive delete account. - path: /faq auth: public summary: FAQ accordion (`faq-item-*`, `button-faq-question-*`) and Contact Support dialog (`button-contact-support`). - path: /how-ai-works auth: public summary: Explains dual-AI receipt verification (`text-how-ai-title`). Implemented as a dedicated React page (not the Firestore static HTML renderer). - path: /expense-tracker-for-adhd auth: public summary: ADHD-focused landing (`text-adhd-landing-title`), FAQ accordion (`adhd-faq-item-*`, `button-adhd-faq-question-*`), CTA `button-adhd-cta-login`. - path: /privacy | /terms | /cookies auth: public summary: Legal / policy HTML loaded from Firestore `staticPages` (`static-page-content`). Back control `button-back`. - path: /admin/feedback auth: required + admin email summary: Review Firestore feedback (`text-admin-feedback-title`, filters `select-status-filter`, `select-app-filter`, cards `card-feedback-*`, actions `button-admin-feedback-status-*`, `button-admin-feedback-delete-*`). - path: /admin/taxes auth: required + admin email summary: Custom tax catalog editor (`text-admin-tax-catalog-title`, `button-add-tax`, rows `tax-row-*`, `button-admin-tax-edit-*`, `button-admin-tax-delete-*`, dialog fields `input-tax-*`, `button-save-tax`). global_chrome: | Header: `link-home`, `button-feedback`, `button-settings` / `button-logout` (signed in) or `button-login` (guest). Desktop nav: `link-nav-home`, `link-shoebox`, `link-insights`. Mobile bottom nav: `nav-home`, `nav-shoebox`, `nav-insights`. Footer: `link-faq`, `link-ai`, `link-privacy`, `link-terms`, `link-cookies`. ## CAPABILITY: login description: Sign in to an existing Stack Spent account with email and password input: - name: user-email type: string required: true description: Account email address. - name: user-password type: string required: true description: Account password. output: - type: confirmation description: User is signed in and redirected to the home dashboard at `/`. auth-required: false scope: form-submit ### UI steps: 1. NAVIGATE /login 2. CLICK [data-agent-id="tab-signin"] 3. INPUT [data-agent-id="input-signin-email"] <> 4. INPUT [data-agent-id="input-signin-password"] <> 5. CLICK [data-agent-id="button-signin"] 6. WAIT [data-agent-id="button-camera"] (max: 10s) 7. VERIFY url == "/" ## CAPABILITY: signup description: Create a new Stack Spent account with email and password input: - name: user-email type: string required: true description: Email for the new account. - name: user-password type: string required: true description: Password for the new account. output: - type: confirmation description: Account created and user lands on the home dashboard at `/`. auth-required: false scope: form-submit ### UI steps: 1. NAVIGATE /login 2. CLICK [data-agent-id="tab-signup"] 3. INPUT [data-agent-id="input-signup-email"] <> 4. INPUT [data-agent-id="input-signup-password"] <> 5. INPUT [data-agent-id="input-signup-confirm-password"] <> 6. CLICK [data-agent-id="button-signup"] 7. WAIT [data-agent-id="button-camera"] (max: 10s) 8. VERIFY url == "/" ## CAPABILITY: add-manual-expense description: Add an expense manually without scanning a receipt input: - name: merchant-name type: string required: true description: Merchant or payee name. - name: amount type: string required: true description: Expense amount. - name: category type: string required: true description: Expense category to select (use normalized slug for option button; see SELECTOR NORMALIZATION). - name: date type: string required: true description: Transaction date. - name: project-name type: string required: false description: Optional project or trip to assign (search by name; create flow uses button-project-create). - name: currency type: string required: false description: Optional currency code or symbol. - name: payment-method type: string required: false description: Optional payment method. output: - type: confirmation description: Expense saved and Shoebox search field remains available. auth-required: true scope: form-submit ### UI note: | `button-add-expense` only opens Manual Entry. After filling fields, submit with `button-submit-manual-entry`. Do not use Radix-generated ids (e.g. `#radix-:r3:`) in CSS selectors—colons break querySelector. To use header nav while the dialog is open, dismiss first via `button-close-dialog`. steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-add-expense"] 4. WAIT [data-agent-id="input-merchant"] (max: 5s) 5. INPUT [data-agent-id="input-merchant"] <> 6. INPUT [data-agent-id="input-amount"] <> 7. INPUT [data-agent-id="input-date"] <> (format: YYYY-MM-DD; this is a native text input, type directly) 8. (optional) CLICK [data-agent-id="button-currency-combobox-trigger"] 9. (optional) WAIT [data-agent-id="input-currency-search"] (max: 3s) 10. (optional) INPUT [data-agent-id="input-currency-search"] <> 11. (optional) CLICK [data-agent-id="button-currency-option-<>"] (normalize: lowercase code e.g. usd, cad) 12. CLICK [data-agent-id="button-category-combobox-trigger"] 13. WAIT [data-agent-id="input-category-search"] (max: 3s) 14. INPUT [data-agent-id="input-category-search"] <> 15. WAIT [data-agent-id="button-category-option-<>"] (max: 3s) (token: normalized category id; see SELECTOR NORMALIZATION) 16. CLICK [data-agent-id="button-category-option-<>"] 17. (optional) CLICK [data-agent-id="button-project-combobox-trigger"] 18. (optional) WAIT [data-agent-id="input-project-search"] (max: 3s) 19. (optional) INPUT [data-agent-id="input-project-search"] <> 20. (optional) CLICK [data-agent-id="button-project-create"] (if project doesn't exist) 21. (optional) WAIT [data-agent-id="button-project-combobox-trigger"] (max: 3s) 22. CLICK [data-agent-id="button-submit-manual-entry"] 23. WAIT 2s 24. VERIFY selector_exists [data-agent-id="input-search"] ## CAPABILITY: scan-receipt description: Submit a receipt image from the home capture control for AI extraction (processing runs in the background) input: - name: file-path type: file required: true description: Local path to a receipt image file. output: - type: confirmation description: File is queued, uploaded, and analyzed; an expense is created. High-confidence results may save as verified without a dialog; others appear under Shoebox → Needs Review. auth-required: true scope: form-submit ### UI note: | The Snap control on `/` does not open the review dialog. After upload, open `/shoebox` to inspect Needs Review or History. CRITICAL: `input-camera` is an sr-only file input (visually hidden but DOM-accessible). Do NOT use fill() or type() — use setInputFiles() with a LOCAL file path. Remote URLs are not accepted by file inputs. Download the file locally first, then pass the local path. Do not click button-camera first; set the input directly. steps: 1. ASSERT-AUTH 2. NAVIGATE / 3. SETINPUTFILES [data-agent-id="input-camera"] <> (must be a local file path, not a URL; use setInputFiles() in Playwright) 4. WAIT 30s (allow queue, upload, and analysis; extend on slow networks) 5. NAVIGATE /shoebox 6. VERIFY selector_exists [data-agent-id="input-search"] ## CAPABILITY: review-scanned-receipt description: Review and confirm AI-extracted data for a `review_ready` expense (dialog opens from Shoebox only) note: Requires the expense id (Firestore document id) for the item to review. input: - name: expense-id type: string required: true description: Target expense id (same id used in `button-expense-review-` / `button-expense-delete-` selectors). output: - type: confirmation description: Review dialog closes after confirm; expense updated from extracted data. auth-required: true scope: form-submit ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-expense-review-<>"] 4. WAIT [data-agent-id="button-confirm-review"] (max: 30s) 5. (optional) CLICK [data-agent-id="button-edit-review"] to correct any fields 6. CLICK [data-agent-id="button-confirm-review"] 7. WAIT 2s 8. VERIFY selector_not_exists [data-agent-id="button-confirm-review"] ## CAPABILITY: view-expenses description: View all recorded expenses in the Shoebox input: [] output: - type: confirmation description: Shoebox list visible with search available. auth-required: true scope: read-only ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. VERIFY selector_exists [data-agent-id="input-search"] ## CAPABILITY: filter-expenses description: Filter expenses by date, category, project, receipt type, or additional filters input: - name: filter-type type: string required: true description: "Logical filter: date | category | project | type | more. DOM `data-agent-id`: filter-date, filter-category, filter-project, filter-type, filter-more." output: - type: confirmation description: Filter control activated; list reflects selection after short delay. auth-required: true scope: read-only ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="filter-<>"] 4. WAIT 1s ## CAPABILITY: export-expenses description: Export expenses as a CSV file input: [] output: - type: file description: CSV download triggered (detailed export). auth-required: true scope: file-download ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-export"] 4. WAIT [data-agent-id="menu-export-detailed"] (max: 3s) 5. CLICK [data-agent-id="menu-export-detailed"] ## CAPABILITY: create-project description: Create a new project or trip to group expenses under input: - name: project-name type: string required: true description: Name for the new project or trip. output: - type: confirmation description: Project saved; edit control uses normalized slug (see SELECTOR NORMALIZATION). auth-required: true scope: form-submit ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-manage-projects"] 4. WAIT [data-agent-id="button-new-project"] (max: 5s) 5. CLICK [data-agent-id="button-new-project"] 6. WAIT [data-agent-id="input-project-name"] (max: 3s) 7. INPUT [data-agent-id="input-project-name"] <> 8. CLICK [data-agent-id="button-save-project"] 9. WAIT 2s 10. VERIFY selector_exists [data-agent-id="button-project-edit-<>"] (token: normalized project name; see SELECTOR NORMALIZATION) ## CAPABILITY: google-login description: Sign in to Stack Spent using a Google account input: [] output: - type: confirmation description: User signed in via Google OAuth and redirected to home dashboard at `/`. auth-required: false scope: form-submit ### UI steps: 1. NAVIGATE /login 2. CLICK [data-agent-id="button-google-login"] 3. WAIT [data-agent-id="button-camera"] (max: 15s) 4. VERIFY url == "/" ## CAPABILITY: forgot-password description: Send a password reset email to the user's registered address input: - name: user-email type: string required: true description: The email address the account was registered with. output: - type: confirmation description: Password reset email sent; toast notification confirms dispatch. auth-required: false scope: form-submit ### UI steps: 1. NAVIGATE /login 2. CLICK [data-agent-id="tab-signin"] 3. INPUT [data-agent-id="input-signin-email"] <> 4. CLICK [data-agent-id="button-forgot-password"] 5. WAIT 2s ## CAPABILITY: batch-upload description: Upload multiple receipt images or PDFs at once for AI processing input: - name: file-path type: file required: true description: One or more local file paths to receipt images or single-page PDFs. output: - type: confirmation description: Files processed via batch dialog; outcomes shown per file; expenses may be verified or need Shoebox review. auth-required: true scope: form-submit ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE / 3. CLICK [data-agent-id="button-batch-upload"] 4. WAIT [data-agent-id="input-batch-files"] (max: 5s) 5. UPLOAD [data-agent-id="input-batch-files"] <> 6. CLICK [data-agent-id="button-batch-submit"] 7. WAIT 3s ## CAPABILITY: delete-expense description: Permanently delete a saved expense from the Shoebox input: - name: expense-id type: string required: true description: The ID of the expense to delete. output: - type: confirmation description: Expense removed from the list after confirming the destructive dialog. auth-required: true scope: destructive ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-expense-delete-<>"] 4. WAIT [data-agent-id="button-confirm-delete"] (max: 3s) 5. CLICK [data-agent-id="button-confirm-delete"] 6. WAIT 2s ## CAPABILITY: edit-expense description: Open and edit a previously saved expense input: - name: expense-id type: string required: true description: The ID of the expense to edit. output: - type: confirmation description: Review card opens with existing expense data pre-filled. auth-required: true scope: form-submit ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-expense-review-<>"] 4. WAIT [data-agent-id="button-confirm-review"] (max: 5s) 5. (optional) CLICK [data-agent-id="button-edit-review"] when changing fields 6. (edit fields as needed using ReviewCard selectors) 7. CLICK [data-agent-id="button-confirm-review"] 8. WAIT 2s ## CAPABILITY: search-expenses description: Search expenses by merchant name or keyword input: - name: query type: string required: true description: Search term to filter expenses by. output: - type: confirmation description: Expense list filtered to matching results. auth-required: true scope: read-only ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. INPUT [data-agent-id="input-search"] <> 4. WAIT 1s 5. VERIFY selector_exists [data-agent-id="input-search"] ## CAPABILITY: export-vendor-summary description: Export a vendor-grouped summary of expenses as a CSV file input: [] output: - type: file description: CSV download triggered with expenses grouped by vendor/merchant. auth-required: true scope: file-download ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-export"] 4. WAIT [data-agent-id="menu-export-vendor"] (max: 3s) 5. CLICK [data-agent-id="menu-export-vendor"] ## CAPABILITY: view-insights description: View spending analytics including totals, tax breakdown, top categories, and project summaries input: [] output: - type: confirmation description: Insights page loaded showing spending summary. auth-required: true scope: read-only ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /insights 3. VERIFY selector_exists [data-agent-id="text-insights-title"] ## CAPABILITY: add-custom-category description: Create a new custom expense category for use in future entries input: - name: category-name type: string required: true description: Name for the new custom category. output: - type: confirmation description: Custom category saved and available in category selectors. auth-required: true scope: form-submit ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="filter-category"] 4. CLICK [data-agent-id="button-add-category-dropdown"] 5. WAIT [data-agent-id="input-new-category"] (max: 3s) 6. INPUT [data-agent-id="input-new-category"] <> 7. CLICK [data-agent-id="button-save-category"] 8. WAIT 2s ## CAPABILITY: change-password description: Update the account password from settings input: - name: current-password type: string required: true description: The user's existing password. - name: new-password type: string required: true description: The new password to set. output: - type: confirmation description: Password updated; success toast displayed. auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. INPUT [data-agent-id="input-current-password"] <> 4. INPUT [data-agent-id="input-new-password"] <> 5. INPUT [data-agent-id="input-confirm-password"] <> 6. CLICK [data-agent-id="button-change-password"] 7. WAIT 2s ## CAPABILITY: change-email description: Update the account email address from settings input: - name: new-email type: string required: true description: The new email address for the account. - name: current-password type: string required: true description: Current password to confirm identity (entered in the email form's password field). output: - type: confirmation description: Email updated; success toast displayed. auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. INPUT [data-agent-id="input-new-email"] <> 4. INPUT [data-agent-id="input-email-password"] <> 5. CLICK [data-agent-id="button-change-email"] 6. WAIT 2s ## CAPABILITY: view-marketing-landing description: | Public marketing page at `/` for visitors who are not signed in. Core message: done in the moment, never think about it again. Covers AI scanning, Shoebox workflow, Truck Mode offline capture, global currencies, ADHD/procrastination angle, and the Stack suite (standalone apps that optionally share one login). Routes to `/login` with no credit card required. auth-required: false scope: read-only ### UI steps: 1. (guest) NAVIGATE / 2. VERIFY selector_exists [data-agent-id="text-landing-hero"] ## CAPABILITY: open-login-from-header description: Jump to the sign-in screen from the global header when browsing as a guest auth-required: false scope: read-only ### UI steps: 1. CLICK [data-agent-id="button-login"] 2. VERIFY url == "/login" ## CAPABILITY: view-faq description: | Public FAQ with accordion answers about Stack Spent (AI scanning, offline capture, pricing beta, currencies, privacy themes). Use this page to answer “how does X work?” without signing in. auth-required: false scope: read-only ### UI steps: 1. NAVIGATE /faq 2. VERIFY selector_exists [data-agent-id="text-faq-title"] ## CAPABILITY: contact-support-from-faq description: | Opens the FAQ “Contact Support” dialog to email the team a free-form question (uses the signed-in email when available, otherwise collects an address). auth-required: false scope: form-submit ### UI steps: 1. NAVIGATE /faq 2. CLICK [data-agent-id="button-contact-support"] 3. WAIT [data-agent-id="input-support-question"] (max: 3s) 4. (if email editable) INPUT [data-agent-id="input-support-email"] <> 5. INPUT [data-agent-id="input-support-question"] <> 6. CLICK [data-agent-id="button-send-question"] 7. WAIT 2s ## CAPABILITY: view-how-ai-works description: | Public explainer describing dual-AI receipt verification, what data is extracted, and how privacy is handled—good for user education and trust conversations. auth-required: false scope: read-only ### UI steps: 1. NAVIGATE /how-ai-works 2. VERIFY selector_exists [data-agent-id="text-how-ai-title"] ## CAPABILITY: view-adhd-landing description: | SEO landing page tailored to ADHD freelancers—covers “ADHD tax,” Shoebox workflow, Truck Mode, and an embedded FAQ, with CTA to create an account. auth-required: false scope: read-only ### UI steps: 1. NAVIGATE /expense-tracker-for-adhd 2. VERIFY selector_exists [data-agent-id="text-adhd-landing-title"] ## CAPABILITY: view-privacy-policy description: Privacy policy HTML served from Firestore static page storage auth-required: false scope: read-only ### UI steps: 1. NAVIGATE /privacy 2. VERIFY selector_exists [data-agent-id="static-page-content"] ## CAPABILITY: view-terms-of-service description: Terms of service HTML served from Firestore static page storage auth-required: false scope: read-only ### UI steps: 1. NAVIGATE /terms 2. VERIFY selector_exists [data-agent-id="static-page-content"] ## CAPABILITY: view-cookie-policy description: Cookie policy HTML served from Firestore static page storage auth-required: false scope: read-only ### UI steps: 1. NAVIGATE /cookies 2. VERIFY selector_exists [data-agent-id="static-page-content"] ## CAPABILITY: send-in-app-feedback description: | Lightweight feedback composer in the header (bug reports, ideas). Writes to the `feedback` collection with page path context; optional auth enriches with user id/email. auth-required: false scope: form-submit ### UI steps: 1. CLICK [data-agent-id="button-feedback"] 2. WAIT [data-agent-id="input-feedback"] (max: 3s) 3. INPUT [data-agent-id="input-feedback"] <> 4. CLICK [data-agent-id="button-send-feedback"] 5. WAIT 2s ## CAPABILITY: logout description: Sign out of Firebase and return to the public landing experience auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. CLICK [data-agent-id="button-logout"] 3. WAIT [data-agent-id="button-login"] (max: 5s) 4. VERIFY selector_exists [data-agent-id="text-landing-hero"] ## CAPABILITY: add-manual-expense-from-home description: | Same Manual Entry form as Shoebox, opened from the Home dashboard dialog—useful when the user is already on `/` after capture-oriented workflows. auth-required: true scope: form-submit ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE / 3. CLICK [data-agent-id="button-manual-entry"] 4. WAIT [data-agent-id="input-merchant"] (max: 5s) 5. Repeat steps 5–21 from CAPABILITY add-manual-expense (through optional project steps; same selectors inside the dialog) 6. CLICK [data-agent-id="button-submit-manual-entry"] 7. WAIT 2s 8. VERIFY selector_exists [data-agent-id="text-welcome"] ## CAPABILITY: update-display-preferences description: | Adjust global UI theme (light/dark/system), interface language, and receipt date display format—all persisted per account. auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. (optional) interact [data-agent-id="select-theme"] to choose theme 4. (optional) interact [data-agent-id="select-language"] to choose language 5. (optional) interact [data-agent-id="select-date-format"] to choose format ## CAPABILITY: configure-insurance-settings description: | Optional medical/insurance tracking helpers (toggle + coverage fields) stored under the user’s settings subtree—explain to users who need HSA/insurance documentation alongside medical categories. auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. CLICK [data-agent-id="switch-insurance"] (enable if needed) 4. WAIT [data-agent-id="input-insurance-coverage"] (max: 3s) 5. (optional) INPUT [data-agent-id="input-insurance-coverage"] <> 6. (optional) INPUT [data-agent-id="input-insurance-cap"] <> 7. CLICK [data-agent-id="button-save-insurance"] 8. WAIT 2s ## CAPABILITY: unlink-google-provider description: | Removes Google as a sign-in provider after prerequisites (password on account) so the user can switch to email-only flows—surface warnings from Settings when Google is still required. auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. CLICK [data-agent-id="button-unlink-google"] 4. WAIT [data-agent-id="button-confirm-unlink"] (max: 3s) 5. CLICK [data-agent-id="button-confirm-unlink"] 6. WAIT 2s ## CAPABILITY: add-password-google-only-user description: | When a user signed up with Google and has no password yet, Settings shows “Add Password” using the same fields as change-password but without a current-password requirement—enables email login and eventual Google unlink. auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. INPUT [data-agent-id="input-new-password"] <> 4. INPUT [data-agent-id="input-confirm-password"] <> 5. CLICK [data-agent-id="button-change-password"] 6. WAIT 2s ## CAPABILITY: delete-user-account description: | Irreversibly deletes the Firebase user and associated expense data after typing DELETE in the confirmation dialog—only for explicit user requests. auth-required: true scope: destructive ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. CLICK [data-agent-id="button-delete-account"] 4. WAIT [data-agent-id="input-delete-confirm"] (max: 3s) 5. INPUT [data-agent-id="input-delete-confirm"] DELETE 6. CLICK [data-agent-id="button-confirm-delete"] 7. WAIT 3s ## CAPABILITY: manage-offline-upload-queue description: | When captures are pending or failed, Shoebox shows a sync banner with controls to clear the entire queue, drop failed items only, or retry failed uploads—explain this as “Truck Mode / offline catch-up.” auth-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. WAIT [data-agent-id="text-sync-status"] (max: 5s) 4. (optional) CLICK [data-agent-id="button-retry-failed"] 5. (optional) CLICK [data-agent-id="button-clear-failed"] 6. (optional) CLICK [data-agent-id="button-clear-all"] ## CAPABILITY: export-expenses-mobile-toolbar description: | On narrow viewports the Shoebox export split-button hides; use the icon-only control to reach the same CSV menus as desktop `button-export`. auth-required: true scope: file-download ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /shoebox 3. CLICK [data-agent-id="button-export-mobile"] 4. WAIT [data-agent-id="menu-export-detailed"] (max: 3s) 5. CLICK [data-agent-id="menu-export-detailed"] ## CAPABILITY: open-admin-feedback-console description: | Internal console listing user feedback across StackApps apps with status + delete moderation tools. Only emails passing `isAdminEmail` stay on the page; others redirect to `/`. auth-required: true admin-required: true scope: read-only ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /admin/feedback 3. VERIFY selector_exists [data-agent-id="text-admin-feedback-title"] ## CAPABILITY: open-admin-tax-catalog description: | Internal editor for supplemental custom tax rows merged with built-in tax metadata. Restricted to admin emails; others redirect to `/`. auth-required: true admin-required: true scope: read-only ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /admin/taxes 3. VERIFY selector_exists [data-agent-id="text-admin-tax-catalog-title"] ## CAPABILITY: populate-static-pages-admin description: | One-click maintenance action (Settings → Admin) that seeds/regenerates Firestore static HTML documents for legal pages—intended for operators, not end users. auth-required: true admin-required: true scope: account-modify ### UI steps: 1. ASSERT-AUTH 2. NAVIGATE /settings 3. CLICK [data-agent-id="button-admin-static-pages"] 4. WAIT 3s ## CAPABILITY: navigate-site-footer description: | Explains that every page exposes footer shortcuts to FAQ, AI explainer, and legal policies—helpful when orienting new users. auth-required: false scope: read-only ### UI steps: 1. CLICK [data-agent-id="link-faq"] OR CLICK [data-agent-id="link-ai"] OR CLICK [data-agent-id="link-privacy"] OR CLICK [data-agent-id="link-terms"] OR CLICK [data-agent-id="link-cookies"] 2. WAIT 1s