Blog
Post · 2026-05-23

Develop high on the trees

There is a piece of development advice that has never been wrong and is still, most of the time, applied upside down. Stay close to the metal. Understand what the machine is actually doing. Trace the call stack. Read the bytecode. The advice is correct for performance engineers and hardware people. For governance engineers, it is precisely backwards. The most impactful move in a governed system is always the one that goes higher — further from the implementation, closer to the declaration, deeper into the crown of the governance tree.

This is a post about altitude.

The tree is not a metaphor

In git, a commit is not a diff. It is a tree object — a snapshot of the full directory structure, represented as a directed acyclic graph of blob and tree nodes. Every file you have ever committed lives as a content-addressed blob hanging off a tree node. The hash of a commit is the hash of that snapshot's root tree, which is why two commits with identical content have identical hashes regardless of author, date, or message. You are not versioning diffs. You are hashing trees.

This is a convenience fact that becomes a load-bearing one in a governed system. When you add a row to CANON.md and commit, the governance declaration and every derived artifact (the *.generated.js mirror, the Python SimpleNamespace, the TypeScript as const) are all tree objects in the same snapshot. The governance tree and the git tree are the same structure. The CANON row is literally high in the tree — in the repository path, SERVICES/ and canonic-canonic/FOUNDATION/ sit near the root of the working tree, and every leaf they govern — every worker endpoint, every Next.js component that reads a generated constant, every verify gate that imports a frozenset — hangs below them.

When a developer says "I'm going to add a new event type," the question is not whether they are going to write code. They are going to write code. The question is at what altitude they are going to write it.

The altitude test

A governed event type — say, LAUDE_AXIOM_EXTRACTED — requires three things: a declaration, consumers, and enforcement. The altitude test ranks them in order of compressive power:

A row in a CANON.md table is the highest-altitude move. One line. The gen-ledger-types codegen phase reads it and emits LEDGER_EVENTS.generated.js, LEDGER_EVENTS.generated.ts, and LEDGER_EVENTS.generated.py — three consumers updated automatically. The duplication linter now fails any file that hardcodes the string. Any future mirror (a Swift SDK, a Go client, a CLI autocomplete) gets the new event on next build by reading the same table. The governance tree absorbed the fact. Total hand-maintained lines: one.

A constant in a module is a mid-altitude move. One declaration, one file. Consumers can import it. But: the Python mirror needs a separate edit, the TypeScript mirror needs a separate edit, the linter does not know this is a governed value and cannot enforce it. Every mirror is a manual dependency. The compressive ratio is 1:1 at best. Total hand-maintained lines: one per language.

A hardcoded string in a function is the lowest-altitude move. Zero compressive power. No enforcement, no discovery, no mirror. The function contains a governed value that governance does not know about. If the value changes, the function changes in isolation. The next developer who needs the same value will write it again — the duplication linter finds this, and its report is a measurement of how much low-altitude development has accumulated. Total hand-maintained lines: unbounded.

The altitude test is not about elegance. It is about the ratio of declared facts to maintained lines. The higher the altitude, the more the compiler handles; the lower, the more the human carries. Every developer who reaches for a constant when they could reach for a CANON row has developed lower than the tree allows.

What high looks like in practice

The session that shipped the SALUTEM provider registry chose altitude at every decision point. The care-need types — dialysis, memory_care, pediatric_consult — could have been string literals in the resolver's switch statement. They were a table in SERVICES/SALUTEM/CANON.md. The provider attestation states — VETTED, PENDING, ESCALATED — could have been an enum in salutem.js. They were rows in the CANON. The ledger event types — SALUTEM_NEED_RESOLVED, SALUTEM_GAP_ESCALATED — could have been imported from a local constant. They were added to the governed § Event Types table, one row each, and the codegen built all three language mirrors on the next build.

The resulting salutem.js worker holds almost no governed values. It holds mechanism — the fold logic, the fallback traversal, the escalation guard. The governed facts live in the CANON table four levels up the directory tree, at a node the compiler discovers via BFS. The worker is short not because it was refactored but because it was written at the right altitude from the start.

