Tier1 × Pannell Co · Time-Tracking Integration · Architecture Fork

Keep the legacy engine bolted on, or absorb it into one app.

One decision, made once, that quietly re-shapes five of the six lanes. This is the honest tradeoff — cost, ownership, risk, and exactly what changes where — laid out so the choice is obvious by the time you've read it, not before.

Prepared 2026‑07‑30 · rev 4 Decision owner Abel Build owner if rewrite head developer (already leaning this way) Cognito no longer in scope — Pannell's own login covers it

Your own words already point one direction. You said the thing you can't accept is being unable to fully service this client — and that a permanently-bolted-on Vue/Supabase module is exactly the 5% you'd never fully own.

Everything below is here to pressure-test that instinct, not decorate it. The credit cost of the rewrite is small — smaller than the buffer you were carrying. The one real risk is confirming that ported payroll and reconciliation logic still returns identical results — and that's automatable and local: a script runs the old function and its new port against the same real data and diffs the output. A risk named, then closed — not a reason to hesitate.

01

The two paths, side by side

Same frontend either way. Login is Pannell's own existing auth in both cases — Cognito is no longer in scope. The fork is only about what lives behind the routes.

Path A · current locked plan

Keep the sealed module

The Vue app's backend keeps running as-is — self-hosted Supabase edge functions inside a Fargate container. The new app reaches it through a gateway that proxies, mints a second token, and mirrors roles into it.

What you keep

  • Proven logic, untouched. Payroll math you never have to re-verify.
  • Closest to launch — lanes 02/03 already have mature designs.
  • You never have to own the module's 21K lines.
  • If the client keeps their Vue app, that's a billable relationship.

What it costs you, forever

  • Two codebases — Next.js plus a Deno/Vue module in a stack that isn't yours.
  • Fargate + self-hosted Supabase running every month.
  • More points of failure: proxy hop, token mint, mirror drift, an extra container.
  • You own ~95% of the app. The module stays a foreign object.
  • A clean future public API gets harder, not easier.

Path B · the pivot

Absorb it into one app

Port the ~99 relevant backend functions into Next.js API routes. Retire Supabase entirely. One app, one database, one stack — the logic sits directly behind the same routes, no proxy in between.

What you gain

  • One codebase, one stack. Tier1 owns 100%.
  • No Fargate container — fewer moving parts, fewer failure points.
  • No Supabase at all — retired, not maintained. Zero vendor surface.
  • No new identity provider — migrated users just join Pannell's existing login.
  • Gateway proxy, token mint, and the whole role-mirror simply vanish.
  • Clean path to a future API; full ability to service the client alone.

What it costs you, once

  • Confirming the port preserved exact outputs — real, but scriptable: an automated old‑vs‑new diff run locally against real data, not open‑ended manual QA.
  • Knowledge debt — you own the ported logic (mitigated: a month of study + Claude Code).
  • You lose the billable-Vue-maintenance angle (arguably a liability anyway).
  • Adds rewrite + QA time before launch.
02

The honest cost stack

Where the money and effort actually go under each path. The credit numbers are the real scoped estimates — not the buffer you were carrying.

Line itemKeep the moduleAbsorb into one app
Backend logic conversion (AI credits) $0
nothing to convert
$25–55
99 functions, ~21K lines
Backend verification & QA Minimal
logic runs as-is
Automated bulk run
every function, every input, diffed at once; a human classifies each distinct mismatch pattern once (a handful of calls), the harness auto-resolves every repeat
Frontend conversion (AI credits) $35–80 $35–80 — identical
Auth build · lane 02 Gateway + token‑mint + Fargate module hosting Pannell's existing login — no Cognito, no mint, no container
Roles sync · lane 03 Build SQS + Lambda + nightly reconciliation Deleted role checks happen in code
Data migration · lane 04 Into the module's Postgres Into one standalone Postgres (likely smaller — RLS drops)
Mass upload · lane 05 Same Same — untouched either way
Ongoing hosting (every month, forever) Fargate + Supabase Just the app's database
Codebases to maintain 2 Next.js + Deno/Vue module 1
Share of the app Tier1 fully owns ~95% module is foreign 100%
Operational points of failure More proxy · mint · mirror · container Fewer

