GrantPro AI 收費前審查找出約 100 個錯誤
I Have No Coding Skills. My Team Is 3 AI Models. Here’s the 100-Bug Audit That Saved My Second Startup.
GrantPro AI 創辦人讓第三個 AI 模型在收費前審查程式碼,找出約 100 個獨立錯誤和 56 項程式碼品質問題。問題包括 Paddle webhook 將兩項產品映射至同一 priceId、前端扣點可被繞過,以及 anon 角色可讀取 500,000 筆補助紀錄;修正措施包括資料庫層冪等檢查、伺服器端扣點及收緊資料表權限。團隊亦制定跨檔案不變條件,並用舊版錯誤程式回歸測試審計提示詞。
Two weeks ago, I published the honest account of building ThumbRank with a team of three AI models and zero coding skills. The response was great — mainly because it wasn't a story about how AI writes perfect code. It was a story about how AI writes code that looks perfect until you try to charge money for it.
I didn't stop after ThumbRank. I pointed the same team at a much bigger problem: GrantPro AI.
It’s a funding intelligence platform. The core philosophy is simple: real numbers or guesses — there is no third option. Instead of asking an LLM to guess if you’ll win a grant, we ingest millions of public award records (USAspending, EU CORDIS, NIH, UKRI) and use deterministic code to calculate your real odds.
Same team structure as before:
AI #1: Writes features (React, Supabase, Edge Functions).
AI #2: Handles design and infrastructure.
AI #3: Exists for one reason: to audit everything the first two ship.
Me: The founder, PM, and QA department of one.
Month 1 looked like progress. We had a data pipeline, a dashboard, and a Paddle checkout.
Then I pointed AI #3 at the codebase before letting anyone pay.
It came back with ~100 unique bugs and 56 code-quality issues. Some of them would have destroyed the product on day one. Here are the ones that almost killed us, and how we fixed them.
- The $0.00 Paddle Webhook Disaster The Bug: The AI configured the Paddle webhook to listen for price IDs. But it accidentally mapped two products to the same priceId. The Consequence: Users who bought the 1-credit pack received 5 credits. Users who bought the 5-credit pack received zero. The frontend showed "Success", the webhook returned 200 OK, but the database was silently dropping the money.
The Fix: We moved the idempotency check to the database level (Postgres unique index) instead of the application layer. If a webhook event arrives twice, or a mapping is wrong, the database throws an error and we log it.
sql
CREATE UNIQUE INDEX uq_credit_transactions_kind_reference
ON credit_transactions (kind, reference_id)
WHERE reference_id IS NOT NULL;
- Client-Side Credit Spending (The "Free SaaS" Glitch) The Bug: The AI wrote the credit deduction in the React frontend. If the user had credits > 0, the UI let them click "Generate" and then called the Edge Function. The Consequence: Anyone who opened the browser console could bypass the UI and call the API directly. We were one curl command away from going bankrupt on OpenAI API costs.
The Fix: We deleted all client-side credit logic. Spending now happens exclusively via a Postgres SECURITY DEFINER RPC that checks the user's balance atomically on the server.
sql
CREATE OR REPLACE FUNCTION public.spend_credit(p_kind text)
RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
UPDATE credits SET balance = balance - 1
WHERE user_id = auth.uid() AND kind = p_kind AND balance > 0;
IF NOT FOUND THEN RETURN false; END IF;
RETURN true;
END; $$;
- The Public Data Moat Leak The Bug: We spent a month ingesting 500,000 grant records into tables like awards and win_assessments. The AI enabled Row Level Security (RLS), but left the SELECT policy open to the anon role for caching purposes. The Consequence: Our entire proprietary Data Empire — the thing we built the company around — was fully readable by anyone with a Supabase anon key (which is public by design).
The Fix: We dropped the public read policies. If a table contains the "secret sauce" (historical awards, win probabilities), it is locked to service_role or authenticated users only.
- The Zombie Function The Bug: The AI built an entire compliance checker. A beautiful UI, a complex prompt, and a Deno Edge Function. The Consequence: It literally didn't work. The AI forgot to export the handler (export default async function...). The function compiled, but when called by the frontend, it silently failed. It was a zombie feature.
The Fix: We added a CI gate that explicitly tests the exports of every Edge Function before deployment.
Why did the AI miss these?
Here is the hard truth about coding with LLMs: The AI is incredible at writing isolated files. It is terrible at understanding cross-file intersections.
AI #1 wrote the React checkout. AI #2 wrote the Paddle webhook. Neither of them knew that they needed to agree on how a user_id is passed. The bug lived in the gap between them.
The Rule We Build By Now
We didn't just fix the bugs. We changed the process.
The Invariants Doc: We wrote a document of cross-file rules. "Every paid checkout ends with credits on the right identity." "No public table is writable with the anon key." AI #1 reads this before coding. AI #3 reads this before auditing.
Regression-Testing the Auditor: We kept the old, buggy commit. Every time we update the auditor's prompt, we run it against the old code. If it doesn't find the 100 bugs again, the prompt got too soft, and we reject it.
What's Next?
GrantPro AI is live. We are tracking real grants across the US, EU, UK, Canada, and Japan.
If you are building with AI, remember this: The code the AI writes is only half the product. The other half is the system you build to catch it.
You can run a free win-check on your next grant proposal here: grantproai.com/win-check
來源:DEV Community · dev.to