Ownership

Ownership is orchex's core safety mechanism. Every stream declares which files it owns (can write) and which it reads (can only read). This prevents parallel agents from overwriting each other's work.

The Problem

Without ownership enforcement, parallel AI agents create race conditions:

Stream A: Writes src/config.ts → { port: 3000, database: "postgres" }
Stream B: Writes src/config.ts → { port: 3000, auth: { secret: "abc" } }
                                                        (runs in parallel)

The last stream to finish wins. The other stream's changes are silently lost. This is the fundamental challenge of parallel code generation — and the reason most orchestration tools run tasks serially.

How Ownership Works

Declaration

Each stream declares files in two arrays:

"auth-middleware": {
  owns: ["src/middleware/auth.ts"],       // Can create, modify, delete
  reads: ["src/types/user.ts"],          // Can read, cannot modify
  deps: ["user-types"],
  plan: "..."
}

Enforcement Points

Ownership is enforced at four points:

  1. At init time — orchex validates that no two streams own the same file. If there's a conflict, the initialization fails with a clear error message.

  2. During context building — Each stream's context prompt includes only files it owns (with "you may edit") and files it reads (with "read-only — do not modify").

  3. During artifact application — When the LLM produces file operations, orchex checks each operation against the stream's ownership list. Writes to unowned files are blocked.

  4. During verification — After applying the artifact, orchex checks for ownership violations before marking the stream as complete.

Owns vs Reads

`owns` — Exclusive Write Access

  • The stream can create, modify, or delete these files
  • Only one stream can own a given file across the entire orchestration
  • The LLM agent sees these files with full content and line numbers
  • Attempted ownership conflicts are caught at init time

`reads` — Shared Read Access

  • The stream can see these files in its context
  • Multiple streams can read the same file simultaneously
  • The LLM agent sees these files marked as "read-only"
  • Useful for types, configurations, and shared interfaces

Example

streams: {
  "user-types": {
    owns: ["src/types/user.ts"],
    plan: "Define User, UserRole, and CreateUserRequest interfaces"
  },
  "user-api": {
    owns: ["src/routes/users.ts"],
    reads: ["src/types/user.ts"],           // Reads types, can't modify
    deps: ["user-types"],
    plan: "Create REST API routes for user CRUD operations"
  },
  "user-tests": {
    owns: ["tests/users.test.ts"],
    reads: ["src/routes/users.ts", "src/types/user.ts"],  // Reads both
    deps: ["user-api"],
    plan: "Write integration tests for the user API endpoints"
  }
}

In this setup:

  • src/types/user.ts is owned by user-types and read by two other streams
  • Each route and test file is owned by exactly one stream
  • No two streams can write to the same file

Conflict Detection

At Init Time

Error: Ownership conflict — file "src/config.ts" is owned by both
"setup-config" and "update-config". Each file can only be owned by one stream.

Suggestion: Merge these streams, or split src/config.ts into separate
files (e.g., src/config/database.ts and src/config/auth.ts).

During Artifact Application

If an LLM agent attempts to modify a file it doesn't own (despite instructions not to), the operation is silently skipped and logged:

[WARN] Stream "auth-api" attempted to modify "src/types/user.ts"
       which is owned by "user-types". Operation blocked.

Patterns

Single Responsibility

Each stream owns files related to one concern:

"database-models": {
  owns: ["src/models/user.ts", "src/models/post.ts"],
  plan: "Create Prisma models for User and Post"
}

Shared Types

A dedicated types stream creates interfaces that other streams read:

"api-types": {
  owns: ["src/types/api.ts"],
  plan: "Define Request/Response interfaces for all API endpoints"
},
"api-routes": {
  owns: ["src/routes/api.ts"],
  reads: ["src/types/api.ts"],
  deps: ["api-types"],
  plan: "Implement API routes using the defined interfaces"
}

Code and Tests Together

Keep implementation and test files in the same stream when they must match:

"auth-service": {
  owns: ["src/services/auth.ts", "tests/auth.test.ts"],
  reads: ["src/types/auth.ts"],
  plan: "Implement auth service and write vitest tests",
  verify: ["npx vitest run tests/auth.test.ts"]
}

This ensures the tests match the implementation without an extra dependency hop.

Migration Files

Database migrations should be isolated in their own streams:

"migration-users": {
  owns: ["migrations/001-create-users.sql"],
  plan: "Create SQL migration for the users table",
  verify: ["npm run migrate:dry-run"]
}

Automatic Ownership Inference

When you use orchex learn to parse a plan document, ownership is inferred from file references in the plan:

  • Create: src/routes/api.ts → Added to owns
  • Modify: src/routes/api.ts → Added to owns
  • Read: src/types/api.ts → Added to reads
  • Import: import { User } from '../types/user' → Added to reads

The learn pipeline also detects ownership conflicts and generates dependency edges when one stream reads a file owned by another stream.

Pitfalls

Over-Owning

Owning too many files in one stream increases context size and reduces parallelism:

// Bad: One stream owns everything
"all-routes": {
  owns: ["src/routes/users.ts", "src/routes/posts.ts", "src/routes/auth.ts",
         "src/routes/billing.ts", "src/routes/admin.ts"],
  plan: "Implement all API routes"
}

Split into focused streams for better parallelism and manageable context.

Forgetting Reads

If a stream imports from a file it doesn't declare in reads, the LLM won't see that file's content and may generate incorrect code:

// Bad: Missing reads declaration
"api-routes": {
  owns: ["src/routes/api.ts"],
  // Forgot: reads: ["src/types/api.ts"]
  plan: "Implement routes using ApiRequest and ApiResponse types"
}

The LLM will guess at the type definitions instead of seeing the actual code.

Cross-Stream Edits

If you need two streams to modify the same file, restructure:

  1. Split the file — Put each stream's content in separate files
  2. Create a merge stream — A third stream that combines the outputs
  3. Reassign ownership — Give one stream ownership and make the other depend on it
  • Streams — Stream definition and lifecycle
  • Waves — How ownership enables safe parallel execution
  • Self-Healing — How ownership violations are handled
  • orchex learn — Automatic ownership inference from plans