Bridge Extension Model

August 14, 2026 · View on GitHub

The Bridge has two layers: a CORE platform (generic, on main) and USER extensions (org-specific, on user/* branches). This document formalizes that split, names where each routing domain lives, and prepares for future Plugin extraction.

Option C — Current Model (USER Branch = Extension)

Everything on your user/{name} branch that is NOT on main IS your extension. This is the working model.

What lives where

LayerBranchPurposeExample
COREmainGeneric platform, shareableSkills, templates, schemas, examples, docs
Extensionuser/{name}Org-specific configs, agents, dataProject configs, contexts, personas, coordinator

Extension inventory (Org, post-C-prime)

CategoryPathScopeWhat it contains
Project Configsworkflow/projects/<slug>.yamlUSER · promote-on-demandTracker project configs (GitHub Projects V2 / ADO Boards) — field values, governance, health checks
Sub-Agents.claude/agents/customer-a-*.md, network-reconcile.mdscope: orgCustomerA engagement specialists
Coordinator Skillskills/customer-a-coordinator/scope: orgEnd-to-end orchestrator with references + playbooks + domain knowledge
Doc Systemskills/doc-system/scope: coreGeneric document-intake skill — reads workflow/contexts/doc-system.yaml (the context file stays scope: org) and runs the document-intake flow
Routing Contextsworkflow/contexts/<id>.yamlUSER (gitignored — PII)Per-domain routing rules (see Routing Map below)
Personasidentity/personas/<id>.yamlUSER (gitignored — PII)Self-identities (tax data, signatures, destination paths)
Mandantsidentity/mandants/<id>.yamlUSER (gitignored — PII)Recipient groups for outbound messages
Calendarworkflow/calendars/entries.yamlUSERScheduled outbound actions with recipient refs
Rulesrules/*.md (core), rules/org/** (org), rules/user/** (user)tiered by FOLDEROperating rules tiered by folder — top-level rules/*.md is scope: core, rules/org/** is scope: org, rules/user/** is scope: user (personal rules, e.g. for a private pipeline)
Standing Ordersprotocols/standing-orders/<name>.md (CORE defaults), standing-orders/user/<name>.md (USER)CORE / USER · cross-cuttingAlways-on advisory/blocking rules (NOT routing — see Routing Map)
Remotesinfra/remotes/<name>.yaml (+-setup.md)USER (PII clean)Machine inventory + Wake-on-LAN + SSH config
Channelsinfra/channels/<name>.yamlUSEROutbound transport definitions
Backupsinfra/backups/topology.yaml + _state.yamlUSERSource × Target × Pipeline topology
Workwork/USERLogs, board, active/ongoing/done tasks
Configbridge-config.yamlUSER · gitignoredTheme, language, features, identity-block, integrations
Bridge-Deck Configbridge-deck.config.yamlUSERDaemon collector paths for the Pixel-Art Visualizer
Ecosystemecosystem.yamlUSERRepo registry — created at onboarding, gitignored (absent on a fresh clone)

How it works today

  1. Clone open-bridge → get CORE platform on main (templates, schemas, skills — you create ecosystem.yaml at onboarding)
  2. Create user/{name} branch → start your extension
  3. Run /bridge-onboard → creates bridge-config.yaml, work system, optionally first persona/mandant/context from templates
  4. Add project configs, sub-agents, contexts as needed
  5. git merge main → pull CORE updates without conflicts (paths don't overlap by design — see CLAUDE.md "Git & Branches")

Sharing within a team

For a colleague to pick up Org extension content:

  1. Clone open-bridge
  2. git checkout -b user/their-name from main
  3. Cherry-pick or copy specific files from user/user:
    • workflow/projects/<slug>.yaml (Org project configs)
    • .claude/agents/customer-a-*.md, .claude/agents/network-reconcile.md (CustomerA sub-agents, scope: org)
    • skills/customer-a-coordinator/ (Org-scoped skill)
    • Cross-cutting standing orders that apply (e.g. code-standards.md, security-baseline.md, document-work.md)
  4. Create their own bridge-config.yaml (gitignored) and persona/mandant/context files

Personas, mandants, contexts, calendar entries are NEVER shared cross-user (PII-by-construction).


Routing Map

The Bridge has multiple routing domains — distinct kinds of "input → destination" decisions. Each lives in exactly one place. No routing belongs in protocols/standing-orders/ — those are cross-cutting always-on rules, not routing.

Per-domain routing table

Routing domainSource of truthSchema / patternResolved by
Documents (PDFs, scans, downloads)workflow/contexts/doc-system.yamlsources[], areas{}, rules[] with when: predicates and route: targetsdoc-system skill at /doc-inbox
Mail attachmentssame — workflow/contexts/doc-system.yaml, intake_sources.mail[]account-references plus the same routing rulesdoc-system skill (mail-source pickers); an org overlay can add dedicated mail-attachment-processor / outlook-attachment-processor skills (scope: org)
Outbound messages (calendar-driven)workflow/calendars/entries.yaml recipients[]each entry references mandant/person pairscalendar skill, bridge-deck Calendar tab
Recipient groups (who to address)identity/mandants/<id>.yamlpersons[] with channels per person/mandants, calendar entries, message composers
Channel selection (which transport)infra/channels/<name>.yamltype, runtime.host, credentials-refchannel skill (an org overlay can add transport-specific skills such as an email-manager, scope: org)
Persona destinations (filing paths per identity)identity/personas/<id>.yaml.destinationskey→path map, variable-interpolatedreferenced by name from context routing rules
Tracker / Issue dispatchworkflow/projects/<slug>.yamlfield values, governance rules, state mappingsgithub-projects-manager, project-advisor
Sub-repo / context taggingskills/org-context/SKILL.md (org overlay)tag-tableorg-context skill — an org-overlay addition (scope: org), always-active when present, not shipped in open-bridge
Cross-cutting always-on rules (not routing — listed here for contrast)protocols/standing-orders/<name>.mdscope: always, enforcement: advisory/blockingsession-start of every Bridge session

Routing-context naming convention

workflow/contexts/<domain>.yaml — one file per routing domain.

Examples (existing or plausible):

  • context.doc-system.yaml — document filing (active)
  • context.invoices.yaml — invoice generation/filing (future)
  • context.contracts.yaml — contract handling (future)
  • context.mail-triage.yaml — incoming-mail classification (future)

The contract:

  • persona_ref: <id> — which identity this routing operates under
  • sources[] — where input arrives (filesystem paths, mail accounts, ...)
  • rules[]when: { ... } predicates → route: { target: <destination-key> }
  • areas{} — destination tree structure (path + per-area metadata)
  • destination-key resolution: first the context's own destinations: block, then fallback to personas/<persona_ref>.yaml.destinations

Sub-routing companion files

Inside the destination tree, an _INFO.md per area can carry human-friendly notes (history, edge cases, area conventions). The hard rule:

Conflict between workflow/contexts/<id>.yaml and a destination-tree _INFO.mdthe YAML wins. _INFO.md is supplementary doc, not routing source.

This is the same "machine-readable wins, human notes annotate" principle as infra/remotes/<name>.yaml vs infra/remotes/<name>-setup.md.

Why standing-orders are NOT routing

Standing orders solve a different problem: always-on rules that govern Claude's behavior across all sessions. They have no route: field. Examples:

  • code-standards.md — code quality guidelines (advisory)
  • document-work.md — log all significant actions to work log (blocking)
  • work-board-reconciliation.md — folder ↔ board coherence check (advisory)
  • security-baseline.md — security practices (advisory)

If you find yourself wanting to put route: <X> into a standing order, you want a routing context, not a standing order. Create workflow/contexts/<your-domain>.yaml.


Downstream materialization (org overlays)

The extension inventory above is the publish side: /promote reads each file's tier from where it lives (scripts/categorize-commits.py) and routes scope:org content up to your org overlay, scope:core to open-bridge, scope:user nowhere. Org overlays are the downstream inverse: they answer how a teammate's fresh clone gets that scope:org content back — without cloning the whole seed and without the org content ever leaking into the OSS CORE.

The /overlay skill (bridge-overlay, engine scripts/overlay.py) subscribes a consumer Bridge to one or more overlay repos and materializes their files into the live tree as copies, each pinned to an immutable git SHA. By default those copies are excluded from the consumer's own git (see org-overlays.md § Git tracking of managed dests for the opt-in switch and where the backup actually lives). Subscription state lives in two USER-tier root files — a generated overlays.lock.yaml (per-file source/materialized hashes, the drift detector) and a sparse .bridge/ cache — both gitignored in a public fork. Each subscription is a role: org-overlay entry in bridge-config.yaml.upstreams[] carrying its own materialize: block; an instance opts in via infra/instances/<slug>.yaml subscribes_overlays:. The same classifier and scope tripwire run in both directions, so a file /promote routes to the org overlay is exactly the file /overlay pulls back — and exactly the file both refuse to let reach open-bridge. Full guide: docs/org-overlays.md.


Option B — Future Plugin Extraction

When the Org extension needs to be shared as a proper package, extract it into a Claude Code Plugin at <your-org>/bridge-org-extension.

Plugin structure (C-prime aligned)

bridge-org-extension/
├── plugin.json                          # Claude Code plugin manifest
├── README.md                            # What this extension adds

├── .claude/
│   └── agents/                          # Sub-agents (scope: org)
│       ├── customer-a-log-analyst.md
│       ├── customer-a-incident-handler.md
│       ├── customer-a-deployment-verifier.md
│       └── network-reconcile.md

├── skills/                              # Org-specific skills (scope: org)
│   └── customer-a-coordinator/           # SKILL.md + references/ + playbooks/
│   # (doc-system/ is scope: core — ships in the base bridge, not this extension)

├── workflow/                            # Routing + project configs (templates)
│   ├── project.customer-a.yaml.template
│   ├── project.org-ops.yaml.template
│   ├── context.doc-system.yaml.template
│   └── ...

└── protocols/
    └── standing-orders/                 # ONLY cross-cutting always-on rules
        ├── code-standards.md
        └── security-baseline.md
        # (No routing-*.md — routing lives in workflow/contexts/<id>.yaml)

plugin.json

{
  "name": "bridge-org-extension",
  "version": "1.0.0",
  "description": "Org overlay for open-bridge — a customer coordinator, project configs, doc-system",
  "author": "<your-org>",
  "requires": {
    "open-bridge": ">=1.0.0"
  },
  "install": {
    "copy": [
      { "from": ".claude/agents/", "to": ".claude/agents/", "merge": true },
      { "from": "skills/", "to": "skills/", "merge": true },
      { "from": "workflow/", "to": "workflow/", "merge": true, "rename": { ".template": "" } },
      { "from": "protocols/standing-orders/", "to": "protocols/standing-orders/", "merge": true }
    ]
  }
}

Extraction checklist

When ready to extract from USER branch to Plugin:

  • Copy .claude/agents/customer-a-*.md + network-reconcile.md (all scope: org)
  • Copy skills/ entries with scope: org (customer-a-coordinator)
  • Copy workflow/projects/<slug>.yaml (the Org project configs)
  • Sanitize workflow/contexts/doc-system.yaml.template (strip personal paths, keep schema)
  • Copy cross-cutting standing orders (code-standards, security-baseline, document-work, etc.) — NOT routing
  • Create plugin.json manifest
  • Neutralize absolute paths (use ${CLAUDE_PLUGIN_ROOT} + ${onedrive_root} for script refs)
  • Run rules/promote-safety.md scan on all files (no secrets, no PII)
  • Test: fresh bridge clone + plugin install + /bridge-onboard → working system

What stays on USER branch (never in plugin)

  • bridge-config.yaml — personal identity, theme, language (gitignored anyway)
  • identity/personas/<id>.yaml — tax data, signatures (gitignored)
  • identity/mandants/<id>.yaml — recipient PII (gitignored)
  • workflow/contexts/<id>.yaml — concrete routing instances (gitignored — uses real paths)
  • workflow/calendars/entries.yaml — concrete schedule
  • infra/remotes/<name>.yaml — personal machine inventory
  • infra/channels/<name>.yaml — personal messaging configs
  • infra/backups/topology.yaml — personal backup pipelines
  • work/ — personal work log and tasks

Installation flow (future)

# From a fresh bridge clone:
cd open-bridge
git checkout -b user/alice

# Install Org extension:
claude plugins add <your-org>/bridge-org-extension

# Run onboard:
# /bridge-onboard detects context.doc-system.yaml.template, offers to instantiate
# /bridge-onboard detects customer-a-coordinator skill, registers it

Extension Design Principles

  1. CORE is self-sufficient — the bridge works without any extension
  2. Extensions are additive — they add templates, configs, scripts but never modify CORE files
  3. No secrets in extensions — credentials stay in KeyVault/1Password, extensions reference them by name
  4. Routing lives in workflow/contexts/<id>.yaml — never in standing-orders, never hardcoded in skills (one instance of principle 8 / § Generic CORE Skills)
  5. Persona destinations are key→path maps — referenced by name from routing rules, swappable per persona
  6. Project configs are the contractworkflow/projects/<slug>.yaml defines tracker field values and governance
  7. Standing orders are always-on rules — they govern Claude's behavior, not where things go
  8. CORE skills are config-driven — a scope: core skill stays generic; instance-specific configuration and workflow live in USER/config files (bridge-config.yaml, workflow/, infra/, identity/, state/snapshots). Skills read config; they never embed instance logic, hardcoded queries, org/project IDs, personas, or thresholds. See § Generic CORE Skills below.

Generic CORE Skills — the one "never hardcode" principle

Principles 4, 6, and 8 are the same rule from three angles: a CORE file or skill carries the generic mechanism; the instance carries the data. The repo states this per domain in several places — these are all instances of this single principle, not separate rules:

DomainPer-domain statementStated in
Tracker fieldsworkflow/projects/<slug>.yaml is the contract — never hardcode field valuesAGENTS.md § Project Registry, rules/knowledge-growth.md
Routingrouting lives in workflow/contexts/<id>.yaml — never hardcoded in skillsprinciple 4 above
Backupstopology.yaml is the truth — never hardcode pathsCLAUDE.md § Backups, infra/backups/README.md
Tracker queriesbriefing reads integrations.{name}.* from bridge-config.yaml — not hardcodedskills/briefing/
Skill editsedit the seed skill, never a downstream overlay copydocs/skill-distribution-architecture.md

Why it's load-bearing: a scope: core skill is shipped and merged upstream. The moment instance logic — an ADO pipeline ID, a customer query, a persona, a render block — lands inside SKILL.md or references/, the skill stops being generic: it breaks upstream-mergeability and leaks instance shape into OSS CORE. The fix is always the same — move the data to bridge-config.yaml (or the matching workflow/ / infra/ / identity/ file) and have the skill read it. If no config key exists yet, add the key plus a template default first, then read it.

The test before adding anything to a CORE skill: would this line be wrong in someone else's Bridge? If yes, it is instance config, not skill content — it belongs in bridge-config.yaml / a USER file, and the skill reads it.

Operating-manual summary (auto-loaded at session start): CLAUDE.md § Generic CORE Skills.