A new shape of application, born from new capability and new foundations.
An application that holds the machinery to extend itself: its own model, its own source, its own compiler, and a boundary it can load new behaviour across while running. It meets a gap in what it can do by writing the thing that closes it, compiling it, checking it, activating it, and carrying on.
It is possible now because two things changed, so I am building this at canopyrig.dev.
Neither alone produces it. Cheap memory without translation is a bigger machine running the old shape. Translation without room to hold a compiler, a repository and a history has nowhere to put what it writes.
Where that machinery has lived has moved in one direction for seventy years:
Every step moved more of the construction outward under a real constraint, and added apparatus. Every one of those constraints has since lifted.
Something else already moved, and it moved a long way.
The web turned distribution into something live, and then it moved execution as well. Before that, software left on physical media and ran on a machine nobody could reach afterwards, so it had to be finished before it shipped. Once it arrived over a connection and ran on infrastructure the people who wrote it controlled, nothing forced a finish line any more. You could change what people had and change where it ran, while they were using it.
So software stopped shipping final. Continuous deployment, feature flags, rolling releases, services that update under you — an application now changes many times a week and gains abilities it did not have when it launched. The finished artifact, shipped once and then left alone, is mostly gone.
What did not move is where the machinery to do that lives. Every one of those changes is still made outside and delivered in, by a construction site that assembles, does its work and stands down, over and over, for the life of the product.
The industry saw this clearly and answered it. Infrastructure as code, image building, Terraform and OpenTofu, internal developer platforms, the whole of platform engineering — that discipline exists because software stopped finishing. If the site has to stand for the life of the product, then make it declarative, reproducible, versioned, automated and permanently available.
It worked. A change reaches production in minutes, repeatably, across environments nobody has to remember the state of.
Which is the deeper reason this is not only a question of where the machinery sits.
A building has a finish line. The cranes leave because there is nothing left to build, and the whole arrangement is correct because the work genuinely ends.
Software has no finish line, and has not had one for a long time.
An application that cannot extend itself is not a finished building. It is a permanent dependency on a site that has to be reassembled every time anything needs to change — which is now continuous, and is exactly what we automated rather than questioned.
If you knew a building would never be finished, that it would need another floor every week for as long as anyone used it, you would not keep hiring cranes and closing the street. You would never put the crane away. And once it is staying, it stops being equipment parked outside and becomes part of what you built.
The surface those changes land on is already live. Running continuously, reachable, updatable, gaining capability while it is in use. That part arrived years ago and we stopped noticing it.
What changes now is who does the building, and when. Not a release train, not a sprint boundary, not a queue of tickets waiting for someone with time. At the point of need, when the demand appears, on the surface that is already running.
A map of a transition I am standing in.
Some of it is built and running. The rest is being built.
It is still a theory. Things can follow from what you are seeing and the frame still be wrong. What would falsify it: if generated code never becomes trustworthy enough to sit behind a capability boundary, this is a toy; if the dependency habit is load-bearing for reasons I am underrating — security review, shared maintenance, many eyes on one library — it produces a mountain of unreviewed near-duplicates and the supply chain wins on merit; if the governance layer cannot be made usable, all of it is technically true and practically irrelevant.
Three observations, and what follows from them.
This came out of the agentic web — thinking about what it means for software to be addressable by something that is not a person, then watching what these models actually do.
They work from reference, and choose how much to take. Sometimes that is the whole library — a mature cryptography implementation, a parser with a decade of edge cases in it, anything where the accumulated corrections are the value.
Sometimes it is twenty lines fitted to the case in front of it, rather than the thousand that serve everyone’s.
The capability is not that they avoid dependencies. It is that the choice becomes live.
For a person it mostly is not. Writing and maintaining it yourself is expensive enough that taking the dependency is the default, and a supply chain was built around that default. When producing a fitted extract costs almost nothing, both options are available and can be weighed case by case. That is agency.
They are slow, and that latency is architectural. This is the least arguable of the three because it is measurable. A model call is seconds. A function call is nanoseconds, a local read microseconds. That is not a gap tuning closes, and any design that puts a model in the hot path inherits it permanently — which rules out the model as a live interpreter consulted whenever the system meets something it cannot handle.
Generation is slow, so generation must terminate in an artifact: source, a compiled capability, something fast and inspectable that runs a thousand times afterwards without asking anything.
Every real gain in engineering has come from matching work to whatever suits it. There are three things to allocate across:
One line in the middle profile changes what you can design: a model will read the whole manual. Every page of the specification, every footnote in the RFC. Human attention is scarce and expensive, so reading what matters and inferring the rest is the correct response — the same scarcity logic that put the compiler outside the program, applied to a person’s afternoon. For a model that cost is near zero, so exhaustive reading is simply the cheaper option.
The same model will read every page of a specification and then emit a call to a function that does not exist, with no signal that anything is wrong. Hallucination is the failure mode at this stage of the technology, and it is measurably less common than it was a year ago. It is still the one the design has to hold.
So: harnesses. Types, tests, schemas, compilers, sandboxes, a commit that has to pass before it counts. This is what engineering is. Concrete gets rebar because concrete fails in tension. Fuses exist because circuits fail closed. Type systems exist because human attention does not scale to a large codebase.
It is also where this meets the iron triangle. Scope, time and cost are the three sides, and risk is what gives — squeeze the schedule and it is thoroughness that absorbs it. The test not written, the specification skimmed, the review deferred, the reason not written down. When thoroughness costs close to nothing, the price of that trade changes, and in exchange you accept a new risk: output that looks right and is not. Both numbers are moving. Models hallucinate measurably less than a year ago, and the harnesses around them have got better at the same time.
The harness does two jobs. It catches the made-up parts, and it turns a slow probabilistic act into something fast and reusable. As hallucination rates fall the harness thins, but the second job never depended on the first.
The allocation then writes itself. A model should decide something once. Deterministic machinery should do it a million times. A person should decide what is allowed to happen at all.
Each component that follows is a capability the design requires, not a product it depends on. WebAssembly, a compiler, a store — I name the ones I use, and something else providing the same properties would do equally well.
A boundary is where one kind of work converts into another, and where the properties of the result are decided. Generated code crossing into execution is where probabilistic output becomes a fixed artifact you can test. A capability boundary is where unreviewed code stops inheriting authority. A commit is where a proposal becomes truth. A delegation is where a person’s authority becomes an agent’s, attenuated and time-bound.
The direction of construction reverses.
I called it the inside-out app, because of what everything before it is.
A building site fills with machinery that is not the building: cranes, scaffolding, hoists, a site office. Behind it is a supply chain — factories making components elsewhere, warehouses holding them, trucks moving them, roads built so the trucks can arrive. Most of the effort in construction is not construction. It is logistics.
Software has every piece of that. Compilers and build systems are the cranes. Libraries are the factories. Registries are the warehouses. Pipelines are the trucks. The network is the roads, and a road exists because the quarry is not the site — it carries no load of its own.
When the work finishes the machinery leaves, and what remains cannot extend itself. For buildings that is correct. The machinery is operated by people who go home. For software it was correct on the same condition.
An inside-out app inverts it. The machinery is in the building, and extending it does not require an external site, because the thing that constructs is not a person who has gone home.
The supply chain serves generic need. A library is built for everyone, so it carries the weight of every case except yours alongside the one you wanted. Genericity is what makes manufacturing at a distance possible at all — and it sets the direction of construction. You start from what the tree offers, take a subset, and shape your application around what you were given.
Inside-out reverses the direction: start at the point of need and work outward, taking only what that need requires. Not the package — the technique the package would have delivered, fitted to the case in front of you. Which is already how the models work.
The material comes from open source. Decades of it, written down and readable.
Packaging existed because understanding someone else’s solution and rewriting it for your case was expensive, skilled, human work. Distribution was the cheap part. Comprehension was not.
Re-expression and close reproduction are different acts, and telling them apart at volume is not a solved problem. Installs and dependents do not register a technique that was read.
Moving the machinery inside also changes what the surrounding apparatus is for. Package managers resolve what happens when whole libraries are taken in. That problem shrinks when the technique is taken instead. Continuous integration reassembles the construction site whenever something changes; there is less to reassemble when the site never left. Each solves a real problem created by the separation. Change the separation and the problem changes shape.
Each piece came inside because the piece before it could not work alone.
The test case was small and stubborn: several agents working on one 3D scene at once without ruining each other’s work. I started where everyone starts — give them tools. A function to move an object, a function to read a material. It worked the way a demo works.
Then I asked what else had moved while they were moving that, and there was nowhere for the answer to come from. A tool call can change a thing. It cannot say what depended on it, who else was holding it, or whether the change survived.
What agents need in a shared world is closer to what a debugger needs than what an endpoint offers: typed objects, relationships, mutation boundaries, schemas, source locations, events, dependencies, provenance. That is compiler information, and a system that exposes that much of itself is most of the way to being able to change itself.
I did not design what follows. I pulled one piece inside and then had no choice about the next.
The first piece that could be inside was the model itself — resident, not called out to as a service. That lasted as long as it took to ask what it would act on. A model inside an application that cannot read or write its own source is a chat window with a good view.
For my whole career the executable was the application, with source, compiler, database and jobs standing around it as infrastructure. That was honest while people wrote the code.
A shared world needs a definition of truth that several agents can act against at once. Nothing becomes authoritative except through a committed transaction. Changes can be staged, previewed, validated and thrown away; commit is where they become real. That rule bought something I did not expect: somewhere safe to be wrong, which matters when the thing proposing changes generates plausible ideas cheaply.
Pure event sourcing is the wrong trade — agents query current state constantly and reconstructing it from a long history is expensive every time. What holds is a hybrid:
A separate database server existed because disk and memory were expensive and shared: one costly machine held the data, many cheap terminals used it. What it means in practice is that an application does not own what it knows — it rents it over a socket, in SQL, arranged as relations that do not match the objects in the code, paying a translation tax at the boundary forever. The tax has a name, the object-relational impedance mismatch, and ORMs exist to pay it.
Those constraints inverted. Kilobytes became tens of gigabytes. A ten-millisecond disk seek — the thing essentially all database engineering exists to avoid — became microseconds on flash. Meanwhile a network hop got relatively worse: memory is nanoseconds, local flash microseconds, another machine milliseconds.
Inside describes the application’s boundary, not its topology. A world can be held by a distributed store across a cluster and still be one logical world. Every node runs the same small bootstrap and takes work from it. This is where databases have always been excellent — concurrency, transactions, coordination, durability, distribution. None of that is a crane. State living in a database is not the problem; the application’s model being translated into a foreign one at the door, losing identity, history and provenance, is.
Files stay useful, but the filesystem becomes a projection: a source object appears at src/parser.rs without that path being what it is.
A model needs somewhere to put what it writes, so the repository comes inside and code becomes application state. The world then holds the whole path from text to running behaviour:
Every arrow is a relationship you can query rather than a step somebody remembers. Ask what produced a running capability and the answer is exact: this source, these dependencies, this compiler, these tests, these people.
One authoritative copy on a machine everyone could reach was the original reason. Distributed version control removed the technical need for a centre. The centre was rebuilt anyway, for social and operational reasons. The consequence that matters: your source lives in a system that knows nothing about your running application, and neither can answer a question about the other without a person in between.
Git stays as an interchange surface — the application should speak Git without becoming Git-shaped. Content addressing is what makes handoff exact: two agents holding the same hash know they have the same bytes, which is the ambiguity humans resolve by asking each other.
Source inside is just text until something can build it. The loop is ordinary application behaviour:
The compiler lived outside because the machine could hold the compiler or your program, not both. Separate compilation came from the same wall.
Compilers want a filesystem less than their reputation suggests — rustc and clang already operate over in-memory buffers, and Cargo’s fingerprints are a precise map of what a build depends on, more valuable as shared state than as a target/ directory everybody deletes. And one lifted constraint matters directly: one core became dozens, so there is somewhere to compile without stalling the work already running.
Once it has built something, how does that join a running application? You cannot relink a running process or restart the world every time it learns something. The boundary has to provide four properties:
dlopen provides none of them. WebAssembly provides all four. Code a person wrote has been read by a person. Code generated at runtime to fill a gap discovered at runtime has been read by nobody, and should not inherit the host’s authority because it happens to be running.
Self-modification is succession, not mutation: edit source, compile a successor, validate, move the active reference. Nothing mutates, history stays intact, and rollback is the pointer moving back.
A compiler inside needs a memory of what it has already built. Compilers, renderers, parsers, search indexes and financial rollups share one shape:
If those identities have not changed the earlier result is still valid — not probably valid, valid. Modification time approximates “did this change” with “did someone touch the file”, which is why builds are both wastefully repeated and dangerously skipped. A derivation can be identified exactly, which turns the question from is this stale into does this exact derivation exist. That is a lookup, and lookups distribute: one worker claims it, the rest wait, leases handle the one that dies halfway.
The underlying primitive is persistent facts, typed transformations and dependency-aware memoised derived state — most systems build a private, partial version and call it a cache. The same model covers business computation: change one transaction and precisely the dependent results go stale. And duplication becomes a signal — a helper written for the eleventh time is evidence of a missing abstraction, which the system can act on rather than a reviewer eventually complaining about.
Generated code needs somewhere to look things up, or several agents invent several names for one concept. A live registry holds identities, types, schemas, capabilities, contracts, implementations, builds, dependencies, effects, permissions and what replaced what. Agents query it before inventing identifiers; genuinely new concepts get a canonical identity reserved before implementation starts.
It can also describe what should exist — a subsystem declaring it requires a parser, an extractor and a validator without naming binaries.
Past a certain completeness it stops being metadata about the system and becomes a representation of it — the architectural equivalent of an AST. Compilers do not edit text when they optimise; they transform a structure and emit text at the end. Refactoring becomes a change to a semantic record rather than a coordinated edit across forty files.
Declarative architecture needs something comparing declared against actual:
Agentic software adds a case that loop never had: the system may not know how to realise the declaration. Conventionally that is terminal — capability missing, deployment fails, and the most interesting information in the run is written to a log and forgotten.
Instead the gap becomes a record: the evidence that exposed it, its scope, hypotheses, and a plan, linked to the world state and source that surfaced it. A parser meeting a construct it cannot handle can capture the sample, identify what is unsupported and write its own improvement plan against real evidence.
Jobs have to be durable programs rather than queue rows — waiting, branching, retrying, spawning child work, taking leases, outliving whatever worker runs them. Events say something happened. Jobs say something must happen.
Something has to do the work, and it is the only place in the design where intelligence is spent.
It investigates: reads the gap record, the evidence, the source that surfaced it, and what the registry says already exists. It plans. It writes source, creates jobs, builds tests, proposes schema changes, and constructs the capability that was missing.
What it does not get is a special position. It reads the registry like anything else, writes to the repository like anything else, and its output goes through the same compile, validate and activate path as code a person wrote.
That constraint is doing more work than it looks. An intelligence with privileged access to the world would be fast and unauditable. One that has to go through the boundaries is slower and leaves a trace at every step — the record exists because the builder had no way to skip it.
And it is deliberately narrow. The reconciler handles every gap it can handle deterministically. The registry answers identity questions as lookups. Derivations answer what already exists. The builder is what remains once everything answerable without intelligence has been answered.
An event log is not memory, and neither is a transcript or commit history. Each records that something occurred, not what it meant.
A discovery records what became known that changes the understanding of a goal. A skill records a method that proved effective enough to reuse. Both are deliberately separated from the volume of activity that produced them, and that separation is the value — a record that keeps everything has decided nothing is more important than anything else.
Promotion carries provenance: who promoted it, against which world state, on what evidence, with what confidence, linked to exact commits, jobs and artifacts.
Commits record what changed, not what else was considered or why one architecture beat another. For human teams that loss is survivable. Someone was in the room.
This is where my own judgement became the design note. My judgement is worth more at forty-five than at twenty-five, and not because I am quicker or know more technologies — I know fewer of the current ones than the people I hire. At twenty-five I could generate six plausible architectures before lunch.
Most of what a senior technologist is paid for is not that, because — followed by this instead, which is the half that separates judgement from caution. That is what experience is: compressed negative knowledge, and the redirection that comes with it.
The value was never in generating possibilities. It is in eliminating them early, cheaply, before anyone spends a quarter finding out.
So rejected alternatives have to be durable, and hold both halves. Why not alone produces a system that is merely conservative. A request also stops being a job: it becomes an intent and a decision space, resolved in order.
Every step leftward is cheaper, better reviewed and likelier to be maintained. Users need somewhere that is part catalog, part decision tree, part commissioning desk — what is published, what alternatives exist, what is being built, what was rejected and what was done instead.
Once software can create and activate code, identity and delegation become working parts. People and agents are both principals. An agent acts under its own identity while exercising authority delegated by someone else, and those stay separable, which is what makes who did this answerable when the actor was not a person. Delegation attenuates, and extra permission is requested with purpose, scope and duration.
A string, once handed over, has left your control. A capability can be scoped, attenuated, expired and revoked. Audit becomes a chain rather than a pile:
Accountability stops being an operational detail and becomes structural. That record either exists in the design or it does not. Nothing reconstructs it afterwards.
A self-modifying program changes itself. A factory constructs: designs, raw material, machinery, memory of what worked and records of what failed. It takes a request, prefers reuse, manufactures what is missing, validates it, installs it, and keeps what it learned.
A new parser is available to every later job. A new compiler manufactures later executables. A new schema gives the world vocabulary for something newly understood. Capability stops being something you plan and becomes something you accumulate.
The executable each node runs can end up small: enough trusted machinery to reach persistence, establish root trust and start the initial world. The rest lives inside that world as state and executable content, shared by every node that reaches it.
The version number is an outside-in artifact — software arriving in discrete drops from a site that is otherwise idle. So is the rewrite: we rewrite systems because their knowledge lives in people who left and decisions nobody recorded.
None of this came from deciding software should be different. It came from two things changing underneath it — what software can now do, and what the machine can now hold.
The application does not need to know everything when it ships. It needs to know how to become more capable without losing its integrity.
If any of this excites or interests you, come and look at CanopyRig — and reach out. I would like to hear from you.