Commit Graph
3588 Commits
Author SHA1 Message Date
Bilal Godil de526eaa31 Backfill endedAt on terminal subscription rows
Terminal rows written before endedAt was derived on every sync (or first-
synced already-terminal) have endedAt = NULL, which the status-agnostic
isSubscriptionInEffect predicate reads as entitled forever. Close them out
with LEAST(currentPeriodEnd, NOW()), mirroring getEndedAtForSync's fallback
when Stripe omits ended_at.

Measured population in production: 7 rows (2026-07-17), so no batching or
temp-index machinery is needed. After deploy, re-run
scripts/bulldozer-payments-init.ts to re-emit the affected rows so their
grants expire in the timefold.
2026-07-17 09:57:59 -07:00
Bilal Godil eb4c07be32 Reject repurchase of winding-down Stripe subs instead of webhook reactivation
The invoice.paid reactivation path (clear cancel_at_period_end when a paid
invoice postdates canceledAt) was fragile: the created-after-cancel heuristic
can match unrelated collectible invoices, and the single-subscription guard
counted raw invoice lines, so re-price invoices with proration lines would
silently skip reactivation. Purchase sessions now return a 400 when the
conflicting Stripe sub is canceled or winding down; reactivation-on-repurchase
is deferred (see PR #1773 discussion).
2026-07-17 09:42:33 -07:00
Bilal Godil 7e1bad8c0d Shorten comments in subscription wind-down handling 2026-07-16 18:15:46 -07:00
Bilal Godil 45ddd3877d Reconcile Stripe wind-down state in webhook sync
Follow-ups from further review of the wind-down handling:

- getEndedAtForSync now reconciles endedAt on NON-terminal syncs too:
  cancel_at_period_end true schedules Stripe's period end, false clears
  it. This repairs the eagerly-written endedAt when a pending cancel is
  reversed through Stripe's Dashboard/API (previously sticky — Stripe
  kept billing while bulldozer still ended the grants) and when the
  eager write used a stale pre-renewal boundary. getCanceledAtForSync
  now mirrors Stripe's canceled_at including null for the same reason.
