Branch Universality

Architecture 02 (forkability) drew a line between system (portable) and data (instance-specific). Architecture 26 (domain branches) subdivided data into independently selectable bundles — personal,…

Architecture 02 (forkability) drew a line between system (portable) and data (instance-specific). Architecture 26 (domain branches) subdivided data into independently selectable bundles — personal, work, reference. This document argues that the system/data distinction was an approximation. The real architecture is simpler: everything lives on a branch. The "system layer" is the root branch.


#The Problem with Two Layers

Architecture 02's two layers — system (skills, patterns, ontology) and data (projects, databases) — assumed that skills are always portable and data is always instance-specific. Neither holds.

Skills that aren't portable:

  • /signal requires a linked phone number and a running bridge daemon. It belongs to the personal branch.
  • /email requires Google OAuth for a specific account. It could belong to personal, work, or both.
  • /music requires music.db populated from a personal collection. Personal branch.
  • /papers requires papers.db and optionally s2ag.db. Work + reference:academic.

These skills don't work in a fresh fork. They depend on branch-specific databases, credentials, and services. They are not system-layer objects with data-layer dependencies — they are branch objects.

Patterns that aren't portable:

  • A lab's manuscript review workflow encodes institutional conventions (who reviews, what criteria, which journals). It belongs to the work branch for that institution.
  • A personal health-tracking pattern belongs to personal.
  • An investment analysis method belongs to personal/financial.

Databases that aren't just data:

  • entities.db is "core" — but its schema mappings (the Python functions that ingest sources) are currently in the system layer (scripts/build_entities.py). The mappings are branch-specific: a WhatsApp mapping belongs to personal, a papers.db mapping belongs to work. The generic builder is system; the mappings are branch.

The system/data distinction conflates two independent things: generality (how widely applicable) and axis (what kind of thing). A skill can be general (system) or specific (branch). A database can be general (core) or specific (branch). The axis doesn't predict the layer.


#The Correction

Every object in the system lives on a branch. The branch determines its visibility, its policies, and which installations carry it. The root branch is what architecture 02 called the "system layer" — it's visible everywhere, inherited by all forks. But it's not special. It's just the highest branch.

The three axes map to the branch tree:

root (= "system layer")
├── domains/
│   ├── core/                    ← entities.db, vault.db, files.db
│   ├── personal/                ← contacts.db, gateway.db, health.db, personal.db
│   │   ├── personal/id          ← passport scans, CC photos
│   │   ├── personal/financial   ← taxes, accounts, retirement
│   │   ├── personal/health      ← health.db, wearable data
│   │   └── personal/properties  ← deeds, contracts, architecture plans
│   ├── work/                    ← google_work.db, papers.db, repos.db
│   │   └── work/mainen-lab      ← lab-specific projects, personas
│   └── reference/               ← s2ag.db, discogs.db, public-archives.db
│       ├── reference/academic
│       ├── reference/music
│       └── reference/investigative
├── methods/                     ← patterns, skills, architecture docs
│   ├── methods/core             ← /read, /write, /commit, /search — always present
│   ├── methods/comms            ← /signal, /email, /whatsapp — branch:personal
│   ├── methods/research         ← /papers, /lit-search, /review — branch:work
│   └── methods/music            ← /music — branch:personal
└── actors/                      ← personas
    ├── actors/core              ← system personas (reviewer, editor, librarian)
    └── actors/mainen-lab        ← lab-specific personas — branch:work

Every node is an entity. Visibility flows down. PRs flow up. The three rules from architecture 24 apply uniformly.


#What a Company Brings

A company forking HAAK adds a subtree:

root
├── domains/
│   └── company/acme             ← their domain branch
│       ├── acme/engineering      ← repos, CI/CD configs, deploy scripts
│       ├── acme/sales            ← CRM database, pipeline
│       └── acme/legal            ← contracts, compliance
├── methods/
│   └── methods/acme             ← their skills and patterns
│       ├── /acme-deploy          ← custom deploy skill
│       ├── /acme-invoice         ← billing workflow
│       └── acme-code-review      ← their review pattern (refines methods/core review)
└── actors/
    └── actors/acme              ← their personas
        ├── acme-engineer         ← capability profile
        └── acme-manager          ← capability profile

The company inherits everything from root: /read, /write, /commit, the ontology, the entity model, the branch machinery. They add their domain-specific databases, skills, patterns, and personas. Their employees' personal branches are siblings, not children — personal/alice doesn't belong to company/acme. Alice brings her personal branch independently.

The company's manifest.toml:

[install]
name = "haak-acme-prod"
host = "aws"

[branches]
core = true
company.acme = true
company.acme.engineering = true
company.acme.sales = true
reference = ["academic"]     # they do R&D

