The Plugin-First Rule
The most important Morph rule is simple:
Feature work belongs in packages, not in compiler core files.
That rule exists because packages govern the compile pipeline: they register sema, lowering, backend routes, and tooling through MorphABI. Core grows only when a new kind of hook is needed, not when a new feature is needed—see Plugin-governed pipeline.
What This Means
When you want to add behavior such as:
- a ternary expression
- a new operator
- a new type
- a runtime family
- a build or platform target
your first question is not "which core file should I patch?"
Your first question is:
Which package should own this?
Decision Tree
Use this order:
- Can an existing package own the feature?
- If not, should a new package own it?
- If packages cannot express it today, which framework or SDK capability must be widened?
Only the third case justifies core framework work.
Good Decisions
| Goal | Correct Direction |
|---|---|
| Add a new shared operator | extend Ops or another owning package |
| Add a tensor-specific feature | extend Tensor |
| Add a new test assertion | extend Test |
| Add a new execution command | extend Vcon or another tooling-owning package |
| Add a new platform target | extend Build plus a platform package |
Bad Decisions
These are the mistakes Morph is designed to stop:
- adding feature semantics directly to
src/parser/... - adding domain runtime behavior directly to
runtime/src/... - hardcoding a new command in core CLI dispatch
- adding platform-specific build branches directly in core build files
Core is for framework capability, not feature ownership.
Ternary As An Example
Suppose you want condition ? when_true : when_false.
The wrong instinct is:
- edit core parser code
- patch core semantic logic
- add a one-off lowering branch
The right instinct is:
- identify the package that should own expression syntax of that kind
- add or extend package features for syntax, sema, and lowering
- widen framework syntax hooks only if packages cannot express the form today
The Real Role Of Core
Core does not know \u2014 and must never know \u2014 any of the following:
- What keywords or tokens exist (
is,method,gpu,+,..\u2014 all declared in package[tokens]) - What a variable declaration looks like (declared in
blocks/*/block.toml[forms.*], not insrc/parser/) - What any type is (
Int,Float,Tensor,String\u2014 owned byTypesand concrete packages) - How any construct is lowered, optimized, or emitted (owned by package feature+route components)
- Which runtime symbols exist (declared in
[runtime.family.*]in packagemorph.tomlfiles)
Core is a generic, domain-agnostic orchestration host. Its job is to:
- run pipeline stages (lex, parse, sema, NIR, MIR, backend dispatch)
- load and compose registered package plugins via the
MorphABI - enforce ABI contracts between pipeline stages
- provide stable hook shapes so packages can plug in without touching
src/
Core's ignorance is its superpower. Because Core knows nothing, any language feature, any type, any target, any execution model can be added by extending packages \u2014 without rebuilding Core's understanding of the world.
Core is allowed to change only when package authors need a new kind of hook that the framework cannot currently express:
- a new parser hook shape
- a new manifest descriptor kind
- a new runtime host interface
- a new build or execution host API
That is framework work. It is rare. It is not the same as adding a feature.
A Core file that mentions a token name, a type name, or a specific AST construct by concept is a leak. Treat it as a bug.
Next Steps
- Package Layout - where package-owned work lives
- morph.toml - how ownership is declared
- Framework Headers And SDK - when framework widening is the right move