Generated by reverse-engineering every file in the codebase. This is your weapons-grade cheat sheet. Study it line by line.
- Project Deep Understanding
- Architecture & Request Flow
- File-by-File Code Walkthrough
- Backend Questions
- AI/LLM Questions
- Docker Questions
- TypeScript Questions
- System Design & Scalability
- Production Readiness
- Behavioral Questions
- Deep Dive ("Why?" Chain)
- Hidden Gotcha Questions (Only the Builder Would Know)
- Final 100-Question Revision Sheet
- Cheat Sheet & Architecture Summary
- Red Flags & Things NOT to Say
Best answer:
EvolveOS is a sovereign AI agent civilization — a monorepo that implements an autonomous multi-agent system where AI agents live, work, evolve, and mentor each other.
The core idea: Instead of a single AI assistant, you have a civilization of specialized AI agents (Architects, Coders, Reviewers, Customs agents) that operate inside an isolated "world." Humans submit tasks through a border API, and the system autonomously assigns, executes, and learns from those tasks.
The key innovations are:
- Auction-based dispatch — Agents bid on tasks via LLM self-evaluation; the best-fit agent becomes the lead, the worst-fit becomes the shadow/mentee
- Genesis Protocol — When agents become highly reputable (90+), they breed new specialized agents by combining their system prompts with LLM-generated mutations
- Mentorship Pipeline — Every task has a senior agent execute it while a junior agent observes, synthesizes the architectural pattern, and saves it to vector memory
- Judgement Loop — An agentic while-loop where the LLM calls tools (terminal commands, file writes), receives results, and iterates until the task is complete
The architecture: Border API (Express) → BullMQ Queue (Redis) → World Engine Worker (Gemini 2.5 Flash) → Docker sandbox for execution, with Supabase PostgreSQL for persistence and Google text-embedding-004 for vector memory.
Best answer: The Genesis Protocol. It's a self-evolving AI civilization — agents with high reputation literally breed new agents by combining their system prompts. The child gets a mutated focus area. This means the system's capabilities grow autonomously over time without human intervention. No one manually configures new agents; the civilization evolves its own specializations.
Best answer: Traditional AI assistants are monolithic — one model, one prompt, one context window. EvolveOS solves the problem of:
- Context window limitations — Different agents handle different domains
- Specialization — Agents evolve to become domain experts
- Knowledge compounding — The mentorship pipeline ensures institutional knowledge accumulates
- Autonomous operation — The system self-organizes without human orchestration
- Graceful degradation — Circuit breakers at every level (embedding, LLM, execution)
Best answer: Designing the Judgement Loop (the agentic execution pattern in queue.ts). The hardest part was making the LLM decide when to stop using tools and return a final answer. The while-loop pattern while (functionCalls && functionCalls.length > 0) is simple but has edge cases: infinite loops if the LLM keeps calling tools, handling tool failures gracefully without crashing the entire job, and deciding when to break out. I also had to implement circuit breakers at every LLM call site because the Gemini API has rate limits and occasional failures — a single unhandled rejection would crash the worker process.
Best answer: The agent state deadlock. If the worker process crashed mid-execution, agents would be permanently locked in WORKING state in Supabase. The fix was the crash recovery in bootWorld() — every time any process starts, it resets all agent states to IDLE (state: 'IDLE'). This is a form of "last-writer-wins" crash recovery. The tradeoff is that a genuinely in-progress task loses its lock, but in a single-process architecture this is acceptable.
Best answer: Two things:
- The memory system — I'd use Supabase pgvector from day one instead of the in-memory array. Currently memories reset on restart, which defeats the purpose of "compounding knowledge."
- I'd add a result retrieval endpoint — right now tasks can be submitted but there's no way to poll for results.
Q: Why top-level await bootWorld() at line 23?
A: This is ESM ("type": "module" in package.json), so top-level await is valid. I need agents loaded BEFORE the worker starts accepting jobs, otherwise the first job would see an empty registry and fail with "Worker Starvation." The alternative would be wrapping in an async IIFE, but top-level await is cleaner in ESM.
Q: Why const for the worker at line 28 but you don't export it?
A: This is a standalone process, not a library. The worker needs to exist in this process only. No other module imports it. Using const makes it clear this is a one-time initialization.
Q: Line 80: while (functionCalls && functionCalls.length > 0) — what if the LLM returns 100 tool calls in sequence?
A: This is the infinite loop risk. Currently there's no iteration limit. In production I'd add a MAX_ITERATIONS counter (e.g., 20) and break out. The Gemini model has a context window limit which naturally caps this, but it's still a real risk.
Q: Line 81: const call = functionCalls[0]!; — why the non-null assertion?
A: Because we already checked functionCalls.length > 0 on line 80, so [0] is guaranteed to exist. TypeScript's strict mode doesn't track this correlation, so ! suppresses the error. Alternative: use functionCalls[0] with a null check, but that's redundant.
Q: Why chat.sendMessage() instead of model.generateContent() for the lead agent (line 74)?
A: startChat() creates a stateful session that remembers conversation history. The Judgement Loop is multi-turn: tool call → result → tool call → result. generateContent() is stateless — each call is independent. The chat session maintains context across iterations.
Q: Line 62: Why inject memories into systemInstruction instead of as a user message?
A: System instructions are treated as authoritative context by the LLM. Putting memories there makes them "background knowledge" rather than "something the user said." This prevents the LLM from confusing past lessons with the current task. The tradeoff: system instructions count against the context window.
Q: Line 103-104: The circuit breaker returns a mock string. Isn't that a problem?
A: Yes, it's a known compromise. The options were: (a) crash and fail the entire job, (b) retry with exponential backoff (but BullMQ has its own retry logic), or (c) return a recognizable mock. I chose (c) because: the task still completes (no agent lock leak), the mock is clearly marked so the human knows it's not real, and BullMQ's retry mechanism handles transient failures separately. In production, I'd combine this with BullMQ's attempts config.
Q: Why does the shadow agent only get 2 sentences (line 119)? A: Deliberate constraint. The shadow's job is to distill the essential pattern, not re-explain everything. 2 sentences forces compression — similar to the "explain like I'm 5" technique. Longer summaries would waste tokens and dilute the signal. The saved memory is then retrieved by future agents via cosine similarity.
Q: Line 154: as TaskPacket — why the type assertion?
A: The return object is spread from task (which is a TaskPacket) and then overridden fields are added. TypeScript can't infer the full type from spread + override, so as TaskPacket is needed. This is safe because we're adding fields that exist on TaskPacket.
Q: Why setInterval instead of a proper cron scheduler?
A: For a prototype, setInterval is sufficient and has zero dependencies. The 5-second tick is not a cron schedule — it's a heartbeat. In production I'd use BullMQ's repeatable jobs or a proper scheduler like node-cron. The risk with setInterval: if tick() takes longer than 5s (which it can, due to LLM calls), overlapping executions occur. I mitigate this by locking agents before long operations, so overlapping ticks won't double-process.
Q: Line 31: Math.random() — 50% chance of research. Why random?
A: Simulates organic curiosity behavior. Not every agent researches every tick. This creates emergent patterns — some agents are "busier" than others. It also prevents API abuse (too many embedding/LLM calls). The 0.5 threshold is arbitrary; in production you'd make it configurable or reputation-based.
Q: Line 39: setTimeout(resolve, 2000) — why simulate?
A: Intrinsic research is a placeholder feature. The 2-second delay simulates work happening. In production, this would call the LLM to actually research something, or query external APIs. Right now it's scaffolding for the feature.
Q: Line 50: Why filter for reputation >= 90 AND state === 'IDLE'?
A: Only top-performing agents that aren't currently busy should breed. reputation >= 90 ensures only the most successful agents pass on their "DNA." state === 'IDLE' prevents a working agent from being selected (race condition avoidance). The combination ensures quality breeding candidates.
Q: Line 55-56: topAgents[0]! and topAgents[1]! — is this safe?
A: Yes, because line 52 checks topAgents.length >= 2, guaranteeing both indices exist. Same pattern as the queue.ts non-null assertions.
Q: Line 91: agent-${Date.now()} — any collision risk?
A: Yes. If two Genesis Protocol events fire in the same millisecond (unlikely with a 5s tick but theoretically possible), both children would get the same ID. In production, use UUIDs. The current implementation is acceptable for a prototype with MAX_POPULATION = 10.
Q: Line 93: Why hardcode name: 'Gen-2 Architect'?
A: This is a simplification. All children get the same name. In production, the child name should be dynamically generated based on the mutation (e.g., "Gen-2 Security Specialist"). The name is less important than the system prompt, which IS unique.
Q: Lines 106-107: Parents lose 10 reputation. Why? A: This creates evolutionary pressure. Without it, top agents would keep breeding indefinitely. The -10 cost means: (a) parents must earn back reputation through successful tasks, (b) it prevents one pair of agents from dominating, (c) it creates a natural ebb and flow in the population.
Q: Why use LLM to score agent capability instead of a simple heuristic? A: Heuristics (like domain keyword matching) can't understand nuance. The LLM reads the agent's system prompt AND the task intent and understands whether the agent can actually help. For example, two "CODER" agents might have different specializations — one might be great at "build a REST API" but terrible at "write a Dockerfile." The LLM captures this nuance.
Q: Line 12: responseMimeType: 'application/json' — what does this do?
A: This is Gemini's structured output feature. It forces the model to return valid JSON matching the schema. Without this, the model might return JSON wrapped in markdown code fences, or with conversational text around it. With responseMimeType, we get clean JSON that can be parsed directly.
Q: Line 50: Why sort descending?
A: Highest bidder wins. b.score - a.score puts the most confident agent first. This is the lead agent.
Q: Lines 15-16: SENIOR_THRESHOLD and JUNIOR_THRESHOLD are defined but unused.
A: This is technical debt. Originally planned to filter out agents with scores below JUNIOR_THRESHOLD from being shadow agents, and only allow SENIOR_THRESHOLD agents to be leads. Currently unused because even a low-scoring agent benefits from observing (the mentorship pipeline is about learning, not optimal execution).
Q: Why parallelize bids with Promise.all (line 47)?
A: Each bid requires an LLM call (~1-3 seconds). If we serialized them, 5 agents would take 5-15 seconds. Promise.all runs them concurrently, so total time is ~1-3 seconds. This is critical for latency. The tradeoff: more concurrent API calls = higher rate limit pressure.
Q: Line 59: The winner is locked AFTER sorting. What if the lock fails?
A: Good catch. If lockAgent fails (Supabase is down), the agent stays IDLE in RAM but the function returns it as assigned. This is a race condition. In production, I'd use optimistic locking or transactions. Currently the RAM state is the source of truth and the Supabase write is best-effort.
Q: Line 56: args.command.replace(/"/g, '\\"') — is this safe against injection?
A: No. This is the biggest security concern in the project. Escaping double quotes is NOT sufficient for safe shell execution. An attacker could craft a command like: "; rm -rf /; echo " which would break out of the quotes. The mitigation: commands run INSIDE the axiom-workspace Docker container, not on the host. So even if an agent runs rm -rf /, it only destroys the disposable container. But this is still not production-grade security.
Q: Line 57: Why docker exec instead of running directly?
A: Sandboxing. The agent's commands run inside an isolated container. The container has access to the /workspace directory (which is the sandbox/ folder mounted in). If the agent does something destructive, it only affects the container. Production alternatives: gVisor, Firecracker microVMs, or WASM-based sandboxes.
Q: Line 59: execSync — why synchronous?
A: This is a design choice. The Judgement Loop is sequential anyway (each tool call depends on the previous result). Using async (exec) would add complexity without benefit. The downside: it blocks the Node.js event loop during command execution. In production with long-running commands, this would be a problem. For short commands (ls, cat, npm install), it's fine.
Q: Line 64: Path sanitization — replace('/workspace/', '').replace(/^(\.\/|\/)/, '')
A: This strips the /workspace/ prefix and leading ./ or / to normalize the path. The goal: all files go to sandbox/{cleanPath}. But this is naive — it doesn't prevent path traversal like ../../etc/passwd. In production, use path.resolve + verify the resolved path starts with the sandbox directory.
Q: Line 82: The error handling casts to a custom type as { message?, stdout?, stderr? }. Why?
A: execSync throws errors that have stdout and stderr properties, but TypeScript doesn't know about them (they're not on the standard Error type). The cast extracts them for better debugging output. This is a pragmatic workaround for a type system limitation.
Q: Line 39-44: The fallback hash algorithm. Walk through it. A: When the Google embedding API fails, we create a deterministic 768-D vector from the text:
- Create a zero-filled array of length 768
- For each character at position
i:- Add
charCodeto positioni % 768 - Add
charCode * 0.5to position(i * 7) % 768
- Add
- Normalize to unit length
The two different modulo operations (i % 768 and (i*7) % 768) spread the signal across different dimensions. The 0.5 factor prevents double-accumulation at positions where both formulas collide. Normalization ensures cosine similarity works correctly.
Q: Why 768 dimensions specifically?
A: That's the output dimension of Google's text-embedding-004 model. If I used a different dimension for fallback vectors, they wouldn't be comparable with real embeddings in the same store.
Q: Line 47-50: Magnitude zero check. Why?
A: If the input text is empty or all characters have charCode 0 (impossible but defensive), the vector would be all zeros. Division by zero would produce NaN. The check if (magnitude === 0) return vector prevents this.
Q: Line 80-97: Cosine similarity implementation. Why not use a library? A: Cosine similarity is ~15 lines of math. Adding a dependency for this is overkill. The implementation handles: null vectors (returns 0), different-length vectors (uses minimum length), zero-magnitude vectors (returns 0). This is a standard, well-understood algorithm.
Q: Line 122: Threshold of 0.6. How did you choose this? A: 0.6 means "moderately similar." 1.0 = identical, 0.0 = orthogonal, -1.0 = opposite. 0.6 is a reasonable starting point for text embeddings. Too low (0.3) returns irrelevant results; too high (0.8) returns almost nothing. In production, this should be configurable per use case.
Q: Line 105: topK: number = 5. Why 5?
A: The context window of the LLM is finite. Injecting 5 relevant memories is enough context without overwhelming the prompt. Too many memories increase latency (more tokens), cost (more input tokens), and can confuse the LLM with conflicting information.
Q: Line 6: export let AgentRegistry — why let instead of const?
A: bootWorld() reassigns the entire array (AgentRegistry = data.map(...)) on line 23. If it were const, reassignment would throw. Alternative: use .length = 0; array.push(...) to mutate in place, but that's less readable.
Q: Why maintain both RAM and Supabase? A: RAM for speed — agent lookups happen on every task dispatch. Database queries would add latency. Supabase for persistence — if the process restarts, agents are recovered from the database. This is the CQRS pattern at a micro scale: read from RAM (fast), write to both RAM and DB (durable).
Q: Line 29: state: 'IDLE' on boot. Why reset?
A: Crash recovery. If an agent was WORKING when the process died, it would be permanently stuck in Supabase as WORKING. Resetting to IDLE on boot ensures no deadlocks. The tradeoff: an actually-in-progress task loses its lock, but this is acceptable because the worker process dying means the task was abandoned anyway.
Q: Line 47: AgentRegistry.push(agent) — is this safe?
A: In a single-process application, yes. In a multi-process setup, this would create a race condition — two processes loading the registry simultaneously could both push, resulting in duplicates. For production, use a database mutex or leader election.
Q: Lines 66-74: Why update both RAM and Supabase for lock/unlock? A: RAM is the source of truth for the current process. Supabase is the source of truth for other processes and persistence. If only RAM is updated, a crash loses the state. If only Supabase is updated, the in-memory registry is stale and other agents could be assigned the same task.
Q: Line 25: transport: ws as any — why the as any cast?
A: The Supabase client expects a WebSocket constructor, but the ws library's type signature doesn't exactly match what Supabase expects (browser WebSocket vs Node.js WebSocket). The as any suppresses the type mismatch. This is a known Supabase/Node.js compatibility issue. In Supabase v2+, this is handled differently.
Q: Line 22: persistSession: false. Why?
A: This is a backend service, not a user-facing app. Sessions (auth tokens) don't need to persist across restarts. The service uses a single API key (anon key), not per-user sessions. Persisting sessions would create unnecessary files on disk.
Q: Line 30: The outer as any cast. What's happening?
A: The createClient options type doesn't have fields for realtime.transport and global.WebSocket. These are undocumented Supabase options needed for Node.js WebSocket support. The outer as any bypasses the type checker entirely. This is pragmatic but fragile — Supabase could change internals and break this silently.
Q: Line 49: domain: 'CODER' as AgentDomain. Why cast?
A: The string 'CODER' is a literal type. TypeScript infers it as 'CODER' (not AgentDomain). The as AgentDomain assertion tells TypeScript "this value is one of the AgentDomain union members." Without it, TypeScript would error because the domain field expects AgentDomain which is the full union.
Q: Why Express 5 instead of FastAPI or Hono?
A: Personal familiarity. Express is the most battle-tested Node.js framework. Express 5 adds async error handling natively (no need for express-async-errors). For a prototype, Express's simplicity wins over Fastify's performance or Hono's edge-compatibility.
Q: Line 56: taskQueue.add('process-task', taskPacket) — why a job name?
A: BullMQ job names are for logging and debugging. The name 'process-task' appears in Redis and BullMQ dashboards. It's metadata, not routing logic. The worker listens to the queue name 'axiom-tasks' regardless of the job name.
Q: No result retrieval endpoint. How do you get results?
A: This is a known gap. The trackingId is returned but there's no GET /api/customs/out/:id endpoint. In production, this would query Redis/BullMQ for job status and return the result. For now, results are logged to the console.
-
Q: Why a single POST endpoint? A: Minimal surface area for security. One entry point = one place to rate-limit, authenticate, and monitor. The entire "API" is just a gateway to the job queue.
-
Q: Why
intentas the only required field? A: Natural language is the universal interface. The human describes what they want in plain English. The LLM figures out how to accomplish it. This is the "prompt-as-input" pattern. -
Q: What if two tasks arrive simultaneously? A: Each gets a unique UUID and is independently enqueued. BullMQ handles concurrent job processing with configurable concurrency. The dispatcher locks agents, so two tasks can't use the same agent simultaneously.
- Q: Your API has no authentication. Isn't that a problem? A: Yes, this is a prototype limitation. In production: API key authentication, JWT tokens, or OAuth. For now, it's localhost-only. The border metaphor extends to this — the "border" should have a gate.
- Q: How would you add rate limiting?
A: Express middleware like
express-rate-limit. Key by IP or API key. Limit to N requests per minute. Also add BullMQ rate limiting withlimiter: { max: 10, duration: 60000 }on the queue.
- Q: Why return 500 with a generic message on error? A: Never expose internal errors to the client. The error message might contain stack traces, database details, or API keys. Log the detailed error server-side, return a generic message to the client.
- Q: Where would you add caching? A: Redis (already in the stack) for: agent registry (avoid Supabase hits), bid evaluations (cache LLM responses for repeated tasks), and results (cache completed task outputs).
- Q: Why BullMQ instead of a simple in-process queue? A: BullMQ persists jobs to Redis. If the worker crashes, jobs are recovered. An in-process queue (like an array) loses all pending jobs on crash. BullMQ also provides: retry logic, rate limiting, delayed jobs, job priorities, and a monitoring UI (Bull Board).
- Q: The clock uses
setInterval+async function tick(). Any issues? A: Yes.setIntervaldoesn't wait for the async function to complete. Iftick()takes 10 seconds, the next interval fires before the first finishes, causing overlapping executions. Fix: usesetTimeoutrecursively with a check for completion, or use a mutex/semaphore.
-
Q: Your agents table uses
textfor ID andtextfor state/domain. Why not enums? A: PostgreSQL supports custom enums, but text is more flexible for a prototype. Adding a new state or domain requires an ALTER TABLE with enums. With text, it's just a code change. Tradeoff: no database-level constraint on valid values. -
Q: Why no indexes on the agents table? A: The table is loaded entirely into RAM on boot. Indexes don't help for full-table scans. In production with 10,000+ agents, you'd index on
domainandstatefor filtered queries.
-
Q: Walk me through the lead agent's prompt structure. A: Three layers: (1) System instruction = agent's personality + memory context, (2) User message = "HUMAN TASK: {intent}", (3) Tool definitions =
runTerminalCommandandwriteLocalFile. The system instruction establishes identity, the user message provides the task, the tools provide capabilities. -
Q: Why does the shadow agent get a different prompt format? A: The shadow's task is meta-cognitive — it's not executing, it's learning. Its prompt says "observe the senior's output and distill the pattern." This is fundamentally different from the lead's execution prompt.
-
Q: How do you prevent hallucinated tool calls? A: We don't currently. The LLM decides what tools to call. If it hallucinates a tool that doesn't exist,
executeSyscallreturns an error string, which is sent back to the LLM as a tool response. The LLM then self-corrects. The Judgement Loop naturally handles this through the response → correction cycle.
-
Q: Why Gemini 2.5 Flash instead of GPT-4 or Claude? A: Cost-performance tradeoff. Gemini Flash is optimized for speed and cost while maintaining good tool-calling capabilities. For a system where LLM calls happen on every task + bid evaluation + mentorship, cost matters significantly. Also: Google's generous free tier for development.
-
Q: What happens when Gemini rate-limits you? A: Each LLM call site has a try/catch with a circuit breaker that returns mock output. The system degrades gracefully — tasks still complete but with placeholder results. In production, add retry with exponential backoff + circuit breaker pattern (like Polly.js).
-
Q: How do you manage token costs? A: Currently: (1) Memory context is injected into system instruction (bounded by topK=5), (2) Shadow agent summaries are capped at 2 sentences, (3) Bid evaluations use 1-sentence reasoning. In production: add token counting before each API call, truncate if approaching limits, cache repeated prompts.
-
Q: Why only 2 tools? A: Minimal viable toolset for an agent that writes code.
runTerminalCommandcovers everything (install packages, run scripts, check files).writeLocalFilehandles code generation. More tools = more LLM confusion and higher token cost. Additional tools should be added as specific needs arise. -
Q: What's the difference between Gemini Function Calling and OpenAI Function Calling? A: Conceptually identical — the LLM returns structured function calls instead of text. API differences: Gemini uses
FunctionDeclarationwithSchemaType, usesfunctionCalls()andfunctionResponsemessage format. OpenAI usestoolsparameter with JSON Schema, usestool_callsandtoolrole messages. The patterns are converging. -
Q: Could the LLM refuse to use tools? A: Yes. If the LLM decides it can answer the task without tools, it returns text directly. The while-loop checks
functionCalls.length > 0— if the first response has no function calls, it goes straight toleadOutput = result.response.text(). This is correct behavior.
- Q: Explain the difference between text-embedding-004 and text-embedding-ada-002. A: Both produce dense vector representations of text. text-embedding-004 (Google) produces 768-D vectors; ada-002 (OpenAI) produces 1536-D vectors. Performance is comparable on MTEB benchmarks. Key difference: you must use the SAME model for encoding queries and stored text, otherwise vectors are in different spaces and similarity search breaks.
-
Q: Your memory is in-memory, not persistent. How does this affect the system? A: Memories reset on restart. The "compounding knowledge" feature only works within a single process lifetime. Once the worker restarts, all shadow agent lessons are lost. This is the most critical missing feature for the project to fulfill its vision.
-
Q: How would you implement hybrid search (keyword + semantic)? A: Combine pgvector cosine similarity with PostgreSQL full-text search (
tsvector/tsquery). Use reciprocal rank fusion to merge results. This handles both semantic queries ("how to handle errors") and keyword queries ("express.js middleware").
-
Q: Explain your docker-compose.yml. A: Three services: Redis (job queue), PostgreSQL (agent persistence), and a workspace container (agent sandbox). The workspace mounts
./sandboxinto/workspaceso files written by the host process are accessible inside the container and vice versa. -
Q: Why
redis:alpineandpostgres:15-alpine? A: Alpine images are ~5MB vs ~100MB for standard images. They use musl libc instead of glibc, which is fine for Redis/PostgreSQL. Smaller images = faster pulls, less attack surface, less disk usage. -
Q: Why
tail -f /dev/nullas CMD? A: The workspace container needs to stay alive indefinitely. Commands are injected viadocker exec axiom-workspace sh -c "...". If the container exited after CMD, there would be nothing to exec into.tail -f /dev/nullis a common pattern for "keep alive" containers. -
Q: How would you Dockerize the world-engine and border-api? A: Multi-stage Dockerfile:
FROM node:20-slim AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-slim WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules CMD ["node", "dist/queue.js"]
Multi-stage: install deps + compile in builder stage, copy only dist + node_modules to runtime. Reduces image size by excluding devDependencies, source code, and build tools.
-
Q: How would you handle environment variables in production? A: Never bake .env files into images. Use: (a) Docker secrets for sensitive values, (b) Kubernetes ConfigMaps for non-sensitive config, (c)
docker run --env-file .envfor local dev, (d) CI/CD pipeline injection (GitHub Actions secrets → env vars). -
Q: What volumes do you need and why? A:
redis_data— persist job queue across container restarts.pg_data— persist agent registry across restarts../sandbox:/workspace— shared filesystem between host and agent workspace container. Without volumes, all data is lost ondocker compose down. -
Q: Your PostgreSQL credentials are hardcoded. Isn't this terrible? A: Yes, for production. For local dev, it's acceptable. In production: use Docker secrets, Kubernetes secrets, or a managed database (like Supabase or RDS) where credentials are injected via IAM.
-
Q: Why a separate
@axiom/typespackage? A: Shared types across the monorepo.TaskPacketis used by bothborder-apiandworld-engine.AgentEntityis used byworld-engineand will be used byjarvis-web. Without a shared package, you'd duplicate types or useany. -
Q: Why union types instead of enums for
TaskStatus,AgentState,AgentDomain? A: TypeScript union types are erased at compile time — zero runtime cost. Enums create actual JavaScript objects. Union types are also more tree-shakeable and work better with type narrowing. The tradeoff: no runtimeTaskStatus.PENDING_CUSTOMSreference, only string literals. -
Q:
context?: stringon TaskPacket. What's the?mean? A: Optional property. A TaskPacket can exist without context. This is the "Jarvi injection" field — context is only added by the Jarvis frontend, not by the border API. -
Q:
JobRecordis defined but unused. Why keep it? A: It's a forward-looking type. The planned architecture includes job tracking (linking tasks to execution records). Right now the system doesn't track this, but the type is ready for when the feature is implemented. -
Q:
ExecutionModeis unused too. Same reasoning? A: Yes. The concept of SOLO vs MENTORSHIP execution exists in the code (dispatcher always assigns a shadow when multiple agents are available), but theExecutionModetype isn't wired up to the actual logic yet.
-
Q: Why
"verbatimModuleSyntax": true? A: Enforces that import statements exactly match what's emitted in JavaScript. Preventsimport type Foo from 'bar'from being emitted as a value import. Ensures ESM/CJS compatibility. -
Q: Why
"isolatedModules": true? A: Required fortsx(the TS runner used in dev).tsxtranspiles files individually, not as a program. This flag ensures each file can be independently transpiled without cross-file type information. -
Q: Why
"module": "nodenext"for world-engine but"module": "esnext"would also work? A:nodenextresolves.jsextensions in imports (required for ESM in Node.js).esnextdoesn't enforce extension resolution. Withnodenext, TypeScript checks thatimport { foo } from './bar.js'actually resolves to a file. This catches runtime errors at compile time.
- Current architecture handles this fine. Single Redis, single PostgreSQL, single worker process.
- Bottleneck: LLM API rate limits (Gemini Flash has generous limits, but 100 concurrent tasks × bid evaluation × execution = lots of API calls).
- Fix: Add BullMQ concurrency limit (
concurrency: 5on the worker), add request queuing.
- Problem 1: Single worker process can't handle the throughput.
- Solution: Horizontal scaling — run N copies of the worker. BullMQ handles job distribution automatically (multiple workers on the same queue = load balancing).
- Problem 2: In-memory agent registry is duplicated across workers. Two workers could assign the same agent.
- Solution: Move agent state entirely to Redis (not Supabase) for sub-millisecond reads with atomic operations. Use Redis
WATCH/MULTIfor optimistic locking. - Problem 3: In-memory MemoryStore is per-worker. Worker A's memories are invisible to Worker B.
- Solution: Move to Supabase pgvector (already planned in the README SQL). All workers share the same vector store.
- Problem 4: LLM API costs.
- Solution: Cache bid evaluations (same task + same agents = same bid), cache embedding generation (same text = same embedding), implement token budgets per task.
- Architecture change: Microservices. Separate:
- API Gateway (rate limiting, auth, routing)
- Task Orchestrator (queue management)
- Agent Runtime (execution)
- Memory Service (vector search)
- Evolution Service (Genesis Protocol)
- Database: PostgreSQL with read replicas, connection pooling (PgBouncer), table partitioning on
created_at. - Queue: Redis Cluster with sharding.
- Agent state: Distributed lock (Redis Redlock or etcd) instead of in-memory boolean.
- LLM: Multiple provider fallback (Gemini → GPT-4 → Claude). Load balance across API keys. Implement prompt caching.
- CDN: Frontend assets via Cloudflare/CloudFront.
- Observability: Distributed tracing (OpenTelemetry), metrics (Prometheus), logs (ELK).
-
Q: How do you monitor the system? A: Currently:
console.logeverywhere. Production: structured logging (winston/pino), metrics (Prometheus), tracing (OpenTelemetry), dashboards (Grafana). Key metrics: task completion rate, average execution time, LLM API latency, agent utilization rate, memory search accuracy. -
Q: How do you handle secrets? A: Currently:
.envfiles (gitignored). Production: HashiCorp Vault, AWS Secrets Manager, or Kubernetes Secrets. Never hardcode. Rotate keys regularly. Use separate keys for dev/staging/prod. -
Q: How do you recover from failures? A: Agent crash recovery:
bootWorld()resets all states to IDLE. BullMQ retry: jobs automatically retry with exponential backoff. Circuit breakers: LLM failures return mock output instead of crashing. Missing: dead letter queue for permanently failed tasks, health checks, auto-restart (Dockerrestart: always). -
Q: CI/CD pipeline? A: Not implemented. Production pipeline: GitHub Actions → lint → typecheck → test → build → deploy. Docker images built and pushed to registry. Kubernetes deployment with rolling updates. Blue-green deployment for zero downtime.
-
Q: Testing strategy? A: Not implemented. Needed: Unit tests for
cosineSimilarity,executeSyscall,generateEmbedding. Integration tests for the BullMQ worker (mock Redis). E2E tests for the full request flow (submit task → verify completion). LLM mocking for deterministic tests. -
Q: Security concerns? A: Critical: (1) Command injection in
runTerminalCommand, (2) Path traversal inwriteLocalFile, (3) No authentication on border API, (4) Hardcoded DB credentials, (5) No rate limiting, (6)as anycasts bypass TypeScript safety, (7) Agent can execute arbitrary commands in the Docker container. Mitigations: WAF, input validation, least-privilege containers, API authentication, rate limiting.
-
Q: Why this tech stack? A: TypeScript for full-stack consistency. Node.js for async I/O (critical for LLM API calls). Express for simplicity. BullMQ for reliable job processing. Supabase for rapid database setup with built-in vector support. Gemini for cost-effective LLM. Docker for sandboxing. Each choice prioritizes development speed and learning over production optimization.
-
Q: What would you do differently? A: (1) Start with persistent memory (pgvector) instead of in-memory, (2) Add authentication from day one, (3) Write tests alongside code, (4) Use UUIDs everywhere instead of
Date.now(), (5) Add a results endpoint, (6) Implement the REPAIRING and OFFLINE states. -
Q: How did you debug issues? A: Extensive logging at every decision point (
[DISPATCHER],[LLM CORE],[SYSCALL],[CIRCUIT BREAKER]). Each log line has a tag indicating which module produced it. For LLM issues: inspect the actual prompts sent. For Docker issues: exec into the workspace container manually. For Redis issues: use Redis CLI. -
Q: What did you learn building this? A: (1) LLM tool-coding is non-deterministic — same prompt can produce different tool calls, (2) Circuit breakers are essential when depending on external APIs, (3) In-memory state is fast but fragile, (4) The agentic loop pattern (while + tool calls) is powerful but needs iteration limits, (5) Multi-process state management is hard — the RAM + DB dual-write pattern has many edge cases.
Start with any topic and keep asking "Why?" to reach implementation-level understanding:
- Why monorepo? → Shared types, single deploy, unified tooling
- Why npm workspaces? → Native npm, no extra tooling (vs Turborepo, Nx)
- Why two separate processes (clock + worker)? → Different responsibilities, different lifecycles. Clock is continuous, worker is event-driven.
- Why not one process? →
setIntervalin the clock would block the event loop, making the worker slow. Separation of concerns. - Why not microservices? → Too early. Monolith first, extract when you have a reason.
- Why Gemini Flash? → Fast, cheap, good tool-calling
- Why not GPT-4? → 10x more expensive per token
- Why function calling instead of text parsing? → Structured output is reliable. Text parsing breaks on formatting changes.
- Why a while-loop for the Judgement Loop? → Simple, explicit control flow. Alternative: state machine, but overkill for this use case.
- Why 2 tools only? → Minimum viable toolset. More tools = more confusion.
- Why embeddings? → Semantic search is better than keyword search for natural language queries
- Why cosine similarity? → Standard metric, handles vectors of different magnitudes, bounded output (-1 to 1)
- Why threshold 0.6? → Empirical starting point. Tune based on evaluation data.
- Why topK=5? → Balance between context richness and token cost
- Why in-memory? → Prototype simplicity. In production: pgvector.
- Why Docker? → Process isolation. Agent commands can't escape to host.
- Why
docker exec? → The container is already running.docker execinjects a new process into it. - Why not
docker run? → Would create a new container for each command. Slow startup (~200ms).docker execis ~10ms. - Why
tail -f /dev/null? → Keeps the container alive. Without it, the container exits immediately. - Why not gVisor? → Too complex for a prototype. gVisor adds ~200MB to the image and requires specific kernel features.
Every answer should trigger deeper questions. Here are the follow-up chains:
If you say "BullMQ for job queue": → Why not RabbitMQ? → RabbitMQ is more feature-rich but heavier. BullMQ is Redis-native and simpler. → Why not Kafka? → Kafka is for streaming, not job queues. Overkill for this use case. → What happens if Redis crashes? → Jobs in the queue are lost (unless AOF is enabled). Jobs being processed are retried. → How do you handle job priorities? → BullMQ supports priorities, but we don't use them yet.
If you say "Supabase for persistence": → Why not raw PostgreSQL? → Supabase gives you a REST API, auth, and realtime out of the box. → Why not MongoDB? → Relational data (agents have structured fields). PostgreSQL with pgvector gives us vector search too. → What's the connection pool size? → Default Supabase client settings. For production, configure PgBouncer. → How do you handle migrations? → Not implemented yet. SQL is in the README.
If you say "Circuit breakers for LLM failures": → What pattern is this? → Fallback pattern. Not a full circuit breaker (which tracks failure counts and opens/closes). It's a try/catch with mock output. → Why not a real circuit breaker? → Overkill for a prototype. A real circuit breaker (like opossum library) tracks state across calls. Here, each call is independent. → What's the recovery strategy? → Next task will try the real LLM again. No persistent "open" state.
If you say "Genesis Protocol breeds new agents": → What prevents infinite breeding? → MAX_POPULATION cap (10) and reputation cost (-10 per parent). → What if all agents have high reputation? → Population cap stops breeding. Agents must stay below 10 total. → What if the child is worse than parents? → The child starts with reputation 50. Bad agents don't breed (need 90+). Natural selection via reputation decay. → How does reputation increase? → Not implemented yet. Planned: successful task completion increases reputation.
SECTION 12 — HIDDEN GOTCHA QUESTIONS
These are tricky questions that test whether you actually built this:
-
Q: In
queue.tsline 80, the Judgement Loop only processesfunctionCalls[0]. What if the LLM returns multiple function calls in one response? A: You're right to notice this. Gemini can return multiple function calls in a single response. The current code only processes the first one (functionCalls[0]!). The remaining calls are silently dropped. This is a bug. Fix: loop through all calls, execute each, and send all responses back before checking for more calls. -
Q: In
dispatcher.ts, the bid evaluation makes parallel LLM calls. What's the maximum number of concurrent API calls? A: It equals the number of IDLE agents in the required domain. With MAX_POPULATION=10, that's at most 10 concurrent Gemini API calls. If Gemini's rate limit is 60 RPM, this could hit limits with multiple tasks. -
Q: Your
generateEmbeddingfallback uses character codes. What happens with Unicode characters with codes > 256? A:charCodeAtreturns values up to 65535 for Unicode BMP characters. The fallback handles this correctly — it just adds larger values to the vector positions. For emoji or supplementary plane characters,charCodeAtreturns NaN for surrogate pairs. I should usecodePointAtinstead. -
Q: In
writeLocalFile, you strip/workspace/from the path. What if the LLM sends../../../etc/passwd? A: The path sanitization doesn't prevent directory traversal.path.resolve(process.cwd(), 'sandbox', '../../etc/passwd')would resolve outside the sandbox. This is a real security vulnerability. Fix: verify that the resolved path starts with the sandbox directory. -
Q: The
bootWorldfunction is called by BOTH clock.ts and queue.ts. They're separate processes. Doesn't this cause issues? A: No — they're separate Node.js processes with separate memory spaces. Each loads its ownAgentRegistryfrom Supabase. The concern is concurrent modification: if clock spawns a child agent while queue is processing, the queue's in-memory registry won't see the new agent until restart. In practice this is fine because spawned agents are rare. -
Q: Your
lockAgentinregistry.tsupdates RAM first, then Supabase. What if the Supabase update fails? A: The agent is locked in RAM (so this process won't assign it again) but not in Supabase. If this process crashes and restarts,bootWorldresets the agent to IDLE, and another process could assign it. This is a minor inconsistency. In production, use database-first updates with RAM sync. -
Q: In
clock.ts, what iftick()is running and a BullMQ job arrives? Do they conflict? A: They can, if both try to lock the same agent simultaneously.lockAgentis not atomic — it does a RAM check, then a Supabase update. Between these two steps, another process could lock the same agent. Fix: use Redis distributed lock (Redlock) before locking an agent. -
Q: Your BullMQ worker has no
concurrencyoption. What's the default? A: Default is 1 — the worker processes one job at a time. This means tasks are serialized, not parallel. For higher throughput, setconcurrency: 5on the Worker constructor. -
Q: What happens if the border API and the world-engine have different Node.js versions? A: The
.nvmrcspecifies Node 20, but there's no enforcement. If border-api runs on Node 18 and world-engine on Node 20, they'd still work because they communicate via Redis (not in-memory). But subtle ESM resolution differences could cause issues. -
Q: The
Memoryinterface hasembedding: number[]but Gemini returnsnumber[]too. What if you switch embedding providers? A: You'd need to ensure the new provider produces 768-D vectors (or update the fallback hash to match). ThecosineSimilarityfunction works with any dimension, so the algorithm is provider-agnostic. The hardcoded768in the fallback is the coupling point.
- What is EvolveOS? — Sovereign AI agent civilization.
- What's the core innovation? — Genesis Protocol + Mentorship Pipeline.
- What problem does it solve? — Monolithic AI limitations.
- What's the architecture? — Border API → BullMQ → World Engine → Docker sandbox.
- What's the tech stack? — TypeScript, Express, BullMQ, Redis, Supabase, Gemini, Docker.
- What's the biggest challenge? — Designing the Judgement Loop with proper error handling.
- What's the biggest security risk? — Command injection in
runTerminalCommand. - What would you improve first? — Persistent memory + results endpoint.
- What's the most interesting algorithm? — Cosine similarity for vector search.
- How does the system learn? — Shadow agent observations → embedding → retrieval.
- Why two processes? — Different lifecycles (continuous vs event-driven).
- Why BullMQ? — Persistent job queue with retries.
- Why Supabase? — PostgreSQL + pgvector + managed hosting.
- Why Docker sandbox? — Process isolation for agent commands.
- Why monorepo? — Shared types, unified tooling.
- Why npm workspaces? — Native, no extra dependencies.
- Why Express 5? — Simplicity, async error handling.
- Why ESM? — Modern module system, tree-shaking.
- Why Redis? — BullMQ requires it, also usable for caching/locking.
- Why in-memory agent registry? — Microsecond reads for task dispatch.
- What does
POST /api/customs/indo? — Creates TaskPacket, enqueues to BullMQ. - What does
GET /healthdo? — Health check for load balancer/Docker probes. - How does BullMQ distribute jobs? — Single queue, single worker type.
- What happens if Redis crashes? — Pending jobs lost, running jobs retried.
- How do you prevent duplicate processing? — Agent locking (RAM + Supabase).
- What's the worker concurrency? — Default 1 (serial processing).
- How would you add authentication? — API key middleware.
- How would you add rate limiting? — Express middleware + BullMQ limiter.
- What's the error handling strategy? — Try/catch at every level, circuit breakers for LLM.
- How would you add logging? — Replace console.log with structured logger (pino).
- What database indexes do you need? — None currently (full table load to RAM).
- How would you add caching? — Redis cache layer for agent registry + embeddings.
- What's the concurrency model? — Single-threaded Node.js, async I/O.
- How do you handle graceful shutdown? — Not implemented. Need SIGTERM handler.
- What's the deployment strategy? — Docker Compose for local, Kubernetes for production.
- What LLM does the system use? — Gemini 2.5 Flash.
- What embedding model? — Google text-embedding-004 (768-D).
- How does the Judgement Loop work? — While-loop with function calls.
- What tools can the LLM call? —
runTerminalCommand,writeLocalFile. - How does the dispatcher assign tasks? — LLM-based auction scoring.
- How does the Genesis Protocol work? — LLM combines parent prompts into child.
- How does the mentorship pipeline work? — Shadow observes lead, synthesizes 2-sentence lesson.
- How does memory search work? — Embed query, cosine similarity, threshold filter.
- What's the memory threshold? — 0.6 (moderately similar).
- How many memories are retrieved? — Top 5.
- What's the circuit breaker strategy? — Mock output on API failure.
- How do you handle token limits? — Not currently (potential for context overflow).
- How does function calling differ from text parsing? — Structured JSON output vs regex extraction.
- What's the temperature setting? — Default (1.0 for Gemini). Not explicitly set.
- How would you add prompt caching? — Cache system instructions + memory context by agent ID.
- What's the cost per task? — ~4 LLM calls (bid eval × N agents + execution + mentorship).
- How do you handle hallucinated tool calls? — Error response → LLM self-correction loop.
- How would you evaluate system quality? — Task completion rate, memory retrieval relevance, agent evolution tracking.
- How would you add streaming? — Use
chat.sendMessageStream()instead ofchat.sendMessage(). - What's the max context window usage? — System instruction + memory context + task + tool responses. No bounds checking.
- What does the docker-compose run? — Redis, PostgreSQL, workspace container.
- Why Alpine images? — Small size (~5MB), less attack surface.
- What's the workspace container for? — Sandboxed agent command execution.
- Why
tail -f /dev/null? — Keep container alive fordocker exec. - How do you Dockerize the app? — Multi-stage build (builder + runtime).
- Why multi-stage builds? — Smaller runtime images, exclude dev dependencies.
- How do you handle env vars in Docker? —
.envfile for dev, secrets for prod. - What volumes are needed? —
redis_data,pg_data,./sandbox:/workspace. - How would you deploy to production? — Kubernetes with Helm charts.
- How do you handle secrets? — Docker secrets or Kubernetes secrets.
- Why TypeScript? — Type safety, IDE support, catch errors at compile time.
- What does
verbatimModuleSyntaxdo? — Enforces exact import syntax. - What does
isolatedModulesdo? — Enables single-file transpilation. - Why union types over enums? — Zero runtime cost, better type narrowing.
- What's the
?on TaskPacket.context? — Optional property. - Why
as anyin db.ts? — Bypass type mismatch for WebSocket transport. - What does
as AgentDomaindo in server.ts? — Narrows string literal to union type. - Why strict mode? — Catches null/undefined errors, enforces type safety.
- Why
"type": "module"in package.json? — Enable ESM imports. - How do you handle type sharing? —
@axiom/typespackage in the monorepo.
- How would you handle 10K users? — Horizontal worker scaling + Redis agent state.
- How would you handle 1M users? — Microservices + distributed locking + CDN.
- What's the biggest bottleneck? — LLM API latency and rate limits.
- How would you reduce latency? — Cache bids, parallelize tool calls, use faster model for simple tasks.
- How would you reduce cost? — Prompt caching, smaller models for bid eval, token budgets.
- What's the failure mode? — LLM failure → mock output. Redis failure → job loss. Supabase failure → RAM-only mode.
- How do you handle data consistency? — RAM is eventually consistent with Supabase. Not ACID.
- What's the availability target? — Not defined. Prototype priority is functionality over availability.
- How would you add load balancing? — Multiple worker instances on the same BullMQ queue.
- What's the disaster recovery? — Supabase backups for agents. Redis AOF for jobs. Memories are ephemeral.
- How do you monitor? — Console logs → structured logging → Prometheus metrics → Grafana dashboards.
- How do you test? — Not implemented. Need unit + integration + E2E tests.
- How do you do CI/CD? — Not implemented. GitHub Actions → lint → test → build → deploy.
- How do you handle secrets? —
.envfiles → Vault/K8s secrets → rotation. - What's the rollback strategy? — Not implemented. Need blue-green deployment.
- How do you handle database migrations? — Not implemented. Need migration tool (Drizzle/Prisma).
- How do you handle zero-downtime deploys? — Not implemented. Need rolling updates or blue-green.
- What's the SLA? — Not defined. Prototype priority is functionality.
- How do you handle data backup? — Supabase handles PostgreSQL backups. Redis snapshots.
- How do you handle disaster recovery? — Rebuild from Supabase backup + redeploy containers.
- Why did you choose this tech stack? — TypeScript consistency, Node.js async, Gemini cost.
- What's your biggest learning? — LLM non-determinism, circuit breaker importance.
- What would you do differently? — Persistent memory, authentication, tests from day one.
- How did you debug issues? — Tagged logging at every decision point.
- What's the next feature you'd build? — Result retrieval endpoint + persistent memory.
Human → POST /api/customs/in → Express (border-api)
↓
BullMQ Queue (Redis)
↓
┌───────────────┴───────────────┐
↓ ↓
clock.ts (heartbeat) queue.ts (worker)
- Intrinsic Research - Auction Dispatch
- Genesis Protocol - Judgement Loop
↓ - Mentorship
Supabase ↓
(agents table) Docker Sandbox
(axiom-workspace)
- TICK_INTERVAL: 5000ms (5 seconds)
- MAX_POPULATION: 10 agents
- SENIOR_THRESHOLD: 80.0 (unused)
- JUNIOR_THRESHOLD: 40.0 (unused)
- Memory threshold: 0.6
- Memory topK: 5
- Embedding dimensions: 768
- Shadow output: 2 sentences max
- Genesis reputation cost: -10 per parent
- Genesis reputation threshold: 90+
- Intrinsic research chance: 50% per tick
| File | Lines | Purpose |
|---|---|---|
queue.ts |
172 | Judgement Loop — the core execution engine |
clock.ts |
115 | Heartbeat + Genesis Protocol |
dispatcher.ts |
115 | Auction-based agent assignment |
tools.ts |
90 | Syscall execution (Docker sandbox) |
memory.ts |
126 | Vector embeddings + cosine similarity |
registry.ts |
90 | Agent state management (RAM + Supabase) |
db.ts |
32 | Supabase client initialization |
server.ts |
74 | Express REST API gateway |
axiom-types/index.ts |
61 | Shared type definitions |
- No result retrieval endpoint
- In-memory memory (not persistent)
- No authentication
- Command injection risk in
runTerminalCommand - Path traversal risk in
writeLocalFile - Only processes first function call in multi-call response
SENIOR_THRESHOLDandJUNIOR_THRESHOLDunusedJobRecordandExecutionModetypes unusedDate.now()for IDs (collision risk)setInterval+ async overlap risk- No iteration limit in Judgement Loop
execSyncblocks event loop- No graceful shutdown handler
- Hardcoded DB credentials
- No tests
-
❌ "I used AI to generate this code" — Even if you did, talk about YOUR decisions, YOUR debugging, YOUR architecture choices.
-
❌ "I copied this from a tutorial" — Even if inspired by one, explain what you changed and why.
-
❌ "I didn't think about security" — Say: "Security was a known compromise for the prototype. Here's what I'd do in production..."
-
❌ "TypeScript is just JavaScript with types" — Show depth: "TypeScript's structural typing, conditional types, and template literal types enable patterns impossible in JavaScript."
-
❌ "Redis is just a cache" — Redis is a data structure server. BullMQ uses it as a job queue with persistence, priorities, and retry logic.
-
❌ "Docker is for deployment" — Docker here is for sandboxing security, not deployment. The agent's commands run inside an isolated container.
-
❌ "I don't know why I chose X" — Have a reason for every choice. Even if the reason is "simplicity for a prototype."
-
❌ "My code has no bugs" — Lead with known issues. "I know about 15 specific issues I'd fix in production..." shows maturity.
-
❌ "The system is production-ready" — It's not. Be honest: "This is a prototype demonstrating the architecture. Production would need auth, tests, monitoring, and the memory persistence gap."
-
❌ "I don't use tests" — Say: "Tests aren't implemented yet. Here's what my test plan would look like..."
- Can't explain why a specific technology was chosen
- Can't walk through code line by line
- Doesn't know the security implications of
execSyncwith user input - Can't discuss tradeoffs of in-memory vs persistent state
- Doesn't know what
cosine similarityis or how it works - Can't explain the Judgement Loop's failure modes
- Doesn't know the difference between
execSyncand async execution - Can't discuss alternative architectures
- Doesn't know about rate limiting, circuit breakers, or graceful degradation
- Can't explain how the system scales (or fails to scale)
- Lead with tradeoffs: "I chose X because Y, knowing that Z is the tradeoff"
- Discuss failure modes: "When X fails, the system does Y because Z"
- Show awareness of production gaps: "For production, I'd add X, Y, Z"
- Connect architecture to business goals: "The Genesis Protocol exists because..."
- Discuss observability: "I'd monitor X metric to detect Y problem"
- Reference real-world patterns: "This is similar to the circuit breaker pattern described by..."
- Quantify decisions: "The 0.6 threshold balances precision and recall..."
- Show iteration: "I started with X, found it didn't work because Y, switched to Z"