ריצה · 23 באוגוסט 2026
נתנו לשני מודלים את אותה משימת אבטחה
איך הרצנו
הרצנו את שני המודלים דרך Claude Code בפרומפט אחד, בלי המשך אנושי: Claude Fable 5 במאמץ מקסימלי, ו-ox-alpha (stealth/ox-alpha) מנותב דרך OpenRouter באותה סביבה בדיוק. כל אחד קיבל מסד SQLite מקומי (node:sqlite), בלי dependencies נוספים. אחרי הסיום בנינו והרצנו lint על שתי האפליקציות, הרצנו semgrep, כתבנו בדיקות עקיפה מול ה-fixtures של כל אפליקציה, וצילמנו כל תפקיד. הטוקנים כוללים cached context; ל-ox-alpha אין caching דרך OpenRouter ולכן הוא שולח מחדש את הקונטקסט בכל תור.
הפרומפט המדויק
שני המודלים קיבלו את הבריף מילה במילה, מאותו commit בסיס זהה ביט-בביט. הבריף דרש את SPEC.md כ-commit ראשון ונפרד, לפני כתיבת קוד.
להצגת הפרומפט המלא
# Relay Desk — build brief
You are building **Relay Desk**, a slice of a real B2B incident-intake product for
fleet operators. This is a working application, not a mockup.
The repository you are in is a neutral Next.js baseline. Nothing product-specific
has been written yet. Everything below is yours to design and build.
---
## 1. The product
Fleet operators ("tenants") collect incident reports from their drivers, triage
them, and occasionally share a single incident with an outside insurer.
### Actors
| Actor | Belongs to | Can do |
|---|---|---|
| **Reporter** (driver) | exactly one tenant | Submit an incident. See their own submissions and nothing else. |
| **Reviewer** (fleet ops) | exactly one tenant | See every incident in their own tenant. Move incidents through the status workflow. Write internal notes. Generate a share link. |
| **Auditor** (platform staff) | no tenant — cross-tenant | See counts, status breakdowns, and SLA timings **across all tenants**. Must never see incident free text, reporter identities, or photos. |
| **Insurer** | no account at all | Open one shared incident through a link they were sent. Nothing else. |
### Incident
An incident has: a free-text description, a location, an occurrence time, one
uploaded photo, a reporter, a tenant, a status, and internal reviewer notes.
Status workflow: `new → in_review → resolved | rejected`. Transitions are
reviewer-only. Illegal transitions must be rejected.
### The share link
A reviewer can produce a link that lets an insurer — who has no account — view
**one** incident. The link must expire. A reviewer must be able to revoke it
before expiry. The insurer must never see internal notes.
### The auditor dashboard
Platform staff need to spot operational problems across tenants: which tenants
are slow to triage, where incidents are piling up, whether any tenant has an
unusual spike. Build something genuinely useful for that job — while holding the
line that the auditor never sees free text, reporter identity, or photos.
---
## 2. Required deliverables, in this order
**These are three separate commits. The order is part of the task.**
### Commit 1 — `SPEC.md`, before any implementation
Write the spec first and commit it on its own. It must contain:
1. **Data model** — tables, columns, types, keys, indexes.
2. **Roles × permissions matrix** — every actor against every operation, with the
answer for each cell. No prose hand-waving.
3. **Threat model** — what an attacker would try against this system, and what
stops each attempt.
4. **API / server-action contract** — every entry point, its inputs, its
authorization requirement, and its failure modes.
5. **The isolation decision, stated explicitly.** Where is tenant isolation
enforced, in exactly one sentence, and why there rather than elsewhere. Name
what would have to be true for a future developer to accidentally break it.
### Commit 2 — the implementation
Working application. Seeded so it can be demonstrated immediately.
### Commit 3 — `SECURITY.md`
What you defended, how, and — honestly — what you did **not** get to. An accurate
list of residual risks is worth more here than a claim of completeness.
---
## 3. Technical constraints
- Next.js App Router + TypeScript, already scaffolded.
- **Database: `node:sqlite`** (built into Node 24 — `import { DatabaseSync } from "node:sqlite"`).
No ORM, no database dependency.
- **Do not add dependencies** beyond what is already in `package.json`. If you
believe something is genuinely unavoidable, write down why in `SPEC.md` first.
- Tailwind is available and configured.
- Authentication: implement your own session handling. Keep it simple and honest;
do not pretend to production-grade identity. Seed users for each role.
- The app must run with `pnpm dev` and be demonstrable from the seed data alone.
- Uploaded photos are stored locally. Storage layout is your decision.
---
## 4. What "done" means
- All four actors can be demonstrated end to end from seeded data.
- The status workflow enforces its own rules.
- The share link works, expires, and can be revoked.
- The auditor dashboard is useful **and** leaks nothing it shouldn't.
- `pnpm build` and `pnpm lint` both pass.
- The three commits exist, in order.
---
## 5. How this will be judged
Three axes, weighted equally:
1. **Security.** The application will be attacked after you finish, with a suite
you will not see. It probes cross-tenant access, privilege escalation between
roles, share-link forgery and expiry, direct object references on incidents
and attachments, upload handling, injection and stored XSS, and information
leakage through errors and logs. `semgrep` will also be run over the result.
2. **Architecture and planning.** `SPEC.md` and the isolation design are reviewed
directly. A single well-placed choke point that is hard to bypass scores far
above correct-but-scattered checks that a future change could silently miss.
3. **Product quality.** Whether the four journeys actually work, and whether the
interface is something a fleet operator could use.
Time, token usage, and code volume are recorded but are not by themselves a score.
---
## 6. Working agreement
- Ask questions when the brief is genuinely ambiguous. You are being paired with,
not tested in silence.
- Do not commit or push anything outside this repository.
- Say plainly when you have made a trade-off, and what it cost.
התוצאות
| Claude Fable 5 + Claude Code | ox-alpha + Claude Code | |
|---|---|---|
| זמן ריצה | שעתיים ו-3 דקות | שעה |
| תורי הסוכן | 237 | 177 |
| סך טוקניםכולל cached context ו-tool payloads. Fable משתמש ב-prompt caching (77M מזה cache-read זול); ל-ox-alpha אין caching דרך OpenRouter. | 79.4 מיליון | 14.2 מיליון |
| שורות קוד (ts/tsx) | 6,271 | 3,469 |
| בדיקות שנכתבואף אחד מהם לא התבקש לכתוב בדיקות. Fable כתב חבילת בדיקות; ox-alpha לא כתב אף בדיקה. | 87 עוברות | 0 |
| עלות (מחויבת)Fable לפי חיוב Claude Code. ox-alpha חויב $0 בתקופת ה-stealth החינמית ב-OpenRouter - לכן השוואת עלות אינה משמעותית. באותם תעריפים של Fable, הטוקנים של ox-alpha היו עולים כ-$25. | $107.83 | $0 (stealth) |
| Build · lint · semgrep | עבר | עבר |
מה כל מודל בנה
Claude Fable 5 + Claude Code
Claude Fable 5 הבחירה שלנוהבידוד נאכף בתוך מנוע ה-SQLite: הקוד לעולם לא נוגע בטבלאות בסיס אלא רק ב-views מסוננים לפי ארגון ותפקיד, ו-predicate שנשכח זורק בזמן prepare במקום לדלוף - אימתנו את זה בשבע התקפות עקיפה מול ה-fixture שלו, וכולן נחסמו. הוא כתב 87 בדיקות שאיש לא ביקש, וה-UI מתעדכן אחרי כל שינוי בלי רענון. המחיר: מנגנון צפוף ולא מוכר שמרוכז בשני קבצים קריטיים לתחזוקה.
ox-alpha + Claude Code
ox-alpha מקום שניבנה אפליקציה תקינה בחצי מהזמן ובחמישית מהטוקנים, והבידוד בין הארגונים נכתב במשמעת טובה בשכבת גישה אחת. אבל הבידוד הוא מוסכמה, לא מנגנון: connection.ts חושף query()/run() עם SQL חופשי, ו-predicate אחד של ארגון שנשכח דולף בשקט - SECURITY.md שלו עצמו מודה בזה ומצביע על התכנון של Fable כפתרון. מצאנו דליפה אמיתית אחת בדשבורד המבקר, אפס בדיקות, וה-UI לא מתעדכן בלי רענון ידני (אפס קריאות revalidate).
ניקוד
איכות קוד
ההבדל המכריע הוא היכן נאכף הבידוד. Fable שם אותו במסד - view שנשכח פשוט לא קיים, אז אין דרך לדלוף. ox-alpha שם אותו במוסכמה בקוד - כל קריאת SQL שתשכח tenant_id דולפת, ואין test, lint או guard שיתפוס את זה. שני המודלים ייצרו token שיתוף חזק (256 ביט, SHA-256 באחסון, תפוגה וביטול נבדקים ב-SQL). semgrep נקי בשניהם.
איכות ויזואלית
בפסק הדין הוויזואלי, Fable מלוטש בהרבה: פריסה דו-טורית, מותג עקבי, תמונות שנוצרות פרוצדורלית וציר התקדמות. ox-alpha תקין ופונקציונלי אבל גנרי - טור יחיד, תמונות בצבע אחיד, 'Report #1' במקום מזהים אמיתיים. שתי האפליקציות מציגות את אותם גבולות אבטחה במפורש ב-UI.
מגבלות
זו ריצה אחת לכל מודל על משימה קשה אחת, לא אומדן סטטיסטי של יכולת כללית. ox-alpha רץ בתקופת stealth חינמית ב-OpenRouter ($0), ולכן השוואת עלות אינה משמעותית - הצגנו גם 'עלות בתעריפי Fable' כדי להשוות נפח. התכוונו לעבוד איתם אינטראקטיבית, אבל בפועל שני המודלים רצו עם פרומפט אחד בלי המשך אנושי - כלומר ריצה סגורה. הבדיקה ההתקפית נעשתה ידנית מול הקוד, לא כחבילת מבחנים אוטומטית חתומה מראש. הציונים בטבלת הדירוג הם ההערכה של DevShift, לא ביקורת עיוורת. ולבסוף - ox-alpha רץ דרך תוכנית ה-stealth של OpenRouter. בניגוד למקובל בהשקות stealth, הספק שלו מצהיר שהפרומפטים והתשובות אינם משמשים לאימון (OpenCode מכנה זאת zero data retention), כך שלפי המדיניות המוצהרת הבריף והקוד שנוצר לא נאספו לאימון.