Skip to main content

Project Tracker

Single-page status of the SalesArck build. Updated when implementation lands or shifts; the source of truth is the salesarck monorepo.

How to read this page

Status icons: ✅ shipped · 🟡 partial / behind a feature flag · 🔴 not started · 🧪 experimental · 🔧 known gap

Each status item links to the implementation file (or the doc that explains it) so you can verify it without leaving the page.


Snapshot

LayerStatusNotes
API runtime (Hono)✅ Shipped8 route groups mounted at /api/v1/*
Database schema (Drizzle)✅ Shipped13 tables + indices; managed via drizzle-kit push
Auth (Supabase OTP + PKCE)✅ ShippedEmail + phone OTP, role-aware redirect
RBAC✅ Shipped17 permissions, 4 roles (consumer/client/pos_operator/admin)
Tenant isolation✅ ShippedtenantMiddleware + scoped queries
Square integration✅ ShippedOAuth + webhook + cron poll fallback
Clover integration✅ ShippedOAuth + webhook + REST payment fetch (no poll fallback)
Reward engine✅ ShippedPure, deterministic, fully tested
Wallet ledger✅ ShippedAppend-only, atomic credit/debit
Redemption codes (consumer + POS operator)✅ Shipped6-byte hex, 15-min TTL, single-use
Admin tooling (approve users, freeze/adjust wallets, audit log)✅ ShippedAll actions write admin_audit_logs
Email notifications (Resend)✅ ShippedPoints-earned + redemption-code emails
Webhook retry sweeper✅ Shipped/internal/cron/process-pending-webhooks
Workers app🟡 Scaffoldedapps/workers/src/index.ts is currently a logging stub; cron jobs run via Render scheduler hitting cron routes
Idempotency-key TTL cleanup🔧 Gapexpires_at stored but lookup ignores it; no cleanup job (details)
Clover poll fallback🔧 GapRecovery relies on webhook retry sweeper
Reconciliation jobs🔴 Not startedNo automated ledger reconciliation
Campaign multipliers🟡 StubbedHardcoded 1 in ingestion path

Backend Surface (apps/api)

Routes

GroupMountFileStatus
Auth/api/v1/authroutes/auth.ts
Consumer/api/v1/consumerroutes/consumer.ts
Client (merchant)/api/v1/clientroutes/client.ts
Admin/api/v1/adminroutes/admin.ts
POS operator/api/v1/posroutes/pos.ts
Webhooks/api/v1/webhooksroutes/webhooks.ts✅ Square + Clover
OAuth callbacks/api/v1/oauthroutes/pos-oauth.ts✅ Square + Clover
Internal cron/api/v1/internal/cronroutes/cron.ts✅ Square poll + webhook sweeper

Full route table → API Reference.

Services

ServiceFileStatus
Ingestion (provider-agnostic core)services/ingestion.ts
Wallet credit / ledgerservices/wallet-service.ts
Square adapterservices/square-adapter.ts
Square API clientservices/square-api.ts
Square poll workerservices/square-poll.ts
Clover adapterservices/clover-adapter.ts
Clover API clientservices/clover-api.ts

Middleware

correlationdbauth (or auth-profile) → tenantrbac.requirePermission(...). Order is enforced in app.ts. All middleware shipped.


Frontend Surface (apps/web)

Pages

RouteComponentRolesStatus
/loginLoginPagepublic
/auth/callbackAuthCallbackPagepublic
/profile-errorProfileErrorPageall (post-auth)
/pending-approvalPendingApprovalPageall
/account-blockedAccountBlockedPageall
/onboardingOnboardingPage + MerchantProfileWizardclient
/dashboardDashboardPage (Client/Admin views)client, admin
/rewardsRewardConfigPageclient, admin
/posPosSettingsPageclient, admin
/store-operatorsStoreOperatorsPageclient
/transactionsTransactionsPageclient, admin
/walletWalletPageconsumer
/historyHistoryPageconsumer
/redeemRedeemPage (+ PosOperatorRedeemPanel)consumer, pos_operator
/profileProfilePage (role-dispatched)all
/admin/tenantsTenantsPageadmin
/admin/usersUsersPageadmin
/admin/audit-logsAuditLogsPageadmin

Routing details → Frontend Architecture.

Frontend infrastructure

ConcernStatus
React 18 + Vite 5 + React Router 6
Supabase JS (PKCE flow, session in sessionStorage)
Zustand auth store
apiFetch<T>() with bearer auth + retry/backoff
TanStack Query installed🟡 Configured but not heavily used
Tailwind CSS✅ (existing code); guideline: no Tailwind in new code (use ui.tsx primitives)
ui.tsx primitives (Icon, Avatar, Brandmark, StatusPill, KV, MerchantMark)

Data Model

13 tables in packages/db/src/schema/index.ts. All shipped:

TableAppend-only?Purpose
usersNoProfile mirror of Supabase auth
tenantsNoMerchant org
tenant_usersNoUser ↔ tenant membership + role
pos_connectionsNo (status flips)Encrypted Square/Clover OAuth tokens
pos_webhook_events_rawYesRaw payloads for fast-ack + retry
transactionsYesCanonical normalized POS transaction
walletsNo (balance snapshot)One per (tenant, user)
wallet_ledgerYes — never UPDATE/DELETESource of truth for points
redemption_codesNo (status flips pending → used)Single-use, 15-min TTL
tenant_operator_invitesNo (status flips)Pre-authorized store-operator emails
reward_rulesYes (versioned)Tenant reward configuration
reward_eventsYesAudit link tx → wallet earn/redeem
idempotency_keysYesMutation deduplication (cleanup job pending)
admin_audit_logsYes — immutablePrivileged action trail

Full schema → Data Model and Migrations.


Workers / Background Jobs

JobHow it runs todayStatus
Square ListPayments pollRender cron → POST /internal/cron/square-poll-payments
Webhook retry sweeperRender cron → POST /internal/cron/process-pending-webhooks
Token refreshInline during poll (180s buffer ahead of expiry)
Email send (Resend)Fire-and-forget from request handler
Idempotency-key TTL cleanup🔧 Gap (no cleanup)
Clover poll fallback🔧 Gap (sweeper covers retry)
Ledger reconciliation🔴 Not started
Queue-backed worker (Cloudflare Queues / BullMQ)🔴 apps/workers is a logging stub

Packages

PackageStatusNotes
@salesarc/typesFoundation; Zod schemas + TS types
@salesarc/dbDrizzle schema + client
@salesarc/authSupabase server-side auth helpers
@salesarc/rbacPermission enum + hasPermission
@salesarc/tenantMembership resolver + admin bypass
@salesarc/reward-enginePure points calculation
@salesarc/observabilityPino + Sentry + correlation IDs

Deployment

SurfaceTargetStatus
FrontendVercel (salesarc-web.vercel.app)
APIRender (salesarc-api.onrender.com)
DatabaseSupabase Postgres
AuthSupabase Auth (OTP, PKCE)
File storage (logos)Supabase Storage
CI/CDGitHub Actions
Documentation siteCloudflare Pages

Known Gaps and Hardening Backlog

These are tracked from code-level TODOs and docs/code/* "Known gaps" sections.

  1. Idempotency-key cleanupexpires_at is stored but ignored at lookup time; no cleanup job. Acts as permanent dedup until addressed. (ingestion docs)
  2. Idempotency replay snapshotsidempotency_keys.result_snapshot is not yet returned to callers.
  3. Clover polling fallback — recovery for failed Clover webhooks goes through the retry sweeper rather than a ListPayments-equivalent.
  4. Campaign multipliers — ingestion uses a hardcoded 1; tenant campaign config is not yet a first-class concept.
  5. Reconciliation jobs — no automated SUM(ledger) ?= wallet.balance audit.
  6. Workers app is a stub — queue consumer, ingestion-worker, and dedicated token-refresh-worker are TODOs in apps/workers/src/index.ts.
  7. Redemption code verification — server-side flow is solid; richer POS-operator UX (history, filtering, refund) is intentionally minimal in the current phase.
  8. Tenant admin email — admin role is gated to @doshidhruv.com; multi-org admin is not yet supported.

Roadmap Pointers


How to update this page

When a piece of work lands:

  1. Flip the relevant ✅ / 🟡 / 🔴 status above
  2. Move any closed gap from Known Gaps to the Snapshot table (or remove it)
  3. Add a one-line link to the implementation file or doc that proves it
  4. Commit alongside the implementing PR (docs(tracker): ...)

Keep entries short — this is an index, not a changelog. Detailed history lives in git and docs/code/git-evolution.md.

Written byDhruv Doshi