This is what it feels like to develop high on the trees. You write less code. You write it in a place where the compiler can find it. The first test of any new value you need is not "what type should this be?" but "does the tree already know this?" If it does, you reference the compiled output. If it does not, you add a row — and in adding the row, you teach the tree something it will remember for every consumer, forever, across every language you will ever need.

The smell of the low canopy

There are patterns that signal a developer has slipped below their optimal altitude. Not failures — all of them work — but signals that the work could have been done higher:

A runtime check for a governed value (if event_type not in VALID_EVENTS) is a sign the linter should have enforced it at build time. The check is defensive; the gate is architectural.

Parallel migration logic (if legacy_field else new_field) is often a sign that one of the field names should have been declared in a CANON table and the migration written as a single governed rename that propagated everywhere.

A function that returns a magic number (TIER_WEIGHTS = {1: 0.25, 2: 0.5, ...}) is almost always a CANON table waiting to be born. The function is carrying governed facts that the function's callers will have to rediscover.

A comment that says "keep in sync with X" is proof that X and the commented thing should have been the same CANON source emitting both. Comments do not enforce sync. Codegen does.

None of these patterns are bugs. They are altitude — and altitude is correctable by climbing.

Why the crown is not obvious

If developing high is strictly more compressive, why does low-altitude code accumulate? Two reasons.

The first is legibility. A function is locally readable. Its logic is visible at the call site. A CANON row requires knowing that a codegen phase reads it, that a linter enforces it, that a verifier closes the gate — none of which are visible at the call site. A developer who has not internalized the compilation model reaches for the function because the function is visible and the governance is implicit. This is the cost of an underdocumented compiler: its power is invisible to the developer who would benefit most from using it.

The second is false proximity. It feels closer to the problem to write code near the problem. The dialysis need resolver lives in salutem.js, so the care-need types feel like they belong in salutem.js. Moving them to SERVICES/SALUTEM/CANON.md feels like indirection — further from the code that uses them. This intuition is exactly right about locality and exactly wrong about architecture. The CANON.md is not an indirection. It is the fact. The salutem.js usage is the reference. Indirection runs the other way: a hardcoded string in salutem.js is an indirection away from the governed fact, because it has no address the compiler can check.

The fix for both problems is the same: make the compiler's model explicit. Name the altitude levels. Show that CANON.md → codegen → generated.js → consumer is not a longer path — it is a shorter one, because the path from a CANON row to every consumer is compiler-traversed, not developer-maintained.

The view from the crown

A developer who has climbed to the crown of the governance tree sees the system differently. Each addition is a question: does this belong in the tree, or in the mechanism? Each refactor is a measurement: how much hand-maintained code can be absorbed by adding one more CANON row? Each bug is a diagnostic: did this drift because a value was at the wrong altitude, because a consumer was not reading the compiled form, because a gate was not enforcing the constraint?

The root metric of a healthy governance compiler is not lines of code. It is flat mechanism, growing tree. The bin/ directory stays stable in size because no new logic is added to it — only new tables for the existing logic to read. The tree grows as governance grows. The expansion the compiler emits (generated mirrors, verified outputs, hydrated surfaces) grows proportionally. But the interpreter — the thing that does the reading and the enforcing — does not grow. It learns nothing new. The crown teaches it everything.

To develop high on the trees is to write less, delegate more to the compiler, and let the crown carry the facts. The tree remembers everything you teach it. The mechanism never needs to.


Primary publication: hadleylab.org/blogs. Content-hash-addressed and ledgered per the Canonic publication model.

Sources

Claim or named entity Body anchor Source URL
Git tree objects — git's core data model stores snapshots as DAGs of tree and blob nodes, not diffs §the tree is not a metaphor https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
Kolmogorov complexity — minimum-description-length as the bound on compressible information §altitude test / compressive ratio https://doi.org/10.1007/978-3-030-11927-3
Gamma et al. 1994 — Design Patterns (indirection, abstraction altitude) §false proximity https://en.wikipedia.org/wiki/Design_Patterns
Canonical post at hadleylab.org §primary publication https://hadleylab.org/blogs/2026-05-23-develop-high-on-the-trees/