Skip to content

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. Micro-Context Injection (Parsing the Blueprint)

When you run carrot generate, the Carotene compiler first acts like a traditional language compiler. It parses your .carrot files into an Abstract Syntax Tree (AST).

Unlike tools like Claude Code or Cursor, Carotene never sends your entire codebase to the LLM. Instead, when the compiler discovers a "Code Hole"—your @(...) Generative Operator—it builds a highly focused Context Matrix for that specific hole. It gathers:

  • The exact types of the input arguments.
  • The expected return type of the function.
  • The explicit mathematical ensures rules.
  • The database schemas (models) the function is allowed to read or update.
  • The external integrations it is allowed to call.
  • The specific test asserts statements 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:

dart
function CalculateDiscount(userId: String) -> Float {
  ensures rule.ValidDiscount(result)
  implements {
    discount = @("Calculate a tiered volume discount based on past orders")
    return discount
  }
}

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: string

  • Allowed DB Access: store.Order (fields: id, userId, total, date)

  • Return Type Expected: number

  • Mathematical Constraints (ensures): The output MUST satisfy result >= 0.0 and result <= 100.0.

    Task: 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.

Notice how the compiler automatically unpacks the mathematical limits from the referenced rule.ValidDiscount block and injects them as hard system constraints before the AI writes line 1. By hyper-focusing the context, Carotene completely eliminates the risk of the AI hallucinating external libraries, guessing incorrect database table names, writing boilerplate you didn't ask for, or returning out-of-bounds values.

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:

  1. The Hash Check: When you run carrot build or carrot dev, the compiler checks the cryptographic hash of every src/ file against a local .carotene/cache.
  2. 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.
  3. 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)
  4. Auto-Sync: If you choose "Set as @manual", the CLI automatically injects the @manual decorator above that function in your .carrot file. 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.

typescript
// 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.