Ownership Manifest & Lease Headers
> Experimental: Ownership manifest APIs are still iterating. Treat the workflows below as guidance for internal teams until the UI ships broadly.
The ownership manifest tells Weldr which parts of a generated project remain fully managed, which expose safe extension points, and which surfaces are considered fully ejected. It lives at the project root as weldr.ownership.yaml and is evaluated before every regeneration.
API helpers
Automation agents can adjust scope ownership without editing YAML manually:
POST /api/ownership/update
Content-Type: application/json
{
"chatId": "2cf1c766-6e7d-4151-9c79-5ab2a1ef0c27",
"paths": ["lib/server/notifications/providers/slack.ts"],
"mode": "extended",
"description": "Extend Slack provider implementation"
}The endpoint creates (or updates) a scope covering the provided paths and persists the manifest (both in the YAML file and the database). Valid modes are managed, extended, and ejected. Fetch the stored manifest for a chat with GET /api/ownership/ to inspect all recorded surfaces and their repo paths.
Manifest schema
version: ownership/v2
surfaces:
model:
mode: managed
app_model_segments:
- entities
- relationships
- workflows
db: managed # drizzle schema + SQL migrations
auth: managed
api:
mode: extended # generated segments preserved, custom hooks allowed
hooks:
- src/api/_hooks/**/*.ts
actions:
mode: extended
automation:
mode: managed
domain_logic:
mode: ejected
entrypoints:
- src/domain/**/*.ts
ui: managed
ui_pages:
mode: extended
app_model_segments:
- pages
- navigation
ui_components:
mode: extended
eject_scopes:
- src/app/**/Custom*.tsx
- src/components/_custom/**/*
ui_theme:
mode: managed
styling: ejected
infra: managed
ci: managedSurfaces that are omitted default to managed. Structured entries allow extra metadata:
| Field | Meaning |
| ------------------- | ------------------------------------------------------------ |
| hooks | Glob patterns for additional user-owned files to load. |
| entrypoints | Runtime import globs used when a surface is ejected. |
| eject_scopes | UI/component globs that Weldr will never overwrite. |
| app_model_segments| Which App Model segments the surface governs (e.g. entities, pages). |
Unknown surface keys are ignored and reported as warnings.
How it feels in the UI
@weldr … @weldr regions for the generated scaffolding and companion @custom … @custom blocks for your code. Regeneration refreshes the managed regions while preserving anything you have inside the custom slots. Choose this when you want to augment the generated logic without forking it.In the code panel we highlight the active mode so developers instantly see whether a file is safe to edit, shared, or fully owned.
Switching modes safely
mode: extended.
- Regenerate once so Weldr rewrites the affected files with fresh @custom slots.
- Open a file in the code panel and drop your changes between the new @custom:begin / @custom:end markers.
@custom code will be removed on the next regeneration.
- After you regenerate, the file is rewritten without region markers. There is no safe place for manual edits, so keep everything generator-owned.
mode: ejected when you want full ownership.
- Weldr stops emitting future changes for that file. You can remove the @custom fences if you like, but keep the code handy—if you later toggle back to Compose, regeneration will rewrite the file from the managed template and you’ll need to reinsert any bespoke logic.
Remember: toggling to a more automated mode (Extended → Managed) is destructive for custom edits because the merge step disappears. Toggling to a more manual mode (Managed → Extended or Ejected) requires a fresh regeneration to pick up the new structure.
Effective state at runtime
loadOwnershipState() resolves the manifest (or produces an all-managed default when missing). The resolved shape is attached to the IR as ir.ownership, so codegen planners and the CLI always operate with the same surface modes. The helper formatOwnershipSummary() powers DX tooling such as weldr doctor.
File leases
Every generated artifact now carries a header describing its surface, ownership mode, and generator:
/** @weldr
surface: api
mode: extended
region-tags: on
generator: template
version: ownership/v2
**/In addition to the structured lease, we prepend a shorter banner (for example // @generated by Weldr.dev — api route managed…). This banner makes it obvious which files are safe to edit at a glance, while the @weldr block remains the authoritative metadata the CLI validates.
Leases make regeneration deterministic:
surface ties the file back to the manifest.mode records whether Weldr may overwrite, merge, or avoid the file.region-tags tracks whether generated/custom segments are expected (used in later phases).generator clarifies which pipeline produced the file (template, llm, or manual).ensureLeaseHeader() inserts or refreshes the header without touching user code, while extractLease() parses existing files for verification.
Extended regions
When a surface is set to extended, Weldr emits paired regions in the generated file so that custom code survives regeneration:
// @weldr:begin crud-order
// generated code here
// @weldr:end crud-order
// @custom:begin order-helpers
// developer-owned code lives here
// @custom:end order-helpersDuring a subsequent codegen run we only replace the @weldr blocks, leaving the @custom bodies untouched. If a custom block disappears from the latest template we raise a conflict instead of silently deleting user code.
Contracts
contracts describe the TypeScript surface a user-owned module must expose once a surface is ejected. Each contract entry specifies a module path relative to the generated workspace and a list of exports Weldr will type-check after regeneration. Example:
contracts:
- id: domain.orders
surface: domain_logic
module: src/domain/orders.ts
exports:
- name: createOrder
kind: value
type: '(input: CreateOrderInput) => Promise<Order>'
- name: Order
kind: typeAfter codegen, Weldr synthesises a temporary TypeScript program that imports each contract and verifies:
kind: default) is present.type string is supplied, the exported symbol matches the expected signature in both directions.${namespace}.${export} identifier (for example, domainOrders.createOrder).Failures surface as build errors (weldr doctor / weldr sync / weldr contracts check) with the contract id and export name that violated the agreement. This keeps ejected modules honest while letting developers own their implementation details.
CLI commands
weldr doctor — prints the effective ownership table and highlights manifest warnings.weldr leases verify — scans the local workspace, comparing file headers with the recorded manifest and current ownership policy.weldr manifest verify — requires a synced workspace (weldr sync --chat ) and cross-checks every scoped file against the canonical manifest in the database (use pnpm manifest:verify to run it via package scripts).weldr contracts check — validates contract adapters against developer-owned modules, reporting missing exports and type mismatches.Each command accepts --dir to inspect a specific workspace.
Defaults & safety nets
managed (no warnings).This layer is the foundation for the broader partial-ejection roadmap: once leases and ownership metadata are in place, we can introduce region-level merges, contract validation, and CLI workflows without worrying about silent overwrites.