- Apply the endedAt/canceledAt derivation to the webhook upsert's
  create branch. The first sync for a sub can already be terminal
  (out-of-order webhooks, or purchase-session subs whose first local
  write is a webhook); such rows were created with endedAt null, which
  isSubscriptionInEffect reads as entitled forever. A backfill for
  pre-existing rows in that shape is noted as a follow-up (it needs the
  bulldozer rows re-emitted, so it isn't a plain SQL migration).
- Source the cancel and refund routes' eager endedAt from the Stripe
  update response (new getStripeSubscriptionPeriodEnd helper) instead
  of the possibly stale local/bulldozer period end.
- Clear cancel_at_period_end on invoice.paid when the paid invoice was
  issued after canceledAt — that can only be the purchase-session
  re-price of a winding-down sub, so paying it means "keep me
  subscribed". Deliberately NOT cleared at session creation: that runs
  before payment (default_incomplete has no rollback), and a declined
  card would silently reactivate an explicitly-canceled sub while
  leaving it re-priced. The created-after-cancel guard also makes a
  late-delivered invoice.paid for the original creation invoice unable
  to undo a cancel.
- Rank representative subs cancelable > active > in-effect
  (subscriptionDisplayRank) in the products list: a pending-cancel
  Stripe sub stays active, so the previous active-only preference could
  let it shadow a cancelable sibling of a stackable product and report
  is_cancelable false while DELETE would succeed.
- Thread the caller's clock through resolveInEffectPlanSubscription
  instead of calling Date.now() inside, keeping plan selection and
  usage queries on the same instant.
- Unit tests for the sync helpers, the display rank, and deterministic
  clocks in the plan-usage tests.
2026-07-16 17:46:56 -07:00
Bilal Godil 7f4a15078d Address review findings on subscription wind-down handling
- Clear cancel_at_period_end when switching plans on a Stripe sub —
  paying to switch is an explicit "keep me subscribed", and Stripe
  otherwise persists the flag so the newly paid plan would still die
  at the period boundary. Also reset the locally written canceledAt /
  endedAt wind-down marks so the reactivated sub doesn't end early.
- Prefer active subs over winding-down ones when several in-effect
  subs share a productId in the product list (partial cancel of a
  stackable product could otherwise hide the cancel button).
- Extract isSubscriptionCancelable and use it in both the product
  list's is_cancelable field and the cancel route's filter, so
  is_cancelable: false always implies the DELETE returns 400 — for
  Stripe subs too, which previously re-ran the cancel silently. The
  cancel route now also updates the local row eagerly (mirroring the
  refund route) instead of waiting for the webhook sync.
- Give the by-subscription-id cancel branch the same already-canceled
  error, and stop claiming past_due/incomplete subs are "already
  canceled" — they get their own message.
- Treat canceled-at-period-end subs as replaceable conflicts in
  validatePurchaseSession, so buying another plan in the same product
  line during a wind-down is no longer rejected as "already has a
  one-time purchase". Scoped narrower than isSubscriptionInEffect so
  payment retries of incomplete subs aren't rejected as duplicates.
- Make isSubscriptionInEffect's parameter a union so callers must
  actually have one of the endedAt fields, and rename
  resolveActivePlanSubscription to resolveInEffectPlanSubscription to
  match its new semantics.
- Tests: unit tests for both predicates and the plan-usage wind-down
  resolution, a validatePurchaseSession wind-down conflict test, and
  e2e coverage for stackable partial-cancel shadowing, by-id double
  cancel, and same-line replacement during wind-down.
2026-07-16 14:03:08 -07:00
Bilal Godil 8c6a7e77b3 Shorten comments in payments subscription predicates 2026-07-16 13:24:29 -07:00
Bilal Godil 59ef500f7e Treat canceled-at-period-end subscriptions as still in effect
Canceling a subscription without a Stripe backing (test mode, free plans)
writes status=canceled with endedAt set to the end of the current period,
so the customer keeps their entitlements until then. But every read path
classified subscriptions with isActiveSubscription (active/trialing only),
so during the paid-through window the owned product fell through to the
one-time-purchase branch: the account settings payments tab showed
"One-time purchase" with no end date and no way to tell the plan was
winding down, and plan usage reported the free plan while the paid plan's
quotas were still in effect.

Introduce isSubscriptionInEffect (status-agnostic, endedAt-based — the
same semantics Bulldozer's grant timefold and ensure-free-plan already
use) and use it at the read sites: product list classification, plan
usage resolution, and the switch route's sub-vs-OTP occupancy check.
Write sites keep isActiveSubscription so wound-down subs can't be
re-canceled or switched from. The product list now also computes
is_cancelable instead of hardcoding it, double-canceling returns a
dedicated error instead of claiming the product is an OTP, and the
Stripe cancel path uses cancel_at_period_end instead of canceling
immediately, matching the promise made by the confirmation dialog.
The account settings panels render "Ends on <date>" for winding-down
subscriptions.
2026-07-16 13:19:07 -07:00
Aman GanapathyGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>aman
32daff06f5 [Fix]: OOM Risk with Data Table (#1766)
### Context
We were seeing the txn table and the customers tab under product page
OOM.

It turns out this was because the team icon when loaded would fetch all
of the teams.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes OOM and renderer crashes in the transactions and product customers
tables by stopping per-row refetch storms and avoiding list‑all‑teams
calls. Infinite scroll is now robust, and shared avatar skeletons keep
table rendering smooth.

- **Bug Fixes**
- Data grid (`@hexclave/dashboard-ui-components` `use-data-source`):
queue `loadMore` while a fetch is in flight and replay only after a
successful settle; discard queued requests when pagination mode leaves
infinite; commit the cursor only after a result is delivered; skip
redundant refetches when inputs match a completed fetch; abort in‑flight
requests on unmount.
- Transactions table and product customers tab: wrap `User*`/`Team*`
avatar cells in `Suspense` with a shared `AvatarCellSkeleton` to prevent
per‑row fetch storms and suspend thrash.
- `serverApp.getTeam`/`useTeam` (in `@hexclave/shared` consumers): fetch
a single team by id via a cache; return null for invalid ids; keep
user‑scoped variants membership‑scoped; stabilize hook order. This
removes the “fetch all teams per row” behavior that triggered OOMs.
- Prefetching: cap `/projects/*/teams` to `useTeams({ limit: 1 })` and
remove the heavy owner‑team users prefetch.

- **Refactors**
- Add unit tests for infinite pagination in `use-data-source` (deferred
`loadMore`, aborted resets, error handling, and cursor continuity).

<sup>Written for commit 970abc0f03.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1766?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Added loading placeholders for customer avatar/name rendering across
payment and transaction customer tables to prevent jarring updates while
data loads.
* Improved infinite data loading to correctly queue and replay “load
more” after in-flight requests, handle abort/reset races, and avoid
stale cursor behavior; deferred requests are discarded when leaving
infinite mode.
* Improved user-scoped team lookups by validating team identifiers and
reliably returning `null` for missing/invalid teams.
* **Documentation**
* Clarified that user-level team lookups only return teams the user is a
member of.
* **Tests**
* Added/expanded coverage for infinite pagination cursor correctness,
race conditions, error handling, and mode transitions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: aman <[email protected]>
2026-07-14 11:06:41 -07:00
Konsti WohlwendandGitHub ce956a0fd2 Add 6/26/26–7/10/26 changelog entries (#1753)
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / wait-for-fast-fail (push) Has been cancelled
Runs E2E API Tests with custom port prefix / wait-for-fast-fail (push) Has been cancelled
Runs E2E Fallback Tests / wait-for-fast-fail (push) Has been cancelled
Fast-fail tests / select-tests (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Fast-fail tests / fast-fail-tests (push) Has been cancelled
2026-07-13 18:32:53 -07:00
Konsti WohlwendandGitHub 21a04ac7fd Coalesce Overview globe hover hit-testing to one per frame (#1758) 2026-07-13 18:29:14 -07:00
Konstantin Wohlwend 160d41b8f7 Fix all good script to use pagination 2026-07-13 15:39:10 -07:00
Konstantin Wohlwend 72c708a833 Update reminders 2026-07-13 15:34:36 -07:00
Konsti WohlwendandGitHub 42b088f09c Add AI-selected fast-fail test workflow gating the full test suites (#1756) 2026-07-13 14:17:32 -07:00
Konstantin Wohlwend 770f01a057 Various improvements 2026-07-13 13:40:56 -07:00
Konstantin Wohlwend a66a8d972d Revert "Migrate backend transport from Next.js to ElysiaJS (WIP) (#1630)"
This reverts commit 5209ec83ae.
2026-07-13 12:38:53 -07:00
Konstantin Wohlwend 7525526730 Revert "refactor(backend): remove Next.js compat shims, use standard Web APIs (#1652)"
This reverts commit ae09be9c66.
2026-07-13 12:36:36 -07:00
Konstantin Wohlwend 0dacb04a19 Revert "Enhance request logging in development mode"
This reverts commit 67e3350693.
2026-07-13 12:35:14 -07:00
Konstantin Wohlwend f6bf39cf36 Support trailing slashes 2026-07-13 12:24:02 -07:00
Konsti WohlwendandGitHub f33cb4cbc5 Fix flaky payment e2e tests: await async Stripe webhook processing + deterministic fixtures (#1746)
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
2026-07-10 20:22:47 -07:00
MantraGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>mantra
4144ad039b docs(agents): add comment-style guidance and symlink CLAUDE.md to AGENTS.md (#1725)
## Summary
- Add an `AGENTS.md` guideline: write comments as if the reader is new
to the codebase but already familiar with the project's goal — explain
the local "why" and non-obvious decisions, not what the project is
trying to achieve.
- Replace `CLAUDE.md` (previously a 5-line stub) with a symlink to
`AGENTS.md` so both entrypoints share a single source of truth.

Link to Devin session:
https://app.devin.ai/sessions/899d319b4b5648d1bb930c2ada976e3f
Requested by: @mantrakp04

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Add comment-writing guidance in AGENTS.md to explain local “why” and
non-obvious decisions for newcomers. Replace the previous `CLAUDE.md`
stub with a symlink to `AGENTS.md` to keep a single source of truth.

<sup>Written for commit 9bd9570c66.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1725?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Updated guidance on writing comments to focus on local context and
non-obvious decisions for readers new to the codebase.
* Removed a list of documented command examples from the project notes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mantra <[email protected]>
2026-07-10 19:17:50 -07:00
mantrakp04 67e3350693 Enhance request logging in development mode
Added functionality to log request timings in development mode. Introduced a WeakMap to track request start times and log the elapsed time along with the request method, pathname, and response status after each request. This improves observability during development.
2026-07-10 19:02:05 -07:00
Konstantin Wohlwend 06e3a6afa9 Add --clear-screen to backend dev 2026-07-10 18:26:14 -07:00
Konstantin Wohlwend 717c7c2234 Bump up migration file estimator to 10mil 2026-07-10 18:17:53 -07:00
Konstantin Wohlwend 33237592b7 Make dev script exit when deps not running 2026-07-10 18:11:22 -07:00
Konsti WohlwendandGitHub 90832a41bc Silence spurious import.meta CJS warning during pnpm run dev (#1752) 2026-07-10 17:59:22 -07:00
Konstantin Wohlwend df0a2b725d Update dev to share infra with dev:tui 2026-07-10 11:29:27 -07:00
Konstantin Wohlwend ac0bda7522 dev:tui now builds packages again 2026-07-10 11:27:57 -07:00
Konstantin Wohlwend 5acfe0b465 Fix various dev server issues 2026-07-10 11:25:19 -07:00
Konstantin Wohlwend cb6c9024fe Make Bulldozer Studio log less 2026-07-10 11:02:18 -07:00
Konstantin Wohlwend 00b582438e Remove afterFileLint hook from Cursor 2026-07-10 10:46:02 -07:00
Konstantin Wohlwend 31dc84075a Hosted component hacks 2026-07-10 10:45:35 -07:00
Konstantin Wohlwend 09e5a97d77 Less Bulldozer spam 2026-07-09 21:50:13 -07:00
MantraGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>mantraCursorBilal Godil
ae09be9c66 refactor(backend): remove Next.js compat shims, use standard Web APIs (#1652)
## What

Removes the Next.js compatibility shim layer that the ElysiaJS backend
migration (#1630) introduced, replacing it with standard Web APIs and
de-aliased local runtime helpers. Addresses N2D4's review note on #1630:

> i think we should create a followup PR which cleans these up and uses
the elysia methods directly — so we don't need a nextjs compat layer
forever

Stacked on `migrate-backend-to-elysiajs`.

## Changes

**`next/server` → standard Web APIs (eliminated)**
- `NextRequest` → `Request`, `NextResponse.json()` → `Response.json()`,
`new NextResponse(...)` → `new Response(...)`, `req.nextUrl` → `new
URL(req.url)`
- Hot path (`smart-route-handler`) computes `const requestUrl = new
URL(req.url)` once
- Deleted `lib/next-compat/server.tsx`; rewrote
`server/next-request-shim.ts` → `server/backend-request.ts`
(`createBackendRequest`, returns a plain `Request`)

**`next/headers` + `next/navigation` → `lib/runtime/` (de-aliased)**
- `git mv`'d the real runtime helpers (`headers`, `navigation`,
`request-context`) out of `lib/next-compat/` into `lib/runtime/`,
repointing all consumers to `@/lib/runtime/*`
- The cookie/header/redirect mechanism (AsyncLocalStorage + thrown
redirect errors, driven by `app.ts`) is unchanged — it was only *named*
after Next, never actually Next

**Config + cleanup**
- Dropped the `next/*` path aliases from `tsconfig.json`,
`vitest.config.ts`, `tsdown.config.ts` (removed `nextCompatPlugin`, the
alias map, and the `next` bundling special-case)
- Deleted `fetch.d.ts` and removed the no-op `next: { revalidate }`
fetch option in `changelog/route.tsx` (caching never worked outside the
Next runtime)
- Converted the unreferenced `proxy.tsx` so it still compiles (it's dead
code superseded by `server/middleware.ts` — deletion candidate, left out
of this PR)

No `next/*` imports remain in the backend.

## Verification
- `tsc --noEmit` — passes (exit 0)
- `eslint` on all changed files — clean

## Notes / not in scope
- This removes the **`next/*` façade**. It keeps the underlying
ALS-based request-context mechanism rather than threading Elysia's
native context (`set.cookie`, `set.redirect`) into every handler —
that's a much larger change touching handler signatures across the whole
API surface.
- One internal redirect digest string is still `"NEXT_REDIRECT"`
(matched in both `navigation.tsx` and `app.ts`); renamable but cosmetic.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Remove the `next/*` compatibility layer and switch the backend to
standard Web APIs. This simplifies handlers and config while keeping the
ALS request context and redirect behavior unchanged.

- **Refactors**
- Replaced `NextRequest`/`NextResponse` and `req.nextUrl` with
`Request`/`Response` and `new URL(req.url)` across handlers, proxies,
health/unsubscribe routes, and IDP endpoints.
- Moved runtime helpers to `@/lib/runtime/*` (`headers`, `navigation`,
`request-context`) and updated imports.
- Added `server/backend-request.ts` to build backend `Request`s with
merged headers; removed `server/next-request-shim.ts` and
`lib/next-compat/server.tsx`.
- Updated proxy rewrite to set `x-middleware-rewrite`; IDP routes now
return `Response` directly with 307/308 mapping.
- Removed the `next:{revalidate}` fetch option; deleted
`lib/next-compat/fetch.d.ts`.
- Dropped `next/*` aliases from `tsconfig`, `tsdown`, and `vitest`;
route registry/types now use `Request`.

- **Migration**
- Import from `@/lib/runtime/headers` and `@/lib/runtime/navigation`
instead of `next/headers` and `next/navigation`.
- Route handlers should accept `Request` and use `new URL(req.url)` for
URL parsing.

<sup>Written for commit b4a534ff36.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1652?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mantra <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Bilal Godil <[email protected]>
2026-07-09 21:45:15 -07:00
MantraGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>mantraCursorBilal Godil
5209ec83ae Migrate backend transport from Next.js to ElysiaJS (WIP) (#1630)
> **Draft / WIP — do not merge.** Backend Next.js → ElysiaJS transport
migration, in progress.

## Summary

Migrates the backend (`apps/backend`) HTTP transport from **Next.js (App
Router)** to **ElysiaJS** (Node adapter, `@elysiajs/node`), with the
explicit goal of keeping every API route **byte-for-byte backwards
compatible** — same URLs, methods, status codes, headers, and bodies.
Elysia becomes purely the transport layer; the existing
`createSmartRouteHandler` abstraction and all ~221 file-based route
handlers are reused unchanged.

## Approach

- **One wildcard dispatcher** reuses the existing `smart-router`
matchers + generated `routes.json`/`api-versions.json` rather than
re-registering 221 routes on Elysia's router.
- **`proxy.tsx` (Next 16 middleware) ported** to an Elysia request
pipeline: CORS, dev rate-limit, `x-hexclave-*`→`x-stack-*` header
aliasing, OPTIONS preflight, and the **API version rewrite** (`/api/v1`,
`/api/v2betaN` → `/api/latest` / `/api/migrations/*`).
- **Two-URL dispatch**: the version-rewritten path locates the handler;
the original client URL is preserved on `req.url`/`nextUrl`
(load-bearing for OIDC/neon routes that assert `/api/v1/...`).
- **`next/*` compatibility shims** (`next/headers`, `next/navigation`,
`next/server`) under `src/lib/next-compat/`, wired via aliases in
tsconfig / vitest / tsdown so route files stay untouched.
- **Observability** moved off `@sentry/nextjs` → `@sentry/node` + manual
OpenTelemetry NodeSDK preload (`src/instrument.ts`); explicit
`/monitoring` Sentry tunnel.
- **Build/deploy**: `next build/start` → tsdown bundle
(`tsdown.config.ts`) for container; Vercel default-export entry;
Dockerfile CMD → `dist/server.mjs`; `next.config.mjs` removed (security
headers etc. reimplemented).

## Status — WIP

**Green so far:** backend `typecheck`, backend `lint`, tsdown bundle,
`GET /health`, `GET /api/v1`, e2e `migration-tests` (13/13),
`analytics-query` (69/69), unit suite (~1006 tests).

**Remaining before ready for review:**
- [ ] Full backend e2e snapshot suite green (snapshots = byte-for-byte
oracle; never `-u`'d to mask drift)
- [ ] `config.test.ts` (`custom_oidc`) snapshot root-cause
- [ ] M4: source-map upload + remove remaining `@sentry/nextjs` browser
leftovers
- [ ] M5: Docker + Vercel artifact verification
- [ ] M6: confirm React-UI → slim-handler cleanup

## Notes
- Backwards-compat gate: the existing e2e snapshot files (recorded
against the Next.js backend) must pass unchanged.
- Built primarily by Codex (`gpt-5.5`); branch intentionally separate
from `dev`.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Migrates the backend transport from Next.js to ElysiaJS with
byte‑for‑byte compatible APIs, bundled with `tsdown` for Node, Vercel,
and Docker. Reverts out‑of‑scope payments/metrics/custom OIDC changes to
keep this PR focused.

- **Refactors**
- Transport: Next.js → ElysiaJS (`elysia`, `@elysiajs/node`) via
wildcard dispatcher + generated route registry; preserve original URL;
decode params; 400 on malformed params.
- Next shims: `next/headers`, `next/navigation`, `next/server` with
request context and cookie serialization; don’t percent‑decode;
`cookies().delete()` clears pending Set‑Cookie; add `fetch` `next`
options typing.
- Observability: move to `@sentry/node`/`@sentry/browser`; preload
`@opentelemetry/sdk-node`; guard duplicate OTel registration; add
`/monitoring` tunnel; sanitize Sentry release names; replace Next.js
instrumentation with `src/instrument.ts`.
- Build/deploy: bundle `dist/server.mjs` and `dist/vercel.mjs` with
`tsdown`; Vercel function at `api/index.ts` re‑exports the handler
(`runtime: nodejs`, `maxDuration: 60`, `framework: null` rewrite);
Docker runs the Node bundle.
- Runtime: reimplement security headers; dev rate limiter uses high‑res
timers; graceful SIGTERM.
- Env/dev: expand nested env refs; load `.env.development` in dev;
TS/Vitest alias `next/*` to shims.
- Deps/tooling: re‑add `@sinclair/typebox`; regenerate `pnpm-lock.yaml`
with pnpm 11.5.0 and pin `[email protected]`.

- **Bug Fixes**
  - Dispatcher: catch `NextNotFoundError` and return 404.
- Emails: deterministic localhost/loopback SMTP fallback (prefer IPv6)
and consistent HTML for snapshot parity.
- E2E parity: restore email‑conflict error; poll all‑users outbox before
asserting; extend refund/transactions timeouts; fix team invitation
revoke setup.
- Build/Docker: prevent `dist` wipes by setting `clean:false` in
db‑migrations `tsdown` config; reorder COPY so `dist/server.mjs` is
present; backend entrypoint uses `dist/server.mjs`.
- CI/tests: Vitest `minWorkers: 1`; fallback e2e starts the Elysia
bundle via `pnpm run start` with `PORT`.
- Shared: normalize `esbuild-wasm` default export under Node to avoid
runtime mismatches.

<sup>Written for commit d3c9b0ff22.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1630?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
* Improved email delivery by adding localhost/loopback-aware SMTP retry
behavior and more consistent delivered email HTML.
  * Enhanced error reporting during external database synchronization.
* Refined OAuth provider type validation to better handle missing or
custom provider configurations.

* **Improvements**
* Updated backend runtime and routing/request handling for more
consistent behavior, including updated Vercel/Docker startup.
* Improved payment “dual write” reliability by scheduling Bulldozer
projection updates with per-tenant ordering.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mantra <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Bilal Godil <[email protected]>
2026-07-09 21:39:28 -07:00
Konsti WohlwendandGitHub d5ed20863a Enable dev tool Dashboard tab via development-environment probe instead of env var (#1749) 2026-07-09 20:23:07 -07:00
devin-ai-integration[bot]andGitHub f44d5d9bba Auto re-seed bulldozer-js from Postgres during restart-deps (#1745)
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
2026-07-08 20:15:38 -07:00
Konsti WohlwendandGitHub a53a5243aa Silence spurious import.meta CJS warning during pnpm run dev (#1751) 2026-07-08 20:11:33 -07:00
Konstantin Wohlwend 38237fe363 Decrease BulldozerJS log spam 2026-07-08 15:56:59 -07:00
Konsti WohlwendGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
bf93740c7e feat: add CLI Auth dashboard app (#1739)
## Summary

New "CLI Auth" app registered in `apps-config.ts` (alpha, parent:
authentication) and `apps-frontend.tsx` (TerminalWindowIcon, `/cli-auth`
route).

**Backend** — `GET /internal/cli-auth`:
- Queries `CliAuthAttempt` (last 50) with computed status from
`usedAt`/`refreshToken`/`expiresAt`
- Joins claimed `refreshToken` values against `ProjectUserRefreshToken`
to find active CLI sessions + user info via `ProjectUser`
- Returns `{ summary, recent_attempts, active_cli_users }`

**Dashboard** — `/cli-auth/page-client.tsx`:
- Fetches via `hexclaveAppInternalsSymbol` →
`sendRequest("/internal/cli-auth", {}, "admin")`
- Renders KPI cards (total/completed/expired/active), active sessions
list with last-active time, and recent attempts with status badges
- Expired sessions collapsed by default under `<details>`

Link to Devin session:
https://app.devin.ai/sessions/0868d1452a024b9da36b9d6a45044ff3
Requested by: @N2D4

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds an alpha CLI Auth dashboard to track CLI login attempts and active
refresh tokens, powered by a hidden admin-only endpoint.

- **New Features**
- Registers `cli-auth` under Authentication; adds `/cli-auth` route with
TerminalWindowIcon and docs link.
- Backend `GET /internal/cli-auth`: summary stats (incl. used_attempts),
last 50 attempts with computed status, and active CLI users by joining
CLI-issued refresh tokens (bounded).
- Dashboard fetches via admin request and shows KPIs, active sessions
(last-active/expiry), and recent attempts; expired sessions are
collapsed.

- **Bug Fixes**
- Use per-project admin app (`useAdminApp`) to avoid
ADMIN_AUTHENTICATION_REQUIRED and ensure correct tenancy scoping.
- Resolve primary emails for active sessions via `ContactChannel` join
to prevent 500s.
- Fix badges by using `DesignBadge` label prop and set expired to red;
add default cases in status switches.
- Bound token lookup to last 200 CLI-issued tokens and use a separate
COUNT(*) for accurate `active_tokens`.
  - Align loading skeleton grid with the KPI layout.
  - Docs: add `cli-auth` icon.

<sup>Written for commit 292859e921.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1739?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a **CLI Auth** dashboard page with metrics for active sessions,
an expandable expired session list, and recent login attempts.
* Added a hidden internal analytics endpoint powering the dashboard
(summary, recent attempts, and active users).
* Registered **CLI Auth** in the app catalog/navigation (alpha) and
added its icon to the docs UI.
* **Bug Fixes**
* Improved request/response validation, loading/error states, and
avoided state updates after unmount.
* **Documentation**
* Updated docs indexing settings and added a redirect for a related
guide.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-08 12:34:34 -07:00
Konsti WohlwendandGitHub a14b595df3 Add browser script/esm.sh no-bundler setup to the setup prompt (#1743) 2026-07-08 12:04:34 -07:00
Konsti WohlwendandGitHub 4cb0affb64 Fix CI: stop e2e dev-env leaking into hermetic package tests + refresh stale project snapshots (#1744) 2026-07-08 12:01:46 -07:00
Konsti WohlwendandGitHub 165d9a0786 feat: center globe on viewer'S location (#1715) 2026-07-08 10:33:30 -07:00
Konsti WohlwendandGitHub f38fde7d05 Fix CI: externalize AWS SDK from migration bundler + move Bulldozer HTTP calls out of Prisma transactions (#1716) 2026-07-08 10:22:00 -07:00
Konsti WohlwendandGitHub 7a78085a49 Preserve the caught error in the billing-degradation error boundaries (#1741) 2026-07-08 09:26:17 -07:00
Madison 1837f5db2a generated docs.json for broken redirects for SEO fixes 2026-07-07 23:42:13 -07:00
github-actions[bot] 105f03fb94 chore: update package versions dashboard-v1.0.51 2026-07-06 23:34:27 +00:00
Konstantin Wohlwend e33369fecd Small AI setup prompt update
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
Publish RDE dashboard release / publish-dashboard (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
Publish npm packages / publish (push) Has been cancelled
Publish Swift SDK to prerelease repo / publish (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
2026-07-06 16:31:32 -07:00
Konsti WohlwendandGitHub 0238f0ac37 Keep the dashboard loading when bulldozer is down (internal billing degrades gracefully) (#1740) 2026-07-06 16:30:02 -07:00
Madison 6ed51f5a11 Fix docs SEO blocking all pages, and add perm redirect to the guides/going-further/backend-integration -> guides/going-further/local-vs-cloud-dashboard 2026-07-06 13:00:46 -05:00
Aman GanapathyandGitHub beaf009040 [Fix](Dashboard): OOM risk with unbounded scroll box growth (#1735)
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
### Context
The scroll box virtualization on the data grid component was growing
monotonically with number of rows, leading to OOM.

### Summary of Changes
Bound the number of rows in the scroll box


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Prevent OOM by bounding unbounded infinite-scroll DataGrid instances so
virtualization can window rows. When the grid is infinite and has no
height, we cap it and observe against its own scroll container;
paginated grids are unchanged.

- **Bug Fixes**
- Apply a default `maxHeight: calc(100dvh - 16rem)` when
`paginationMode="infinite"` and no `fillHeight`/`maxHeight` is provided.
- Use the grid’s scroll container as the IntersectionObserver root in
this case; sticky header respects the effective max height.
- Add tests for default maxHeight, observer root, and to ensure
paginated grids do not get a forced maxHeight.

<sup>Written for commit e203b643cf.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1735?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-07-05 23:00:33 -07:00