Progressive Ownership

ShippedIntroduced in beta

Owner

@engineering

Last verified

2025-10-13

Sources of truth

  • doc: docs/ownership-manifest.md
  • test: tests/unit/ownership-manifest.test.ts
  • test: tests/unit/extended-merge.test.ts
The complete guide to Weldr's ownership system

---

Table of Contents

  • Overview
  • The Three Ownership Modes
  • The Nine Surfaces
  • Ownership Manifest
  • File Lease System
  • Extended Merge Engine
  • Contract Validation
  • Contract Adapters
  • CLI Tools
  • Implementation Details
  • Advanced Patterns
  • Troubleshooting
  • ---

    Overview

    Progressive Ownership is Weldr's core innovation that solves the vendor lock-in problem in AI code generation.

    The Problem

    Traditional AI code generators force binary choices:

  • Use 100% generated code → Locked in, can't customize
  • Fork and maintain manually → Lose AI assistance, no regeneration
  • The Solution

    Progressive Ownership gives you three modes per surface, allowing granular control:

  • Managed - Weldr owns it, regenerates freely
  • Extended - Shared ownership, mix generated + custom code
  • Ejected - You own it completely, Weldr leaves it untouched
  • Key Principles

  • Granular Control - Choose ownership mode per surface (db, api, ui, etc.)
  • Safe Regeneration - Custom code preserved across all regenerations
  • Contract Validation - TypeScript compiler ensures compatibility
  • No Lock-In - Eject any surface, export full code, run anywhere
  • ---

    The Three Ownership Modes

    Mode 1: Managed

    Definition: Weldr owns the code and regenerates it freely. When to use:
  • • Database schemas and migrations
  • • Authentication (standard login/signup)
  • • Basic CRUD operations
  • • Routing and layouts
  • • Standard infrastructure
  • Benefits:
  • • Skip boilerplate
  • • Auto-updates with best practices
  • • Zero maintenance burden
  • Example:
    # weldr.ownership.yaml
    surfaces:
      db: managed

    // drizzle/schema.ts
    /** @weldr surface: db mode: managed **/
    
    import { pgTable, uuid, text, timestamp } from 'drizzle-orm/pg-core';
    
    export const users = pgTable('users', {
      id: uuid('id').primaryKey().defaultRandom(),
      email: text('email').notNull().unique(),
      name: text('name').notNull(),
      createdAt: timestamp('created_at').defaultNow(),
      updatedAt: timestamp('updated_at').defaultNow()
    });

    Regeneration behavior:
  • • User: "Add avatar URL to users"
  • • Weldr: Adds avatarUrl: text('avatar_url') to schema
  • • Weldr: Generates migration
  • • Weldr: Updates all CRUD operations
  • No conflicts, no merge needed
  • ---

    Mode 2: Extended

    Definition: Shared ownership - generated code in @weldr blocks, custom code in @custom blocks. When to use:
  • • API endpoints that need custom logic
  • • UI components that need unique behavior
  • • Workflows with business-specific steps
  • • Any file where you want to ADD logic, not replace it
  • Benefits:
  • • Keep generated CRUD operations
  • • Add custom helpers/functions
  • • Safe regeneration (custom blocks preserved)
  • • No contract overhead
  • Example:
    surfaces:
      api:
        mode: extended

    // lib/server/actions/users.ts
    /** @weldr surface: api mode: extended **/
    
    // @weldr:begin crud-user
    export async function createUser(input: CreateUserInput) {
      // Weldr regenerates this block
      return await db.insert(users).values(input);
    }
    
    export async function getUser(id: string) {
      return await db.query.users.findFirst({
        where: eq(users.id, id)
      });
    }
    
    export async function updateUser(id: string, input: UpdateUserInput) {
      return await db.update(users)
        .set(input)
        .where(eq(users.id, id));
    }
    // @weldr:end crud-user
    
    // @custom:begin user-helpers
    export async function createUserWithWorkspace(input: CreateUserInput) {
    // YOUR code - this block stays untouched by Weldr
      const user = await createUser(input);
    
      // Create default workspace
      await db.insert(workspaces).values({
        userId: user.id,
        name: `${user.name}'s Workspace`,
        isDefault: true
      });
    
      // Send welcome email
      await sendEmail({
        to: user.email,
        template: 'welcome',
        data: { name: user.name }
      });
    
      return user;
    }
    
    export async function getUserWithWorkspaces(id: string) {
      return await db.query.users.findFirst({
        where: eq(users.id, id),
        with: { workspaces: true }
      });
    }
    // @custom:end user-helpers

    Regeneration behavior:
  • User: "Add password reset functionality"
  • Weldr: Regenerates @weldr:begin crud-user block
  • Weldr: Adds resetPassword function in @weldr block
  • Extended merge engine: Preserves @custom blocks
  • Result: New function added, custom code untouched
  • ---

    Mode 3: Ejected

    Definition: You own the code completely. Weldr validates contracts but skips regeneration. When to use:
  • • Complex algorithms (pricing, matching, recommendations)
  • • Third-party integrations (Stripe, Twilio, custom APIs)
  • • Core IP/competitive advantage
  • • Performance-critical code
  • • Compliance-critical code (HIPAA, PCI)
  • Benefits:
  • • Full control over implementation
  • • TypeScript contract validation
  • • Auto-generated adapters for integration
  • • Can update without breaking generated code
  • Example:
    surfaces:
      domain_logic:
        mode: ejected
        entrypoints:
          - src/domain/pricing.ts
    
    contracts:
      - id: domain.pricing
        surface: domain_logic
        module: src/domain/pricing.ts
        exports:
          - name: calculatePrice
            kind: value
            type: '(params: PriceParams) => Promise<number>'
          - name: PricingTier
            kind: type

    // src/domain/pricing.ts (YOU own this)
    export type PricingTier = 'free' | 'starter' | 'pro' | 'enterprise';
    
    export interface PriceParams {
      tier: PricingTier;
      seats: number;
      billingCycle: 'monthly' | 'annual';
    }
    
    export async function calculatePrice(params: PriceParams): Promise<number> {
      // YOUR complex pricing logic
    // Weldr leaves this file untouched
      const basePrices = {
        free: 0,
        starter: 49,
        pro: 199,
        enterprise: 999
      };
    
      let total = basePrices[params.tier];
    
      if (params.seats > 1) {
        total += (params.seats - 1) * getSeatPrice(params.tier);
      }
    
      if (params.billingCycle === 'annual') {
        total = total * 12 * 0.83; // 17% discount
      }
    
      return total;
    }
    
    function getSeatPrice(tier: PricingTier): number {
      // Helper function (not exported, not in contract)
      const prices = { free: 0, starter: 10, pro: 30, enterprise: 50 };
      return prices[tier];
    }

    Contract validation:
    $ weldr contracts check
    
    ✅ domain.pricing
      ✅ Export 'calculatePrice' exists
      ✅ Type signature matches: (params: PriceParams) => Promise<number>
      ✅ Export 'PricingTier' exists
      ✅ Type matches

    Auto-generated adapter:
    // lib/contracts/domain/pricing.ts (auto-generated by Weldr)
    /** @weldr surface: domain_logic mode: managed **/
    export { calculatePrice, PricingTier } from '@/src/domain/pricing';

    Regeneration behavior:
  • User: "Update checkout workflow"
  • Weldr: Regenerates lib/server/workflows/checkout.ts
  • Weldr: Uses import { domainPricing } from '@/lib/contracts'
  • Contract validator: Ensures src/domain/pricing.ts still satisfies contract
  • Result: Workflow updated, pricing logic untouched
  • ---

    The Nine Surfaces

    Weldr organizes code into 9 surfaces, each with independent ownership mode:

    1. app - Application Routing

    Content:
  • • Next.js app directory structure
  • • Layouts (layout.tsx)
  • • Page shells (page.tsx)
  • • Loading/error states
  • Recommended mode: managed Why: Routing is boilerplate, let Weldr handle it Example:
    app/
    ├── (auth)/
    │   ├── login/
    │   │   └── page.tsx          # Login page
    │   └── signup/
    │       └── page.tsx          # Signup page
    ├── (dashboard)/
    │   ├── layout.tsx            # Dashboard layout
    │   ├── projects/
    │   │   └── page.tsx          # Projects list
    │   └── tasks/
    │       └── page.tsx          # Tasks list
    └── layout.tsx                # Root layout

    ---

    2. db - Database Schema & Migrations

    Content:
  • • Drizzle ORM schema (drizzle/schema.ts)
  • • SQL migrations (drizzle/migrations/)
  • • Relations (drizzle/schema-relations.ts)
  • Recommended mode: managed (unless exotic types needed) Why: Schema changes should propagate everywhere (CRUD, forms, types) Example:
    // drizzle/schema.ts
    export const projects = pgTable('projects', {
      id: uuid('id').primaryKey().defaultRandom(),
      name: text('name').notNull(),
      status: text('status', {
        enum: ['active', 'archived', 'on_hold']
      }).default('active'),
      ownerId: uuid('owner_id').references(() => users.id),
      createdAt: timestamp('created_at').defaultNow()
    });

    When to eject:
  • • PostGIS geometry types
  • • Custom Postgres types
  • • Complex constraints
  • • Performance-critical indexes
  • ---

    3. auth - Authentication

    Content:
  • • Auth configuration (lib/auth/)
  • • Login/signup logic
  • • Session management
  • • Password reset
  • Recommended mode: managed (unless HIPAA/custom requirements) Why: Auth is standardized, security-critical (better generated) When to eject:
  • • Custom SSO integration
  • • HIPAA compliance requirements
  • • Multi-factor auth with custom provider
  • • Biometric authentication
  • ---

    4. api - HTTP Endpoints & Server Actions

    Content:
  • • Server actions (lib/server/actions/)
  • • API routes (app/api/)
  • • CRUD operations
  • Recommended mode: extended (most common customization point) Why: Need both CRUD (generated) and custom logic (your code) Example:
    surfaces:
      api:
        mode: extended

    // lib/server/actions/projects.ts
    /** @weldr surface: api mode: extended **/
    
    // @weldr:begin crud-project
    export async function createProject(input: CreateProjectInput) {
      return await db.insert(projects).values(input);
    }
    // @weldr:end crud-project
    
    // @custom:begin project-archive
    export async function archiveProject(id: string) {
      // Soft delete + cleanup logic
      await db.update(projects)
        .set({ status: 'archived', archivedAt: new Date() })
        .where(eq(projects.id, id));
    
      // Unassign all tasks
      await db.update(tasks)
        .set({ assigneeId: null })
        .where(eq(tasks.projectId, id));
    
      // Notify team
      await notifyTeam(id, 'project_archived');
    }
    // @custom:end project-archive

    ---

    5. domain_logic - Business Logic & Algorithms

    Content:
  • • Complex algorithms
  • • Business rules
  • • Core workflows
  • • Calculations (pricing, scoring, matching)
  • Recommended mode: ejected (your competitive advantage) Why: Business logic IS your product, you should own it Example:
    surfaces:
      domain_logic:
        mode: ejected
        entrypoints:
          - src/domain/pricing.ts
          - src/domain/matching.ts
          - src/domain/analytics.ts

    ---

    6. ui - React Components

    Content:
  • • React components (components/)
  • • Forms
  • • Tables
  • • Dashboards
  • Recommended mode: extended (mix standard + custom components) Why: Standard components (forms, tables) can be generated, custom components are yours Example:
    surfaces:
      ui:
        mode: extended

    // components/project-card.tsx
    /** @weldr surface: ui mode: extended **/
    
    // @weldr:begin project-card-base
    export function ProjectCard({ project }: { project: Project }) {
      return (
        <Card>
          <CardHeader>
            <CardTitle>{project.name}</CardTitle>
          </CardHeader>
          <CardContent>
            <p>Status: {project.status}</p>
            <p>Owner: {project.owner.name}</p>
          </CardContent>
        </Card>
      );
    }
    // @weldr:end project-card-base
    
    // @custom:begin project-card-actions
    export function ProjectCardWithActions({ project }: { project: Project }) {
      const [isArchiving, setIsArchiving] = useState(false);
    
      const handleArchive = async () => {
        setIsArchiving(true);
        await archiveProject(project.id);
        setIsArchiving(false);
        toast.success('Project archived');
      };
    
      return (
        <Card>
          <ProjectCard project={project} />
          <CardFooter>
            <Button onClick={handleArchive} disabled={isArchiving}>
              Archive
            </Button>
          </CardFooter>
        </Card>
      );
    }
    // @custom:end project-card-actions

    ---

    7. styling - CSS & Design System

    Content:
  • • Tailwind configuration
  • • CSS modules
  • • Theme configuration
  • • Design tokens
  • Recommended mode: ejected (your brand) Why: Branding is unique, design systems are custom Example:
    surfaces:
      styling:
        mode: ejected
        entrypoints:
          - tailwind.config.ts
          - app/globals.css

    ---

    8. infra - Infrastructure & Middleware

    Content:
  • • Middleware (middleware.ts)
  • • Utilities (lib/utils/)
  • • Error handling
  • • Logging
  • Recommended mode: managed (unless custom monitoring) Why: Infrastructure is boilerplate When to extend:
  • • Custom error tracking (Sentry, Rollbar)
  • • Performance monitoring (New Relic, Datadog)
  • • Custom logging (structured logs, compliance)
  • ---

    9. ci - CI/CD & Deployment

    Content:
  • • GitHub Actions (.github/workflows/)
  • • Deployment configs (vercel.json, Dockerfile)
  • • Environment configs
  • Recommended mode: managed (unless custom pipeline) Why: CI/CD is standardized When to eject:
  • • Custom deployment targets (Kubernetes, AWS ECS)
  • • Multi-region deployments
  • • Complex testing pipelines
  • ---

    Ownership Manifest

    The ownership manifest (weldr.ownership.yaml) is the single source of truth for ownership decisions.

    Schema

    version: ownership/v2
    
    surfaces:
      # Simple surface (basic mode)
      db: managed
    
      # Surface with options
      api:
        mode: extended
    
      # Ejected surface with entrypoints
      domain_logic:
        mode: ejected
        entrypoints:
          - src/domain/pricing.ts
          - src/domain/matching.ts
    
    contracts:
      # Contract definition
      - id: domain.pricing
        surface: domain_logic
        module: src/domain/pricing.ts
        exports:
          - name: calculatePrice
            kind: value
            type: '(params: PriceParams) => Promise<number>'
          - name: PricingTier
            kind: type

    Validation

    Weldr validates the manifest on every operation:

    $ weldr doctor
    
    ✅ Ownership Manifest (weldr.ownership.yaml)
      ✅ Version: ownership/v2
      ✅ 9 surfaces defined
      ✅ 2 contracts defined
    
    ✅ Surface Status
      ✅ app: managed (12 files)
      ✅ db: managed (4 files)
      ✅ auth: managed (8 files)
      ✅ api: extended (15 files, 6 custom blocks)
      ✅ domain_logic: ejected (3 modules, 2 contracts)
      ✅ ui: extended (24 files, 12 custom blocks)
      ✅ styling: ejected (2 files)
      ✅ infra: managed (5 files)
      ✅ ci: managed (3 files)
    
    ✅ Contracts
      ✅ domain.pricing (src/domain/pricing.ts)
        ✅ calculatePrice: (params: PriceParams) => Promise<number>
        ✅ PricingTier: type
      ✅ domain.matching (src/domain/matching.ts)
        ✅ findMatches: (userId: string) => Promise<Match[]>

    ---

    File Lease System

    Every generated file has an ownership header (lease) that declares its surface and mode.

    Lease Format

    /** @weldr surface: api mode: managed **/

    or

    /** @weldr surface: ui mode: extended **/

    Lease Extraction

    Weldr reads leases to determine ownership:

    // lib/ownership/lease.ts
    
    export interface FileLease {
      surface: SurfaceId;
      mode: OwnershipMode;
    }
    
    export function extractLease(content: string): FileLease | null {
      const regex = /\/\*\*\s*@weldr\s+surface:\s*(\w+)\s+mode:\s*(\w+)\s*\*\*\//;
      const match = content.match(regex);
    
      if (!match) return null;
    
      return {
        surface: match[1] as SurfaceId,
        mode: match[2] as OwnershipMode
      };
    }

    Lease Verification

    $ weldr leases verify
    
    ✅ All file leases match manifest
    
    Checked 78 files:
      ✅ 45 managed files
      ✅ 23 extended files
      ✅ 10 ejected files (no leases, as expected)

    ---

    Extended Merge Engine

    The Extended merge engine preserves @custom blocks during regeneration.

    Block Syntax

    // @weldr:begin block-name
    // Generated code
    // @weldr:end block-name
    
    // @custom:begin custom-block-name
    // Your code
    // @custom:end custom-block-name

    Merge Algorithm

    3-way merge:
  • Base - Last generated version (stored internally)
  • Theirs - New generated version (from current regeneration)
  • Yours - Current file with custom blocks
  • Steps:
  • Extract all @custom blocks from current file
  • Extract all @weldr blocks from new generated version
  • Merge: Replace @weldr blocks, preserve @custom blocks
  • Detect conflicts (orphaned or malformed blocks)
  • Conflict Detection

    Orphaned block:
    // @custom:begin old-function
    // This function was removed from generated code
    // @custom:end old-function

    Warning:
    ⚠️  Orphaned custom block detected: old-function
        File: lib/server/actions/users.ts
        Suggestion: Remove or refactor this block

    Malformed block:
    // @custom:begin missing-end
    // Code with no closing tag

    Error:
    ❌ Malformed custom block: missing-end
       File: lib/server/actions/users.ts
       Error: No closing tag found

    Example Merge

    Before regeneration:
    /** @weldr surface: api mode: extended **/
    
    // @weldr:begin crud-user
    export async function createUser(input: CreateUserInput) {
      return await db.insert(users).values(input);
    }
    // @weldr:end crud-user
    
    // @custom:begin user-helpers
    export async function createUserWithWorkspace(input: CreateUserInput) {
      const user = await createUser(input);
      await createWorkspace(user.id);
      return user;
    }
    // @custom:end user-helpers

    New generated version (schema changed):
    // @weldr:begin crud-user
    export async function createUser(input: CreateUserInput) {
      // New validation logic
      validateEmail(input.email);
    
      return await db.insert(users).values({
        ...input,
        avatarUrl: input.avatarUrl || '/default-avatar.png' // New field
      });
    }
    // @weldr:end crud-user

    After merge:
    /** @weldr surface: api mode: extended **/
    
    // @weldr:begin crud-user
    export async function createUser(input: CreateUserInput) {
      // New validation logic
      validateEmail(input.email);
    
      return await db.insert(users).values({
        ...input,
        avatarUrl: input.avatarUrl || '/default-avatar.png' // New field
      });
    }
    // @weldr:end crud-user
    
    // @custom:begin user-helpers
    export async function createUserWithWorkspace(input: CreateUserInput) {
      const user = await createUser(input);
      await createWorkspace(user.id);
      return user;
    }
    // @custom:end user-helpers

    Result: @weldr block updated, @custom block preserved.

    ---

    Contract Validation

    For Ejected surfaces, Weldr uses the TypeScript compiler API to validate contracts.

    Contract Definition

    contracts:
      - id: domain.pricing
        surface: domain_logic
        module: src/domain/pricing.ts
        exports:
          - name: calculatePrice
            kind: value
            type: '(params: PriceParams) => Promise<number>'
          - name: PricingTier
            kind: type

    Validation Process

    Step 1: Create virtual module that imports contract

    // temp-contract-validator.ts (in-memory)
    import { calculatePrice, PricingTier } from './src/domain/pricing';
    
    // Type assertions to validate signatures
    const _calculatePriceCheck: (params: PriceParams) => Promise<number> = calculatePrice;
    const _pricingTierCheck: PricingTier = 'free';

    Step 2: Compile with TypeScript

    import ts from 'typescript';
    
    const program = ts.createProgram([
      'temp-contract-validator.ts'
    ], {
      moduleResolution: ts.ModuleResolutionKind.Bundler,
      strict: true,
      skipLibCheck: true
    });
    
    const diagnostics = ts.getPreEmitDiagnostics(program);

    Step 3: Extract errors

    if (diagnostics.length > 0) {
      // Type mismatch detected
      return {
        valid: false,
        errors: diagnostics.map(d => ({
          message: d.messageText,
          contractId: 'domain.pricing',
          exportName: 'calculatePrice'
        }))
      };
    }
    
    return { valid: true };

    Example Validation

    Contract satisfied:
    $ weldr contracts check
    
    ✅ domain.pricing
      ✅ Export 'calculatePrice' exists
      ✅ Type signature matches

    Contract violated (wrong return type):
    // src/domain/pricing.ts
    export async function calculatePrice(params: PriceParams): Promise<string> {
      // Returns string instead of number
      return "$49";
    }

    $ weldr contracts check
    
    ❌ domain.pricing
      ❌ Export 'calculatePrice' type mismatch
         Expected: (params: PriceParams) => Promise<number>
         Actual:   (params: PriceParams) => Promise<string>
    
    Suggestion: Update src/domain/pricing.ts to return Promise<number>

    ---

    Contract Adapters

    Weldr auto-generates adapters for ejected modules to integrate them with generated code.

    Adapter Structure

    For contract:
    contracts:
      - id: domain.pricing
        surface: domain_logic
        module: src/domain/pricing.ts
        exports:
          - name: calculatePrice
          - name: PricingTier

    Generated adapter:
    // lib/contracts/domain/pricing.ts (auto-generated)
    /** @weldr surface: domain_logic mode: managed **/
    
    // Re-export from ejected module
    export { calculatePrice, PricingTier } from '@/src/domain/pricing';

    Generated index:
    // lib/contracts/index.ts (auto-generated)
    /** @weldr surface: domain_logic mode: managed **/
    
    export * as domainPricing from './domain/pricing';
    export * as domainMatching from './domain/matching';

    Usage in Generated Code

    // lib/server/workflows/checkout.ts (generated by Weldr)
    import { domainPricing } from '@/lib/contracts';
    
    export async function checkoutFlow(input: CheckoutInput) {
      // Uses adapter (which re-exports from src/domain/pricing.ts)
      const price = await domainPricing.calculatePrice({
        tier: input.tier,
        seats: input.seats,
        billingCycle: input.billingCycle
      });
    
      // Create Stripe session with calculated price
      const session = await stripe.checkout.sessions.create({
        line_items: [{
          price_data: {
            currency: 'usd',
            unit_amount: Math.round(price * 100)
          },
          quantity: 1
        }]
      });
    
      return session;
    }

    Why adapters?
  • Indirection - Generated code imports from lib/contracts, not src/domain
  • Stability - If you move src/domain/pricing.ts, update adapter, not all imports
  • Namespace - All contracts in one place (domainPricing, domainMatching, etc.)
  • Validation - Adapter generation fails if contract not satisfied
  • ---

    CLI Tools

    weldr doctor

    Shows ownership status and diagnostics

    $ weldr doctor
    
    ✅ Ownership Manifest
      ✅ Version: ownership/v2
      ✅ 9 surfaces defined
      ✅ 2 contracts defined
    
    ✅ Surface Status
      ✅ app: managed (12 files)
      ✅ db: managed (4 files)
      ✅ api: extended (15 files, 6 custom blocks)
      ✅ domain_logic: ejected (3 modules)
    
    ✅ Contracts
      ✅ domain.pricing ✅
      ✅ domain.matching ✅
    
    ⚠️  Warnings
      ⚠️  api surface has 2 orphaned custom blocks
          - lib/server/actions/users.ts: old-function
          - lib/server/actions/projects.ts: deprecated-helper

    ---

    weldr leases verify

    Verifies file leases match manifest

    $ weldr leases verify
    
    ✅ All file leases match manifest
    
    Checked 78 files:
      ✅ 45 managed files have correct lease headers
      ✅ 23 extended files have correct lease headers
      ✅ 10 ejected files (no leases required)
    
    Files by surface:
      app:          12 files (all managed)
      db:           4 files (all managed)
      auth:         8 files (all managed)
      api:          15 files (all extended)
      domain_logic: 3 files (all ejected)
      ui:           24 files (20 extended, 4 managed)
      styling:      2 files (all ejected)
      infra:        5 files (all managed)
      ci:           3 files (all managed)

    ---

    weldr contracts check

    Validates all contracts using TypeScript compiler

    $ weldr contracts check
    
    Checking 2 contracts...
    
    ✅ domain.pricing (src/domain/pricing.ts)
      ✅ Export 'calculatePrice' exists
      ✅ Type: (params: PriceParams) => Promise<number>
      ✅ Export 'PricingTier' exists
      ✅ Type: type
    
    ✅ domain.matching (src/domain/matching.ts)
      ✅ Export 'findMatches' exists
      ✅ Type: (userId: string, limit?: number) => Promise<Match[]>
    
    ✅ All contracts satisfied

    ---

    weldr sync

    Syncs ownership state and regenerates

    $ weldr sync
    
    🔄 Syncing ownership state...
    
    1. Loading manifest...
    2. Verifying leases...
    3. Checking contracts...
    4. Regenerating managed surfaces...
       ✅ app (12 files regenerated)
       ✅ db (1 migration generated)
       ✅ auth (0 changes)
    5. Merging extended surfaces...
       ✅ api (15 files merged, 6 custom blocks preserved)
       ✅ ui (24 files merged, 12 custom blocks preserved)
    6. Validating ejected contracts...
       ✅ domain.pricing
       ✅ domain.matching
    7. Generating contract adapters...
       ✅ lib/contracts/domain/pricing.ts
       ✅ lib/contracts/domain/matching.ts
       ✅ lib/contracts/index.ts
    
    ✅ Sync complete

    ---

    Implementation Details

    Codegen Pipeline

    Phase 1-4 implementation:

    // scripts/codegen/pipeline.ts
    
    async function generateApplication(dsl: DSL, manifest: OwnershipManifest) {
      const artifacts: Artifact[] = [];
    
      // 1. Database planner (managed)
      if (manifest.surfaces.db === 'managed') {
        artifacts.push(...await generateDatabaseArtifacts(dsl));
      }
    
      // 2. API planner (managed/extended)
      if (manifest.surfaces.api === 'managed' || manifest.surfaces.api.mode === 'extended') {
        artifacts.push(...await generateAPIArtifacts(dsl));
      }
    
      // 3. Contract planner (for ejected surfaces)
      const ejectedSurfaces = Object.entries(manifest.surfaces)
        .filter(([_, mode]) => mode === 'ejected' || mode.mode === 'ejected');
    
      if (ejectedSurfaces.length > 0) {
        artifacts.push(...await generateContractAdapters(manifest.contracts));
      }
    
      // 4. Apply leases
      for (const artifact of artifacts) {
        artifact.content = applyLease(artifact.content, artifact.surface, artifact.mode);
      }
    
      // 5. Write files (with merge for extended)
      for (const artifact of artifacts) {
        if (artifact.mode === 'extended') {
          await writeWithMerge(artifact);
        } else {
          await writeFile(artifact);
        }
      }
    
      return artifacts;
    }

    Extended Merge Implementation

    // lib/ownership/extended-merge.ts
    
    export function mergeExtended(
      base: string,
      theirs: string,
      yours: string
    ): MergeResult {
      // 1. Extract custom blocks from "yours"
      const customBlocks = extractCustomBlocks(yours);
    
      // 2. Extract weldr blocks from "theirs"
      const weldrBlocks = extractWeldrBlocks(theirs);
    
      // 3. Merge
      let merged = theirs; // Start with new generated version
    
      // Replace weldr blocks (already in 'theirs')
      // Inject custom blocks back
      for (const block of customBlocks) {
        merged = injectCustomBlock(merged, block);
      }
    
      // 4. Detect conflicts
      const conflicts = detectConflicts(customBlocks, weldrBlocks);
    
      return {
        content: merged,
        conflicts,
        customBlocksPreserved: customBlocks.length
      };
    }

    Contract Validator Implementation

    // lib/ownership/contracts.ts
    
    export async function validateContract(
      contract: Contract,
      rootDir: string
    ): Promise<ValidationResult> {
      // 1. Create virtual module
      const validatorCode = generateValidatorCode(contract);
    
      // 2. Write temp file
      const tempFile = path.join(rootDir, '.weldr', 'temp-validator.ts');
      await fs.writeFile(tempFile, validatorCode);
    
      // 3. Compile with TypeScript
      const program = ts.createProgram([tempFile], {
        moduleResolution: ts.ModuleResolutionKind.Bundler,
        strict: true,
        skipLibCheck: true,
        baseUrl: rootDir
      });
    
      // 4. Get diagnostics
      const diagnostics = ts.getPreEmitDiagnostics(program);
    
      // 5. Parse errors
      const errors = diagnostics.map(d => ({
        message: ts.flattenDiagnosticMessageText(d.messageText, '\n'),
        contractId: contract.id,
        exportName: extractExportName(d)
      }));
    
      // 6. Cleanup
      await fs.unlink(tempFile);
    
      return {
        valid: errors.length === 0,
        errors
      };
    }
    
    function generateValidatorCode(contract: Contract): string {
      const imports = contract.exports.map(e => e.name).join(', ');
    
      const assertions = contract.exports.map(e => {
        if (e.kind === 'value') {
          return `const _${e.name}Check: ${e.type} = ${e.name};`;
        } else {
          return `const _${e.name}Check: ${e.name} = null as any;`;
        }
      }).join('\n');
    
      return `
    import { ${imports} } from './${contract.module}';
    
    ${assertions}
      `;
    }

    ---

    Advanced Patterns

    Pattern 1: Gradual Ejection

    Start with everything managed, eject incrementally as needs evolve

    # Month 1: Everything managed
    surfaces:
      db: managed
      api: managed
      domain_logic: managed
      ui: managed

    # Month 3: Eject pricing (competitive advantage)
    surfaces:
      db: managed
      api: extended          # Started adding custom helpers
      domain_logic:
        mode: ejected
        entrypoints:
          - src/domain/pricing.ts
      ui: managed

    # Month 6: Eject more as complexity grows
    surfaces:
      db: managed
      api: extended
      domain_logic:
        mode: ejected
        entrypoints:
          - src/domain/pricing.ts
          - src/domain/recommendations.ts
          - src/domain/analytics.ts
      ui: extended          # Custom components now
      styling: ejected      # Brand customization

    ---

    Pattern 2: Contract-First Development

    Define contracts before implementing

    # Define contracts upfront
    contracts:
      - id: domain.payments
        surface: domain_logic
        module: src/domain/payments.ts
        exports:
          - name: processPayment
            kind: value
            type: '(input: PaymentInput) => Promise<PaymentResult>'
    
      - id: domain.notifications
        surface: domain_logic
        module: src/domain/notifications.ts
        exports:
          - name: sendNotification
            kind: value
            type: '(input: NotificationInput) => Promise<void>'

    Implement stubs:
    // src/domain/payments.ts
    export async function processPayment(input: PaymentInput): Promise<PaymentResult> {
      // TODO: Implement
      throw new Error('Not implemented');
    }

    Weldr generates workflows that use contracts (via adapters):
    // lib/server/workflows/checkout.ts (generated)
    import { domainPayments } from '@/lib/contracts';
    
    export async function checkoutFlow(input: CheckoutInput) {
      // Uses contract (currently throws, but type-safe)
      const result = await domainPayments.processPayment({
        amount: input.amount,
        currency: 'usd',
        source: input.paymentMethod
      });
    
      return result;
    }

    Implement later:
    // src/domain/payments.ts
    export async function processPayment(input: PaymentInput): Promise<PaymentResult> {
      // Real Stripe implementation
      const charge = await stripe.charges.create({
        amount: input.amount,
        currency: input.currency,
        source: input.source
      });
    
      return {
        success: true,
        transactionId: charge.id,
        amount: charge.amount
      };
    }

    No workflow changes needed - contract satisfied.

    ---

    Pattern 3: Multi-Module Ejection

    Eject multiple related modules together

    surfaces:
      domain_logic:
        mode: ejected
        entrypoints:
          - src/domain/pricing/**/*.ts
          - src/domain/billing/**/*.ts
          - src/domain/subscriptions/**/*.ts
    
    contracts:
      # Pricing contracts
      - id: domain.pricing.calculate
        surface: domain_logic
        module: src/domain/pricing/calculate.ts
        exports:
          - name: calculatePrice
            kind: value
            type: '(params: PriceParams) => Promise<number>'
    
      - id: domain.pricing.tiers
        surface: domain_logic
        module: src/domain/pricing/tiers.ts
        exports:
          - name: PricingTier
            kind: type
    
      # Billing contracts
      - id: domain.billing.invoice
        surface: domain_logic
        module: src/domain/billing/invoice.ts
        exports:
          - name: generateInvoice
            kind: value
            type: '(subscription: Subscription) => Promise<Invoice>'
    
      # Subscription contracts
      - id: domain.subscriptions.manage
        surface: domain_logic
        module: src/domain/subscriptions/manage.ts
        exports:
          - name: upgradeSubscription
            kind: value
            type: '(id: string, tier: PricingTier) => Promise<Subscription>'

    Generated adapters:
    // lib/contracts/domain/pricing-calculate.ts
    export { calculatePrice } from '@/src/domain/pricing/calculate';
    
    // lib/contracts/domain/pricing-tiers.ts
    export { PricingTier } from '@/src/domain/pricing/tiers';
    
    // lib/contracts/domain/billing-invoice.ts
    export { generateInvoice } from '@/src/domain/billing/invoice';
    
    // lib/contracts/domain/subscriptions-manage.ts
    export { upgradeSubscription } from '@/src/domain/subscriptions/manage';
    
    // lib/contracts/index.ts
    export * as domainPricingCalculate from './domain/pricing-calculate';
    export * as domainPricingTiers from './domain/pricing-tiers';
    export * as domainBillingInvoice from './domain/billing-invoice';
    export * as domainSubscriptionsManage from './domain/subscriptions-manage';

    ---

    Troubleshooting

    Issue 1: Orphaned Custom Blocks

    Problem:
    ⚠️  Orphaned custom block detected: old-function
        File: lib/server/actions/users.ts

    Cause: Function was removed from generated code, but custom block still references it. Solution:
  • Remove the orphaned block:
  •    // @custom:begin old-function
       // Delete this entire block
       // @custom:end old-function

  • Or refactor to not depend on generated code:
  •    // @custom:begin old-function-refactored
       export async function oldFunction() {
         // Rewrite to not use removed generated function
       }
       // @custom:end old-function-refactored

    ---

    Issue 2: Contract Validation Failure

    Problem:
    ❌ domain.pricing
       ❌ Export 'calculatePrice' type mismatch
          Expected: (params: PriceParams) => Promise<number>
          Actual:   (params: PriceParams) => number

    Cause: Function signature doesn't match contract (missing Promise). Solution: Update implementation to match contract:
    // Before (wrong)
    export function calculatePrice(params: PriceParams): number {
      return 49;
    }
    
    // After (correct)
    export async function calculatePrice(params: PriceParams): Promise<number> {
      return 49;
    }

    ---

    Issue 3: Merge Conflict

    Problem:
    ⚠️  Merge conflict detected in lib/server/actions/users.ts
        - @weldr block 'crud-user' was modified manually

    Cause: You edited inside a @weldr block (regeneration overwrites it). Solution: Move changes to @custom block:
    // Before (wrong - editing weldr block)
    // @weldr:begin crud-user
    export async function createUser(input: CreateUserInput) {
      // Your custom validation (will be overwritten!)
      validateEmail(input.email);
    
      return await db.insert(users).values(input);
    }
    // @weldr:end crud-user
    
    // After (correct - use custom block)
    // @weldr:begin crud-user
    export async function createUser(input: CreateUserInput) {
      return await db.insert(users).values(input);
    }
    // @weldr:end crud-user
    
    // @custom:begin user-validation
    export async function createUserWithValidation(input: CreateUserInput) {
      // Your custom validation (safe from regeneration)
      validateEmail(input.email);
      validatePasswordStrength(input.password);
    
      return await createUser(input);
    }
    // @custom:end user-validation

    ---

    Issue 4: Missing Contract Adapter

    Problem:
    Module not found: Can't resolve '@/lib/contracts/domain/pricing'

    Cause: Contract defined but adapter not generated. Solution: Regenerate adapters:
    $ weldr sync
    
    🔄 Generating contract adapters...
       ✅ lib/contracts/domain/pricing.ts
       ✅ lib/contracts/index.ts

    ---

    Summary

    Progressive Ownership gives you:
  • Three modes per surface (Managed, Extended, Ejected)
  • Nine surfaces for granular control (app, db, api, domain_logic, ui, styling, auth, infra, ci)
  • Ownership manifest as single source of truth
  • File leases for clear ownership boundaries
  • Extended merge for safe custom code preservation
  • Contract validation for type safety
  • Contract adapters for direct integration
  • CLI tools for ownership management
  • Result: Start with everything managed, take ownership incrementally, and keep control without lock-in.

    ---

    Next Steps:
  • • Read For Founders - No-code journey
  • • Read For Agencies - Agency workflows
  • • Read For Developers - Developer guide
  • • Try it: weldr.com
  • ---

    "Progressive Ownership is the only way to build with AI without vendor lock-in. You're in control, not the platform."