Before I move on to HR-related material, I wanted to share a useful tool with you.
It’s a free prompt builder for an invoice mechanism, similar to others that have been created.
As you may know, Replit billing can be a hassle. I was tired of complaining about it and honestly, I was tired of listening to myself.
So, I decided to create a tool to temporarily fix my problem and behave.
Others have developed great tools that they’ve shared with the community. I wholeheartedly endorse them and hope users continue to use whatever method suits them. This is not an attempt to diminish or criticize any of the excellent products or solutions that have been created so far.
Some users may not want to share these details with anyone outside of Replit (which already bills us and knows all this information) to minimize the number of touchpoints. They may wish that Replit would expedite the process of integrating this feature into Stripe Billing.
Crickets assumed their position, and well, here we still are.
However, they have taken action and have asked their engineering staff to produce the following build session (which was great, by the way!) Replit Live Builds - What should we build next in Replit? .
I loaded this prompt material into Claude.
I instructed Claude to carefully examine the language used.
Then, I asked Claude, “Are you able to generate content using the voice of this narrator? If I were to upload a build prompt and want you to rewrite mine as if you were the author of this new build prompt, could you do that?”
Of course, it said it could.
So, I loaded my original prompt in.
Original Prompt:
Main Prompt: Replit Multi-App Billing & Invoice Manager
Prompt: I want you to generate a complete, privacy-first Replit web application that allows users to track, manage, and analyze Replit invoices for multiple projects. The application must have the following features:
- Itemized Billing: • Automatically break down costs by each application developed. • Display current and historical charges per project.
- Invoice Input: • Users can manually enter invoice details or upload them (PDF, CSV, TXT). • Automatically parse uploaded invoices to extract charges.
- Project Organization: • Users can maintain separate collections for personal projects, client projects, or both. • Support multiple accounts per user if desired. • Allow renaming, archiving, or deleting projects/accounts.
- Automatic Tracking & Cataloging: • Track changes to invoices over time. • Update totals and summaries automatically whenever new charges are added.
- Full Data Management: • Users can edit, update, or delete all data entries. • Support undo/redo for recent changes.
- Privacy & Security: • Encrypt all user data end-to-end. • Ensure no one, including the site owner or hosting provider, can access private information. • Use secure local storage or encrypted databases.
- Optional Features: • Visual dashboards for cost tracking. • Exportable reports (CSV, PDF) for clients or personal accounting. • Notifications for unusual charges or budget thresholds.
Technical Requirements: • Responsive front-end UI for desktop and mobile. • Backend support for secure data storage (consider Replit database or third-party encrypted storage). • Modular code structure (frontend, backend, parsing, and storage). • Include clear instructions for setup, running, and usage.
Deliver the project fully functional with all features implemented.
⸻
Supplemental Prompt / Material for Developers
Purpose: Help refine or enhance the app to ensure proper operation anytime a user enters the main prompt.
Instructions for AI or Developers:
- Database Schema: Provide a schema that supports: • Users • Projects (personal vs client) • Invoices (with date, amount, line items, file attachment reference) • Version history for automatic tracking of changes
- File Parsing: Include utilities to parse PDF, CSV, or TXT invoices. Detect and extract line items automatically.
- UI Components: Create: • Dashboard with total costs, project summaries, and visual charts • Invoice entry forms (manual and upload) • Project/account management pages
- Security Features: • Encrypt all data using end-to-end encryption • Implement login/authentication for multi-account users • Ensure all file uploads are securely processed locally or encrypted in storage
- Data Management: Provide full CRUD (Create, Read, Update, Delete) for all entities
- Optional Enhancements: • Notifications for unusual spending patterns • Export data to CSV or PDF • Undo/redo history for edits
Output Requirements: • Provide full, runnable code that works on Replit • Include comments explaining each module • Include setup instructions • Ensure the app works on both desktop and mobile • Maintain modular design so features can be extended or modified easily
and had it refine it while pretending to be the author of the one (which created the live build prompt) producing the following build prompt to do this yourself:
——————————
Main Prompt:
Main Prompt
I want you to read the attached documentation and come up with a plan to build a complete, privacy-first Replit web application that lets users track, manage, and analyze Replit invoices across multiple projects. The app should be clean, modular, and fast — built so that every data decision keeps billing information private, every interaction feels instant, and the codebase stays easy to extend.
Theme: use #9C64FB as the primary accent color with #111111 as the complementary/background color.
You are building a secure, modern billing manager for Replit-hosted projects.
Goal: Create a web app that lets a user import, organize, and analyze Replit invoices at the project level — with full CRUD, version history, encrypted storage, and exportable reports.
A Replit invoice is not a simple bill. It is a dated document covering a billing period that breaks into multiple named charge categories — Agent Usage, Autoscale Compute Units, Autoscale Deployments, Autoscale Requests, Deployments Outbound Data Transfer, Neon Compute Time, Neon Data Storage, and Replit AI Integrations — each with its own unit rate, rate type, and tax treatment. Within each category, every line item is keyed to a repl by UUID, not by name. Each category may also carry one or more credit adjustments: pre-purchase credits drawn down against gross charges, previously-invoiced deductions to prevent double-billing, and promotional discounts. The net amount due is what remains after all three are applied. Design every data and UI decision around this structure.
Primary user story: “As a logged-in user, I can upload a Replit invoice PDF, have it automatically parsed into charge categories and line items, map my repl UUIDs to readable project names, and immediately see how my costs break down across all my projects — gross versus net, by category, by repl, and over time.”
——————————
Supplemental Prompt:
Information Architecture
User: owns all data.
Project: { id, ownerId, name, type (personal|client), status (active|archived), createdAt, updatedAt }
Subscription: { id, ownerId, plan, monthlyAmount, billingCycleStart, status } — tracks flat plan fees separately from usage invoices so users see true total cost of ownership.
Invoice: { id, projectId, ownerId, invoiceNumber, billingPeriodStart, billingPeriodEnd, invoiceDate, subtotal, taxRate, taxAmount, amountDue, source (manual|upload), fileRef, createdAt, updatedAt }
ChargeCategory: { id, invoiceId, name, grossAmount, taxRate, netAmount } — one record per named section on the invoice (Agent Usage, Neon Compute Time, etc.).
LineItem: { id, chargeCategoryId, replUuid, description, quantity, unitRate, unitType (flat|per-unit|per-gb|per-hour|per-token), grossAmount, netAmount }
Adjustment: { id, chargeCategoryId, type (prepurchase|previously-invoiced|discount), description, amount } — always negative, always scoped to a charge category.
ReplMapping: { id, ownerId, replUuid, projectId, label, addedAt } — resolves raw UUIDs to human-readable project names. Built incrementally as users name repls on first parse.
AIUsageDetail: { id, lineItemId, provider, model, tokenType (input|output), quantity, amount } — required for AI Integrations lines which carry a four-level hierarchy: repl / provider / model / token type.
VersionHistory: { id, entityType, entityId, delta, changedAt }
Tag: { id, ownerId, name }
MVP Requirements
Authentication + user accounts: Users sign up and sign in. All data is scoped to the authenticated user. Encrypt all stored data end-to-end so no third party including the host can read it.
Invoice input: Users upload a PDF, CSV, or TXT invoice or enter details manually. The parser must handle: section headers identifying charge category and billing period; UUID-keyed line items with quantity, unit rate, and amount columns; negative adjustment rows (pre-purchase, previously invoiced, discount) treated as Adjustment records not line items; multi-part AI integration descriptions in the format replUuid / provider / model / tokenType; summary rows at the bottom (Subtotal, Sales Tax, Amount Due); and tax rate extraction from inline category headers. After parsing, surface a review screen showing the structured output before the user confirms and saves.
Repl naming: On first parse of any UUID not yet in ReplMapping, prompt the user to assign a readable name. Store that mapping so every subsequent invoice resolves it automatically.
Project organization: Users maintain separate collections for personal and client projects. Support multiple Replit accounts per user. Allow renaming, archiving, and deleting projects.
Automatic tracking: Track changes to invoices over time with a full version history. Update category summaries, project totals, and gross-versus-net breakdowns automatically whenever new data is added.
Library UX: Main dashboard shows project summaries, subscription costs alongside usage totals, per-category cost breakdowns, and recent invoice activity. Search across project names, repl labels, invoice dates, charge categories, and amounts. Filter by project type, tag, and date range. Optimistic UI updates throughout so everything feels instant.
Technical Requirements
Modular code structure with clear separation across frontend, backend, parsing, and storage layers. Responsive UI for desktop and mobile. Backend with secure encrypted storage using Replit DB or a third-party encrypted option where all encryption stays client-side. Shared utility modules so parsing logic and UI components are never duplicated. Include setup instructions, inline code comments per module, and a clear run guide.
Optional Enhancements
Visual cost dashboards with charts per project, per charge category, and per billing period. Side-by-side invoice comparison to surface cost changes period over period. Notifications when a project crosses a budget threshold or when a charge category spikes unexpectedly versus the prior period. Exportable reports in CSV and PDF. Undo/redo for recent edits across all entities.
Supplemental Prompt / Developer Reference
Purpose: Lock in the implementation details so any agent or developer picking up the main prompt builds the right thing the first time. Every section below reflects a problem that was encountered and solved in the live implementation — follow these exactly.
Stack
| Layer | Technology |
|---|---|
| Monorepo | pnpm workspaces |
| Node.js | v24 |
| API | Express 5 |
| Database | PostgreSQL + Drizzle ORM |
| Validation | Zod v4, drizzle-zod |
| API codegen | Orval (OpenAPI → React Query hooks + Zod schemas) |
| Build | esbuild (API), Vite (frontend) |
| Frontend | React + Tailwind CSS v4 |
| Auth | Replit OIDC (@workspace/replit-auth-web) |
| PDF parsing | unpdf (rule-based) + GPT AI fallback |
| AI | @workspace/integrations-openai-ai-server, model gpt-5-mini |
| Charts | Recharts |
Database Schema
CRITICAL: date columns must be text, not timestamp.
billingPeriodStart, billingPeriodEnd, and invoiceDate must be stored as text (ISO date strings like "2026-03-01"), not timestamp columns. Replit invoices do not always have parseable timestamps and storing as text avoids timezone edge cases and Zod coercion crashes.
export const invoicesTable = pgTable("invoices", {
id: serial("id").primaryKey(),
projectId: integer("project_id").references(() => projectsTable.id),
ownerId: text("owner_id").notNull().references(() => usersTable.id),
invoiceNumber: text("invoice_number").notNull(),
billingPeriodStart: text("billing_period_start").notNull(), // TEXT not timestamp
billingPeriodEnd: text("billing_period_end").notNull(), // TEXT not timestamp
invoiceDate: text("invoice_date").notNull(), // TEXT not timestamp
subtotal: numeric("subtotal", { precision: 12, scale: 2 }).notNull(),
taxRate: numeric("tax_rate", { precision: 6, scale: 4 }).notNull().default("0"),
taxAmount: numeric("tax_amount", { precision: 12, scale: 2 }).notNull().default("0"),
amountDue: numeric("amount_due", { precision: 12, scale: 2 }).notNull(),
source: text("source").notNull().default("manual"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => new Date()),
});
Cascade delete chain must be: invoices → charge_categories → line_items → ai_usage_details AND adjustments. Use { onDelete: "cascade" } on every FK reference.
All numeric columns are returned as strings by PostgreSQL/Drizzle. Always parseFloat() before arithmetic. Never assume they are JavaScript numbers.
Zod Date Schema Bug — Non-Negotiable Rule
drizzle-zod’s createInsertSchema generates zod.coerce.date() for text columns with date-like names. When a response object is run through .parse(), an empty string "" coerces to new Date("") which is Invalid Date, causing a hard invalid_date Zod crash on every request that returns an invoice.
Rule: Never call .parse() on API response data. Only use generated Zod schemas to validate incoming request bodies.
// ❌ WRONG — crashes on empty text date columns
res.json(ListInvoicesResponse.parse(invoices));
// ✅ CORRECT — DB data is trusted; serialize directly
res.json(invoices);
// Request body validation is fine — this is incoming, untrusted data
const parsed = UploadInvoiceBody.safeParse(req.body);
The same rule applies to the dashboard route — recentInvoices contains invoice rows with text date columns and must not be .parse()d.
Tailwind CSS v4 — Dark Mode Setup
@apply dark is invalid in Tailwind v4 and will crash the build.
Correct approach:
- Add
class="dark"to the<html>element inindex.html - Add
@custom-variant dark (&:is(.dark *))in your CSS entry file
Never use darkMode: 'class' in a config file — Tailwind v4 does not use a config file for this.
Auth
Always use @workspace/replit-auth-web’s useAuth() hook for authentication state in the frontend. Never use the generated API client for auth routes. Auth routes (/login, /callback, /logout, /auth/user) are handled by the backend’s Replit OIDC middleware and should be called via direct fetch or redirect, not via React Query hooks.
PDF Parsing — The Blob Problem
unpdf with mergePages: true concatenates all page text without any spaces or newlines. A real Replit invoice becomes a single unreadable blob:
ABCDEF-00001InvoicedateJan012025DuedateJan012025Amountdue12345DescriptionQuantityRateexcltaxTaxAmountexcltaxAgentUsageJan2025...
There are no word spaces, no section breaks, and UUIDs are glued to adjacent text. A naive line-by-line parser will find nothing.
The fix: always run preprocessPDFBlob() before any parsing. This function is idempotent (ratio check at top prevents double-processing):
function preprocessPDFBlob(text: string): string {
// If text already has adequate newline structure, return as-is
const newlineRatio = (text.match(/\n/g) || []).length / Math.max(text.length / 80, 1);
if (newlineRatio > 0.3) return text;
let result = text;
// Step 1: restore spaces at camelCase boundaries
// "AgentUsage" → "Agent Usage", "InvoiceDate" → "Invoice Date"
result = result.replace(/([a-z])([A-Z])/g, "$1 $2");
result = result.replace(/(\d)([A-Z][a-z])/g, "$1 $2");
// Step 2: prefix known Replit category names with a newline
// so each category becomes its own line for the section parser
const categoryPatterns: [RegExp, string][] = [
[/Agent\s+Usage/gi, "\nAgent Usage"],
[/Autoscale\s+Compute\s+Units/gi, "\nAutoscale Compute Units"],
[/Autoscale\s+Deployments/gi, "\nAutoscale Deployments"],
[/Autoscale\s+Requests/gi, "\nAutoscale Requests"],
[/Deployments\s+Outbound\s+Data\s+Transfer/gi, "\nDeployments Outbound Data Transfer"],
[/Neon\s+Compute\s+Time/gi, "\nNeon Compute Time"],
[/Neon\s+Data\s+Storage/gi, "\nNeon Data Storage"],
[/Neon\s+Written\s+Data/gi, "\nNeon Written Data"],
[/Replit\s+AI\s+Integrations/gi, "\nReplit AI Integrations"],
[/(?<!\bReplit\s+)AI\s+Integrations/gi, "\nAI Integrations"],
[/Static\s+Deployments/gi, "\nStatic Deployments"],
[/Always\s+On/gi, "\nAlways On"],
[/Ghost\s*writer/gi, "\nGhostwriter"],
];
for (const [pattern, replacement] of categoryPatterns) {
result = result.replace(pattern, replacement);
}
// Step 3: put each UUID on its own line — every Replit line item starts with a UUID
result = result.replace(
/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi,
"\n$1"
);
// Step 4: put summary keywords on their own lines
const summaryKeywords = [
"Subtotal", "Sales Tax", "Amount due", "Amount Due",
"Pre-purchase", "Previously invoiced", "Previously Invoiced", "Discount",
"Description Quantity",
];
for (const kw of summaryKeywords) {
result = result.replace(
new RegExp(`(${kw.replace(/\s+/g, "\\s+")})`, "gi"),
"\n$1"
);
}
return result;
}
Usage in the upload route — preprocess once, pass everywhere:
const processedTextContent = preprocessPDFBlob(textContent);
let result = parseInvoiceText(processedTextContent); // rule-based
// AI fallback also uses processedTextContent (not the raw blob)
parseInvoiceText() should also call preprocessPDFBlob() internally, but the ratio check prevents double-processing so this is safe.
Invoice Number Extraction
Replit invoice numbers follow `` (e.g. ABCDEF-00001). Extract with a direct regex applied to the full preprocessed text before the line-by-line loop, so the number is found even when there is no “Invoice Number:” label:
// At the start of parseInvoiceText(), after preprocessing:
const rawInvoiceNumMatch = processedText.match(/\b([A-Z]{2,10}-\d{4,})\b/);
if (rawInvoiceNumMatch) {
invoiceNumber = rawInvoiceNumMatch[1];
}
Always cap the invoice number at 30 characters when extracting from labels to prevent blob overflow into the field:
const clean = val.replace(/[^a-zA-Z0-9-_]/g, "");
if (clean.length <= 30) invoiceNumber = clean;
AI Fallback (Privacy-First)
Run a quality score on the rule-based result. If score < 60, call AI. AI only ever sees sanitized text — all amounts, emails, and addresses are replaced with tokens before the text leaves the server.
Quality scoring:
function scoreParseQuality(result: ParsedResult): number {
let score = 0;
if (result.invoice.invoiceNumber) score += 20;
if (result.invoice.invoiceDate) score += 15;
if (result.invoice.billingPeriodStart) score += 10;
if (result.invoice.billingPeriodEnd) score += 10;
if (parseFloat(result.invoice.amountDue) > 0) score += 20;
if (result.categories.length > 0) score += 15;
for (const cat of result.categories) {
if (cat.lineItems.length > 0) score += 5;
}
return Math.min(score, 100);
}
Privacy sanitization — strip all PII and amounts before AI:
function sanitizeForAI(text: string): string {
return text
.replace(/\$\s*[\d,]+\.?\d*/g, "[AMOUNT]")
.replace(/[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}/g, "[EMAIL]")
.replace(/\b\d{1,3}\s[\w\s]+,\s[\w\s]+,\s[A-Z]{2}\s\d{5}\b/g, "[ADDRESS]")
.replace(/^\s*Bill(?:ed)?\s+(?:to|from):?.*$/gim, "")
.replace(/^\s*(?:Company|Organization|Name):?\s*.+$/gim, "")
.replace(/credit card[^$\n]*/gi, "[PAYMENT_METHOD]");
}
AI call with hard 10-second timeout — mandatory:
const timeoutMs = 10_000;
const timeoutPromise = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("AI timeout")), timeoutMs)
);
const response = await Promise.race([
openai.chat.completions.create({
model: "gpt-5-mini",
max_completion_tokens: 2048,
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: `Extract the invoice structure:\n\n${sanitized.slice(0, 8000)}` },
],
}),
timeoutPromise,
]);
Key limits: input trimmed to 8000 chars, max_completion_tokens: 2048, 10s hard timeout. If the AI call throws or times out, silently continue with the rule-based result — never surface AI errors to the user.
AI system prompt (structure only, no amounts):
You are a structured data extractor for billing invoices. You will receive invoice text where:
- All dollar amounts have been replaced with [AMOUNT] — do NOT guess or infer any amounts
- Email addresses have been replaced with [EMAIL]
- Addresses have been replaced with [ADDRESS]
Your job is to identify STRUCTURE ONLY: category names, line item descriptions/classifications, adjustment types, dates, and invoice numbers.
CRITICAL RULES:
1. Never include or infer any dollar amounts in your response
2. Do not report payment methods or personal information
3. Only identify structure and labels from what is explicitly in the text
4. For unitType, infer from context: per-token for AI/model usage, per-hour for compute hours, per-gb for data transfer/storage, flat for flat fees, per-unit for everything else
Return ONLY valid JSON — no markdown, no explanation.
AI merge strategy: AI provides structure (names, descriptions, unitTypes). The rule-based parser provides amounts (AI never sees them). When merging:
- If rule found 0 categories but AI found some → use AI category names and structure; fill amounts from rule totals where names match.
- If rule found categories → keep rule categories but enrich descriptions/unitTypes from AI matches by UUID or position index.
- AI date/invoiceNumber values only fill gaps; never overwrite successfully rule-parsed values.
Upload Confirmation UI Requirements
The review screen shown after parsing must include:
-
Editable invoice number field — render as an
<input>pre-filled from the parser, not read-only text. The parser occasionally extracts a slightly wrong value from the blob and users must be able to correct it before saving. Pass the edited value when confirming:invoice: { ...preview.invoice, invoiceNumber: editedInvoiceNumber || preview.invoice.invoiceNumber } -
Charge categories preview — show every detected category with name, line item count, adjustment count, and gross amount. If zero categories were detected, show a clear amber warning explaining that the invoice will save with totals only and categories can be added manually later. Do not silently save a bare invoice.
-
UUID mapper — show only when
preview.unknownUuids.length > 0. Pre-fill from existing mappings if the UUID has been seen before.
Database Schema: Support Users, Projects, Subscriptions, Invoices, ChargeCategories, LineItems, Adjustments, ReplMappings, AIUsageDetail, and VersionHistory. Every record is scoped to an ownerId. No cross-user data access at any layer. Adjustments are always negative values scoped to a ChargeCategory, never to an Invoice directly. LineItem.netAmount is grossAmount minus the proportional share of adjustments in its parent ChargeCategory.
File Parsing: Utilities to handle PDF, CSV, and TXT uploads. The parser must operate in sections — identify each named charge category block, extract its line items and adjustments separately, then roll up to category totals and invoice totals. For AI Integrations lines, split the description on / to extract replUuid, provider, model, and tokenType and write an AIUsageDetail record. For any UUID not in ReplMapping, flag it for the user to name before saving. Return a fully structured preview object the frontend renders for user confirmation before any write occurs.
Known Replit charge category names (keep this list in sync with KNOWN_CATEGORIES in the parser):
agent usage, autoscale compute units, autoscale deployments, autoscale requests,
deployments outbound data transfer, neon compute time, neon data storage,
neon written data, replit ai integrations, ai integrations, static deployments,
always on, boost, egress, storage, compute, ghostwriter
UI Components:
- Dashboard: subscription cost + usage cost = true total; per-project summaries; per-category bar breakdown; gross vs net comparison; recent activity feed
- Invoice parse preview: structured category and line item review with inline repl naming for unknown UUIDs, editable invoice number, categories detected count, confirmation before save
- Invoice entry: manual form and file upload
- Project and account management: create, rename, archive, delete, tag
- Repl mapping manager: view and edit all UUID-to-label assignments
- Version history viewer: per-invoice delta log with restore option
Security: End-to-end encryption on all stored data. Auth-gated routes throughout. File uploads processed locally or encrypted before any persistence. No plaintext billing data anywhere in the stack. AI assistance is strictly structure-only — amounts never leave the server in any AI request.
Data Management: Full CRUD on all entities. Undo/redo history for recent edits. Deleting a project archives its invoices before removal, never hard-deletes without confirmation.
Output Requirements: Full runnable code that works on Replit. Inline comments per module. Setup instructions. Desktop and mobile responsive. Modular design so any feature can be extended or swapped without touching unrelated code.
Frontend Date Utilities
Wrap all date-fns calls in try/catch and return the raw string on failure. This handles empty strings and non-ISO values from text DB columns without crashing:
export function formatDate(dateString: string): string {
try { return format(parseISO(dateString), 'MMM d, yyyy'); }
catch { return dateString; }
}
export function formatMonth(dateString: string): string {
try { return format(parseISO(dateString), 'MMMM yyyy'); }
catch { return dateString; }
}
export function formatCurrency(amount: string | number): string {
const parsed = typeof amount === 'string' ? parseFloat(amount) : amount;
if (isNaN(parsed)) return '$0.00';
return new Intl.NumberFormat('en-US', {
style: 'currency', currency: 'USD',
minimumFractionDigits: 2, maximumFractionDigits: 2,
}).format(parsed);
}
Common Pitfalls — Quick Reference
| Symptom | Root Cause | Fix |
|---|---|---|
invalid_date Zod crash on any invoice endpoint |
Calling .parse() on response objects that contain text date columns |
Remove .parse() from all response serialization; only use Zod for request body validation |
| Invoice number field contains the entire PDF as a string | Regex without anchors captured the first run of unspaced text | Use /\b([A-Z]{2,10}-\d{4,})\b/ first; always cap at 30 chars |
| Zero categories or line items saved after upload | Rule-based parser ran against raw blob without spaces or newlines | Always call preprocessPDFBlob() before parseInvoiceText() |
| Upload takes 40+ seconds or times out | AI prompt used full text with no token/time limits | Trim input to 8000 chars, set max_completion_tokens: 2048, wrap in Promise.race with 10s timeout |
| Tailwind dark mode classes have no effect | Using @apply dark (invalid in Tailwind v4) |
Use class="dark" on <html> + @custom-variant dark (&:is(.dark *)) in CSS |
useAuth() import fails |
Using the generated API client for auth | Always import useAuth from @workspace/replit-auth-web |
Arithmetic on database amounts returns NaN |
PostgreSQL returns numeric columns as strings |
Always parseFloat(row.amount) before any math |
| User cannot correct a garbled invoice number | Invoice number rendered as read-only text | Render as an <input> pre-filled from parser; merge edited value on confirm |
The initial build cost around $5 USD I kid you not





