Generating Code
In Carotene, you do not write business logic; you write architectural boundaries and generative intent. But how does the framework actually translate your intent into deterministic, production-ready code?
Carotene does not just blindly send your entire file to an LLM and cross its fingers. The generation engine is a highly structured compiler pipeline that treats the AI as a pure logic synthesizer, strictly bounded by the rules of your architecture.
Here is the exact lifecycle of how Carotene generates your code.
1. Context Extraction (Parsing the Blueprint)
When you run carrot build or carrot dev, the Carotene compiler first acts like a traditional language compiler. It parses your .carrot files into an Abstract Syntax Tree (AST).
During this phase, the compiler discovers a "Code Hole"—your @(...) Generative Operator. Before it talks to the AI, the compiler builds a rigid Context Matrix for that specific hole. It gathers:
- The exact types of the input arguments.
- The expected return type of the function.
- The database schemas (
models) the function is allowed toreadorupdate. - The external
integrationsit is allowed tocall. - The specific test
assertsstatements that apply to this function.
2. The Prompt Assembly
Carotene does not use generic "Write me a function" prompts. It uses the Context Matrix to construct a mathematically precise system prompt.
If you wrote:
discount = @("Calculate a tiered volume discount based on past orders")Carotene builds a prompt that looks like this under the hood:
System: You are a deterministic logic synthesizer. Your output will be executed in a zero-trust sandbox.
Language: TypeScript
Context:
Input:
userId: stringAllowed DB Access:
store.Order (fields: id, userId, total, date)Return Type Expected:
numberTask: Write the internal logic to satisfy the following intent: "Calculate a tiered volume discount based on past orders".
Constraints: Do not write network code. Do not write SQL connections. Return ONLY the raw mathematical logic to calculate the discount.
By hyper-focusing the context, Carotene completely eliminates the risk of the AI hallucinating external libraries, guessing incorrect database table names, or writing boilerplate you didn't ask for.
The Generation Lifecycle & Code Ownership
Carotene bridges the gap between AI generation and manual coding using a strict Two-Way Sync model. The framework generates read-only TypeScript/Go interfaces in the .generated/ folder, and implementation stubs in the src/ folder.
By default, the Carotene compiler assumes AI Ownership over the src/ stubs. When the .carrot contract changes, the compiler invokes the LLM and overwrites the implementation stub to match the new requirements.
Smart Sync & Manual Overrides
You are never locked out of your own code. If the AI is struggling to implement a specific function, or you have a proprietary algorithm you want to write by hand, you can simply open the generated src/ file and write the logic yourself.
Carotene strictly protects human code using Hash-Detection:
- The Hash Check: When you run
carrot buildorcarrot dev, the compiler checks the cryptographic hash of everysrc/file against a local.carotene/cache. - Detection: If it detects manual edits in a file that is currently owned by the AI, it immediately halts generation for that specific file to protect your work.
- The CLI Intercept: The CLI issues an interactive prompt in your terminal:bash
[!] Manual edits detected in AI-managed files. The following files have been modified outside of the Carotene compiler: - src/auth/LegacySSO.ts ? How would you like to proceed? (Use arrow keys) ❯ Set as @manual in contract (Preserve edits and ignore in future) Overwrite file (Discard manual edits and regenerate via AI) Skip for now (Do not regenerate this run) - Auto-Sync: If you choose "Set as
@manual", the CLI automatically injects the@manualdecorator above that function in your.carrotfile. The compiler will now permanently ignore this file during AI generation.
Note: Even when a file is @manual, Carotene continues to strictly enforce its inputs and outputs. If the contract signature changes later, your native compiler (TypeScript/Go) will flag the exact lines in your manual code that need to be updated to satisfy the new .generated/ interface.
The Implementation Stub
When you open your generated src/ files, you will see exactly how Carotene enforces the Zero-Trust boundary. The function signature includes a strictly-typed Context object.
// apps/order-service/src/commerce/ApplyDiscount.ts
import { ApplyDiscountContext } from '../../.generated/carotene-runtime';
// The Context object contains ONLY the permitted database and network methods
export async function ApplyDiscount(userId: string, ctx: ApplyDiscountContext): Promise<number> {
// --- CAROTENE GENERATED: "Calculate a tiered volume discount based on past orders" ---
// The AI uses the strictly-bound context object
const pastOrders = await ctx.db.Order.readMany({ userId });
const totalVolume = pastOrders.reduce((sum, order) => sum + order.total, 0);
if (totalVolume > 5000) return 0.20;
if (totalVolume > 1000) return 0.10;
return 0.00;
// --------------------------------------------------------------------------------------
}Best Practices for the @(...) Operator
Because the AI's output is directly tied to the quality of your prompt, treating the Generative Operator with care is essential.
- Keep it scoped: Do not ask an operator to "Build the whole checkout flow." Break it down. Use deterministic code to check the user's role, and use
@(...)just to calculate the complex tax math. - Be mathematically explicit: Instead of saying
@("Make the text sound nice"), say@("Format the string as Title Case and append the localized currency symbol"). - Let the architecture do the heavy lifting: You never need to tell the AI how to fetch data or save data. Carotene's variables already handle that. Use the operator strictly for data transformation.