On your $150–200 figure: that's a safe all-in buffer. The actual credit cost is smaller — roughly $25–55 backend plus $35–80 frontend. And the verification that used to look scary is mostly automated and local: Claude writes a harness that runs every old function and its new port against the same real data, all at once, and diffs the outputs — no Playwright, no browser, it's backend‑to‑backend. A human classifies each distinct pattern of mismatch once (timestamp field, generated ID, row order — a handful of calls total), and the harness auto-resolves every repeat of that pattern from then on. Price the decision on the ownership and ops savings, not on a QA ceremony that largely runs itself.

03

The ripple, lane by lane

Relative build weight under each path — the taller the after-bar shrinks, the more work the rewrite removes. Two lanes don't move at all.

LANE 01 Frontend Unchanged
now
after

Same 47 routes, same Rosetta Stone, same cost. Only the word "gateway" is re-defined — cosmetic.

LANE 02 Authentication Mostly dissolves
now
after

Cognito isn't needed at all — Pannell's own login already handles both field and admin sign-in. Token-mint, Fargate, and the proxy hop go with it. What's left: migrated users join the existing login.

LANE 03 Roles & Permissions Collapses
now
after

The whole mirror — SQS, Lambda, reconciliation — is gone. Only the one-time role-mapping decision survives.

LANE 04 Data Migration Retargeted
now
after

Still lift the live database — but into one plain Postgres the app queries directly. RLS likely doesn't migrate at all.

LANE 05 Mass Upload Unchanged
now
after

Already targets Pannell's own backend and rebuilds natively. Nothing here touches it.

04

What actually tips it — your priorities, mapped

Not a generic pros list. These are the things you said mattered, and which path each one points toward.

"I don't want to be at risk of not being able to service this client completely."
→ Absorb
"Fewer points of failure, simpler ops, and eventually an easier public API."
→ Absorb
"I understand the code well enough now — Claude Code makes owning it manageable." — de-risks the rewrite's one real drawback.
→ Absorb
"My head developer already wants to rewrite the whole thing and take it on."
→ Absorb
If the client keeps developing their Vue app, Tier1 can bill for updates. — the one genuine pull toward keeping it… but an ongoing obligation dressed as revenue.
→ Keep
Getting to launch soonest, with the least new build work. — the honest case for not rewriting now, though it's a smaller gap than it looks once the verification is an automated diff, not a manual pass.
→ Keep
05

Inside the rewrite: two ways to do it

Choosing to absorb the backend still leaves one fork — and this, not Cognito, is what actually moves the verification risk. It comes down to one thing: do you keep the database's row-filtering, or rebuild it in code?

Recommended

Keep the row-filtering in the database

Next.js routes replace the old functions — but the database keeps the row-level-security policies it already has. Each route connects carrying the user's identity, so the database filters rows exactly as it does today.

Verification loadLighter

You inherit

Battle-tested filtering, untouched — nothing to re-derive. The residual risk is one uniform thing: passing identity into the database session correctly, solved once in a connection helper and reused everywhere.

The tradeoff

You keep maintaining the database's policy code (a Postgres skill), and the database stays set up to read a per-request identity. Since lane 04 already copies the database with its policies intact, this is the less-work path there too.

Also viable

Rebuild the filtering in app code

Drop the database policies entirely; every row-filter the database used to do silently becomes explicit query logic written into each Next.js route.

Verification loadHeavier — closed by §06

You gain

No database-specific policy skill to keep; all logic in one place, one language. Cleaner if you ever want zero logic living inside the database.

The tradeoff

Every filter has to be re-derived — a larger surface, and exactly the case the automated old‑vs‑new diff harness in §06 is built to catch. Named here, closed there; it's why that harness exists.

Neither mode needs Cognito. Keeping the database policies just needs the app to carry the logged-in user's identity into the database connection — which your own Next.js server does directly, having already authenticated them through Pannell's existing login. Cognito was never part of this layer at all.

06

Whichever mode — the gate that closes it

The one real risk — and the gate that closes it.

The place a ported function can go wrong isn't the arithmetic — that copies over verbatim, unchanged. It's how rows get filtered by who's asking. Keeping the database's own filtering leaves that risk small from the start. Rebuilding it in code carries more of that surface — so that path leans on this gate more, not less.

Either way the gate is the same: run every old function and its new port against the same real historical data, across each role, and diff the outputs. Claude writes that harness; it runs locally against a database copy; anything that doesn't match byte‑for‑byte gets flagged before go‑live. A real risk, named — with a concrete, mostly‑automated path straight through it. The debt you take on is debt you've actually retired, not just moved.