No personal branch on the production server. Alice's laptop manifest adds personal = true.


#Skills on Branches

A skill declares its branch affinity in its frontmatter:

---
name: signal
branch: personal          # only available when personal branch is active
requires:
  - contacts.db           # database dependency
  - gateway.db
  - com.haak.signal-bridge  # service dependency
---

The skill loader reads the manifest, checks which branches are active, and only loads skills whose branch is present. A skill on methods/core (no branch declaration) is always loaded. A skill on methods/comms with branch: personal is loaded only when the personal branch is active.

This replaces the current situation where all skills are loaded regardless of whether their dependencies exist, leading to confusing errors when someone tries /signal on a machine without a signal bridge.


#Databases on Branches

Architecture 26 already assigned branches to databases via vault.db. The extension: the schema mapping (the ingest function that populates entities.db from a source database) also lives on a branch. The generic builder is root-level. The mapping for music.db belongs to branch:personal. The mapping for papers.db belongs to branch:work.

# Schema mapping as a branch-aware entity
entity: mapping:whatsapp-ingest
  → branch:personal       (belongs to personal branch)
  → source:whatsapp.db    (maps this source)
  → method:schema-mapping (is a schema mapping)

When build_entities.py runs, it checks the manifest and only applies mappings for active branches. On Acme's production server, the WhatsApp mapping doesn't run because branch:personal isn't in the manifest.


#Policies on Branches

Each branch carries policies. Policies are inherited downward (architecture 24, rule 1):

BranchPolicy examples
rootOntology-first rule. 500-line limit. Index every folder.
personalEncrypt at rest. Backup to Filen. Never on institutional infra.
personal/idShare only within explicit situations. Alert 90 days before expiry.
personal/financialAnnual audit. Tax docs retained 10 years.
workInstitutional credentials. Backup to GCloud.
work/mainen-labLab meeting every Tuesday. Manuscripts through review pipeline.
company/acmeSOC2 compliance. 90-day retention. Legal hold capability.

A child branch inherits its parent's policies and can add constraints (never relax them — relaxation requires a PR upward). personal/id inherits "encrypt at rest" from personal and adds "share only within explicit situations."


#The Filesystem

The filesystem mirrors the domain subtree of the branch tree. Not the full tree — methods and actors don't need directory representation (they live in patterns/ and personas/ respectively, which are root-level). But domains do, because domains carry files.

projects/               ← the domain subtree
  personal/             ← branch:personal
    id/                 ← branch:personal/id
    financial/          ← branch:personal/financial
      taxes/            ← branch:personal/financial/taxes
      retirement/       ← branch:personal/financial/retirement
    health/             ← branch:personal/health
    properties/         ← branch:personal/properties
  mainen-lab/           ← branch:work/mainen-lab
  ai-ontology/          ← branch:work (or its own branch)

A file's branch is determined by its path. projects/personal/id/zach/CE946227.jpg lives on branch:personal/id. Its policies (encryption, backup, sharing) are inherited from that branch. No per-file policy needed — the tree handles it.


#Situations and Branches

Situations are not branches. A situation is a compound — a coming-together of actors, methods, and domains around materials (Definition 11). It happens within a domain branch. The passport renewal situation happens within branch:personal/id. The tax filing situation happens within branch:personal/financial/taxes.

But a situation can involve materials from multiple branches. The tax filing references bank statements (personal/financial/accounts), identity documents (personal/id), and work contracts (work). The situation doesn't own these files — it references them via materialization belongings. The files stay on their home branches.

Visibility rule: a situation can only reference materials on its own branch or ancestor branches. A situation on personal/financial/taxes can reference files on personal/financial (parent) and personal (grandparent), but not on work (sibling subtree). Cross-branch references require the material to be promoted upward (PR'd to a common ancestor) or the reference to be a weak link (a note saying "see also work/contracts/..." without a hard dependency).

This constraint may be too strict for personal documents — the tax filing genuinely needs the passport number from personal/id. The resolution: the entities.db is on branch:core, which is ancestor to both. The data (passport number as an entity attribute) is visible everywhere via the entity graph. The file (passport scan) stays on its branch. The situation references the entity, not the file. If it needs the file, the user authorizes cross-branch access per-situation — a policy decision, not an architectural one.


#What This Replaces

The system/data distinction from architecture 02 becomes a special case: system = root branch, data = everything else. The distinction isn't wrong — it's just the top level of a deeper structure. Architecture 26's branch manifest extends naturally: instead of listing only databases, it lists active branches, and everything (databases, skills, patterns, policies) follows from which branches are active.


architecture · 27 · branch-universality · 2026-03-15 · zach + claude

Architecture 27 — Branch Universality — 2026 — Zachary F. Mainen / HAAK