Feature
Feature
Workspaces
A workspace is how a bridge binds together the pieces it needs to work on one thing — the config overlays it subscribes to and the member repos it clones into its working set — under one name. It sits above the overlay mechanism, adds a second membership dimension (code repos), and mirrors a small, tool-neutral identity into a shared registry so any conformant tool can answer "which project is this directory in?"
Ein Workspace ist die Art, wie eine Bridge die Teile bündelt, die sie für eine Sache braucht — die Config-Overlays, die sie abonniert, und die Member-Repos, die sie in ihr Arbeitsset klont — unter einem Namen. Er liegt über dem Overlay-Mechanismus, fügt eine zweite Mitgliedschafts-Dimension hinzu (Code-Repos) und spiegelt eine kleine, tool-neutrale Identität in eine geteilte Registry, sodass jedes konforme Tool beantworten kann: „In welchem Projekt liegt dieses Verzeichnis?"
A named binding of member repos + config overlays. Each workspace is one file — workflow/workspaces/<id>.yaml — read and mutated by a standalone engine, with a generated lock recording the resolved reality of its code-member clones.
Eine benannte Bindung von Member-Repos + Config-Overlays. Jeder Workspace ist eine Datei — workflow/workspaces/<id>.yaml — von einer eigenständigen Engine gelesen und verändert, mit einem generierten Lock, das die aufgelöste Realität seiner Code-Member-Klone festhält.
Read the full workspaces guide →Den vollständigen Workspaces-Guide lesen →
The core split
Die zentrale Trennung
Identity vs. materialization
Identität vs. Materialisierung
A workspace has two separable halves. Identity is which project this is — its name, the directories it roots at, and its git remotes. A set of projects is a fact about the machine, not about one repo or one tool, so identity is shared and lives in a tool-neutral registry outside any single tool. Materialization is how one instance realises the workspace locally — the code clones, the lock, and the exclude guard. Those are implementation details of a single instance, so that half stays repo-local.
Ein Workspace hat zwei trennbare Hälften. Identität ist, welches Projekt dies ist — sein Name, die Verzeichnisse, an denen er wurzelt, und seine Git-Remotes. Welche Projekte es gibt, ist eine Tatsache über die Maschine, nicht über ein Repo oder ein Tool — also ist Identität geteilt und lebt in einer tool-neutralen Registry außerhalb jedes einzelnen Tools. Materialisierung ist, wie eine Instanz den Workspace lokal umsetzt — die Code-Klone, das Lock und der Exclude-Schutz. Das sind Implementierungsdetails einer einzelnen Instanz, also bleibt diese Hälfte repo-lokal.
Shared identity registry
Geteilte Identitäts-Registry
A single JSON file at $WORKSPACES_DIR/workspaces.json (else ~/.workspaces/workspaces.json), schema version: 2. It records name, directories, and git remotes so several conformant co-writers agree on project identity. This bridge is a first-class writer, not just a reader — it registers a workspace's identity itself, keyed by an instance-qualified id so two instances that share a slug never clobber each other.
Eine einzelne JSON-Datei unter $WORKSPACES_DIR/workspaces.json (sonst ~/.workspaces/workspaces.json), Schema version: 2. Sie erfasst Name, Verzeichnisse und Git-Remotes, sodass mehrere konforme Co-Writer sich über die Projekt-Identität einig sind. Diese Bridge ist ein vollwertiger Writer, nicht nur ein Reader — sie registriert die Identität eines Workspaces selbst, geschlüsselt über eine instanz-qualifizierte ID, sodass zwei Instanzen mit gleichem Slug einander nie überschreiben.
Repo-local materialization
Repo-lokale Materialisierung
A generated workspaces.lock.yaml pins each role: code member to its resolved commit. The clones live under an ignored .bridge/workspaces/<id>/ tree, and a marked block in the local, untracked .git/info/exclude keeps them out of git — so even a public fork can never git add -A-publish foreign code. The tracked .gitignore is never touched.
Ein generiertes workspaces.lock.yaml pinnt jedes role: code-Member auf seinen aufgelösten Commit. Die Klone liegen unter einem ignorierten .bridge/workspaces/<id>/-Baum, und ein markierter Block in der lokalen, nicht versionierten .git/info/exclude hält sie aus Git heraus — sodass selbst ein öffentlicher Fork fremden Code nie per git add -A veröffentlichen kann. Die versionierte .gitignore wird nie angefasst.
When to use it
Wann man es nutzt
Five ways a workspace earns its keep
Fünf Situationen, in denen ein Workspace sich lohnt
A workspace is worth the file whenever you keep re-assembling the same set of repos and config to work on one thing. Five concrete cases:
Ein Workspace ist die Datei wert, sobald du immer wieder dieselbe Menge an Repos und Config zusammensetzt, um an einer Sache zu arbeiten. Fünf konkrete Fälle:
Client / engagement bundle
Kunden- / Auftragsbündel
One workspace per client: the client's repos as role: code members plus your org config overlay as a role: config member. Picking the engagement back up means the definition already knows the working set — which repos, which overlay, all under one name.
Ein Workspace pro Kunde: die Repos des Kunden als role: code-Member plus dein Org-Config-Overlay als role: config-Member. Den Auftrag wieder aufzunehmen heißt: die Definition kennt das Arbeitsset bereits — welche Repos, welches Overlay, alles unter einem Namen.
Caveat: a role: config member delegates to the repo-wide overlay engine. Overlay materialization is per-repo, not isolated per workspace — subscribing an overlay through a workspace still writes the one instance-wide overlay state, so there is no per-workspace config sandbox.
Vorbehalt: ein role: config-Member delegiert an die repo-weite Overlay-Engine. Die Overlay-Materialisierung erfolgt pro Repo, nicht isoliert pro Workspace — ein Overlay über einen Workspace zu abonnieren schreibt weiterhin den einen instanzweiten Overlay-Zustand, es gibt also keine Config-Sandbox pro Workspace.
Multi-repo feature work
Multi-Repo-Feature-Arbeit
A change that spans several services: subscribe the repos into one workspace, work across them, and let status show per-clone drift against the pinned resolved_sha — so you see at a glance which clone has moved.
Eine Änderung über mehrere Services hinweg: abonniere die Repos in einen Workspace, arbeite quer über sie und lass status die Drift pro Klon gegen den gepinnten resolved_sha zeigen — so siehst du auf einen Blick, welcher Klon sich bewegt hat.
Safe evaluation of third-party code
Sichere Prüfung von Fremdcode
Clones land under the gitignored .bridge/ tree, guarded by a marked block in .git/info/exclude. Even on a public fork, a careless git add -A cannot publish the foreign code — the exclude block is armed before the clone is recorded.
Klone landen unter dem gitignorierten .bridge/-Baum, abgesichert durch einen markierten Block in .git/info/exclude. Selbst auf einem öffentlichen Fork kann ein unbedachtes git add -A den Fremdcode nicht veröffentlichen — der Exclude-Block ist scharf, bevor der Klon überhaupt vermerkt wird.
One machine, many tools
Eine Maschine, viele Tools
The shared identity registry (~/.workspaces/workspaces.json, schema version: 2, multi-writer) means any conformant tool on the machine sees the same project identities. This bridge is a full co-writer, not just a reader — it registers identity itself and leaves every other tool's slice untouched.
Die geteilte Identitäts-Registry (~/.workspaces/workspaces.json, Schema version: 2, Multi-Writer) sorgt dafür, dass jedes konforme Tool auf der Maschine dieselben Projekt-Identitäten sieht. Diese Bridge ist ein vollwertiger Co-Writer, nicht nur ein Reader — sie registriert Identität selbst und lässt den Slice jedes anderen Tools unangetastet.
Rebuild a working set on a new machine
Ein Arbeitsset auf einer neuen Maschine wiederaufbauen
A workspace definition is one small YAML file, so the committed definition already names every member. But subscribe is idempotent: with the definition present it finds the existing entry and returns "already a member — no change" without cloning, so it does not restore a missing clone on its own. status reports each absent member as missing; to rebuild a clone, unsubscribe then subscribe it again (a future materialize/sync verb will fold this into one step).
Eine Workspace-Definition ist eine kleine YAML-Datei — die versionierte Definition benennt also bereits jeden Member. Aber subscribe ist idempotent: liegt die Definition vor, findet es den vorhandenen Eintrag und meldet „already a member — no change" ohne zu klonen, stellt einen fehlenden Klon also nicht von selbst wieder her. status meldet jeden fehlenden Member als missing; um einen Klon wiederaufzubauen, den Member per unsubscribe entfernen und erneut subscribe-n (ein künftiges materialize/sync-Verb wird das in einen Schritt fassen).
Command surface
Befehlsoberfläche
The verbs
Die Verben
Drive the engine directly with six verbs. The mutating verbs (create, subscribe, unsubscribe) refuse to run unless the current branch is user/*, so a CORE branch never carries instance state; the read-only verbs run anywhere.
Steuere die Engine direkt mit sechs Verben. Die verändernden Verben (create, subscribe, unsubscribe) verweigern die Ausführung, sofern der aktuelle Branch nicht user/* ist — so trägt ein CORE-Branch nie Instanz-Zustand; die lesenden Verben laufen überall.
| VerbVerb | What it doesWas es tut |
|---|---|
create |
Scaffold a new workflow/workspaces/<id>.yaml from the template — id, title, timestamps, empty overlays and repos. Refuses an invalid slug or an existing file.
Legt eine neue workflow/workspaces/<id>.yaml aus dem Template an — ID, Titel, Zeitstempel, leere Overlays und Repos. Verweigert einen ungültigen Slug oder eine bereits existierende Datei.
|
subscribe |
Add a member. A role: code member is cloned into the ignored .bridge/ tree and pinned in the lock; a role: config member is normalized and delegated to the overlay engine. Idempotent on the same url+ref.
Fügt ein Member hinzu. Ein role: code-Member wird in den ignorierten .bridge/-Baum geklont und im Lock gepinnt; ein role: config-Member wird normalisiert und an die Overlay-Engine delegiert. Idempotent bei gleicher url+ref.
|
unsubscribe | Remove a code member (delete the clone, drop the lock entry, refresh the exclude block) or a config overlay (delegate the removal) — and shrink the shared-registry mirror. Entfernt ein Code-Member (löscht den Klon, streicht den Lock-Eintrag, baut den Exclude-Block neu) oder ein Config-Overlay (delegiert die Entfernung) — und verkleinert den Spiegel in der geteilten Registry. |
list | Read-only table of workspaces and their code / overlay counts. Nur-Lese-Tabelle der Workspaces mit ihren Code- / Overlay-Zählungen. |
status |
Read-only drift report: for each code member whether the clone is present and matches the lock (clean / ahead / missing / unpinned, where ahead is any HEAD≠pinned-SHA divergence); overlays listed by name.
Nur-Lese-Drift-Bericht: für jedes Code-Member, ob der Klon vorhanden ist und zum Lock passt (clean / ahead / missing / unpinned, wobei ahead jede Abweichung HEAD≠gepinnte-SHA meint); Overlays namentlich aufgeführt.
|
validate | Validate one or all definitions against the schema, preferring an external validator when available with an in-engine fallback otherwise. Validiert eine oder alle Definitionen gegen das Schema — bevorzugt einen externen Validator, falls vorhanden, sonst mit engine-internem Fallback. |
Quickstart
Schnellstart
Three verbs, start to drift-check
Drei Verben, von Anlegen bis Drift-Check
Drive the engine straight from the shell: create the definition, subscribe its members, then ask for a drift report. The mutating verbs need a user/* branch, and nothing here reaches the network until an actual clone.
Steuere die Engine direkt aus der Shell: lege die Definition an, abonniere ihre Member und fordere dann einen Drift-Bericht an. Die verändernden Verben brauchen einen user/*-Branch, und nichts hier geht ins Netz, bevor tatsächlich geklont wird.
# 1 · create the definition (writes workflow/workspaces/client-peppol.yaml) $ python3 scripts/workspace.py create client-peppol \ --title "Client Peppol rollout" # 2 · subscribe members — code repos, plus one config overlay $ python3 scripts/workspace.py subscribe client-peppol \ https://example.org/outbound-service.git --role code $ python3 scripts/workspace.py subscribe client-peppol \ https://example.org/org-config.git --role config # 3 · per-clone drift against the pinned commit $ python3 scripts/workspace.py status client-peppol
The --role config line delegates to the shared overlay engine — overlay state stays instance-wide, never per-workspace (see Limits).
Die --role config-Zeile delegiert an die geteilte Overlay-Engine — der Overlay-Zustand bleibt instanzweit, nie pro Workspace (siehe Grenzen).
No dependency
Keine Abhängigkeit
Standalone by design
Eigenständig by Design
The workspace engine is standalone. Every verb works end-to-end on a bare bridge that has never heard of any external tool — no import, no shelling out to a foreign binary, no path assumption. The only provider hooks in the whole design are two inert seams: an extension bag on the schema that CORE never reads, and a name-resolution seam on subscribe.
Die Workspace-Engine ist eigenständig. Jedes Verb funktioniert Ende zu Ende auf einer nackten Bridge, die nie von einem externen Tool gehört hat — kein Import, kein Aufruf eines fremden Binaries, keine Pfad-Annahme. Die einzigen Provider-Hooks im ganzen Design sind zwei inerte Nahtstellen: eine Erweiterungs-Tasche am Schema, die CORE nie liest, und eine Namensauflösungs-Nahtstelle an subscribe.
If subscribe is given a bare name instead of a git URL, resolving that name to a set of repos would need an optional external provider. On a standalone bridge none is present, so the engine prints a clear message and exits 3 — performing no import and no provider path lookup. A future knowledge/workspace tool may attach through the seam later, because both sides read the same open, versioned schema; nothing here waits on it, and nothing breaks without it.
Bekommt subscribe einen bloßen Namen statt einer Git-URL, bräuchte das Auflösen dieses Namens zu einer Menge von Repos einen optionalen externen Provider. Auf einer eigenständigen Bridge ist keiner vorhanden — also gibt die Engine eine klare Meldung aus und beendet mit 3, ganz ohne Import und ohne Provider-Pfad-Lookup. Ein künftiges Knowledge-/Workspace-Tool kann später über die Nahtstelle andocken, weil beide Seiten dasselbe offene, versionierte Schema lesen; nichts hier wartet darauf, und nichts bricht ohne es.
Correctness
Korrektheit
Multi-writer safety
Multi-Writer-Sicherheit
Because other conformant tools write the same workspaces.json, every mutation is safety-critical, so the writer follows one protocol: take an advisory lock, read, modify in memory, and atomically replace the file (temp + rename). It preserves unknown fields — unrecognized top-level and per-workspace keys round-trip untouched, and it only ever edits the shared identity fields plus its own extension slice, never another tool's. And it is fail-closed: an unparseable file, or a file newer than it understands, refuses the write rather than guessing — the on-disk bytes are left exactly as found. Only a genuine older file rotates, loudly, to a timestamped backup before a fresh registry is started. The advisory lock coordinates cooperating writers; the repo-local definition and lock stay authoritative, so a transiently-wrong shared mirror re-converges on the next local mutation.
Weil andere konforme Tools dieselbe workspaces.json schreiben, ist jede Mutation sicherheitskritisch — also folgt der Writer einem Protokoll: einen advisory Lock nehmen, lesen, im Speicher ändern und die Datei atomar ersetzen (Temp + Rename). Er bewahrt unbekannte Felder — unbekannte Top-Level- und Pro-Workspace-Schlüssel überstehen den Roundtrip unangetastet, und er editiert nur die geteilten Identitätsfelder plus seinen eigenen Erweiterungs-Slice, nie den eines anderen Tools. Und er ist fail-closed: eine unlesbare Datei oder eine Datei neuer als er versteht verweigert den Schreibvorgang, statt zu raten — die Bytes auf der Platte bleiben exakt wie vorgefunden. Nur eine echt ältere Datei rotiert, laut, in ein zeitgestempeltes Backup, bevor eine frische Registry begonnen wird. Der advisory Lock koordiniert kooperierende Writer; die repo-lokale Definition und das Lock bleiben maßgeblich, sodass ein vorübergehend falscher geteilter Spiegel bei der nächsten lokalen Mutation wieder konvergiert.
Honest limits
Ehrliche Grenzen
What it does not promise
Was es nicht verspricht
The feature is built carefully, and it is documented just as carefully. Three limits to know before you lean on it:
Das Feature ist sorgfältig gebaut — und ebenso sorgfältig dokumentiert. Drei Grenzen, die man kennen sollte, bevor man sich darauf verlässt:
The lock is advisory
Der Lock ist advisory
It coordinates only writers that take it. A co-writer that bypasses the lock — or a long-running tool holding stale in-memory state — can still clobber a mirror row between two of our writes. The next local mutation repairs it, but the window is real, not theoretical.
Er koordiniert nur Writer, die ihn nehmen. Ein Co-Writer, der den Lock umgeht — oder ein langlaufendes Tool mit veraltetem In-Memory-Zustand — kann zwischen zwei unserer Schreibvorgänge eine Spiegelzeile überschreiben. Die nächste lokale Mutation repariert das, aber das Fenster ist real, nicht theoretisch.
Convergence is attempted, not guaranteed
Konvergenz wird versucht, nicht garantiert
If two rows both structurally match a workspace, the writer mints a second row rather than guessing; if the only match already carries another tool's slice, it is never adopted. Both cases need a cross-tool reconciler that is not yet built.
Passen zwei Zeilen strukturell zu einem Workspace, legt der Writer eine zweite Zeile an, statt zu raten; trägt die einzige Übereinstimmung bereits den Slice eines anderen Tools, wird sie nie übernommen. Beide Fälle brauchen einen tool-übergreifenden Reconciler, der noch nicht gebaut ist.
No per-workspace config isolation
Keine Config-Isolation pro Workspace
Config overlays are materialized by the single shared overlay engine into the instance-wide overlay state. A workspace only indexes overlay names — it is never a second materializer, so there is no per-workspace config namespace or sandbox.
Config-Overlays werden von der einen geteilten Overlay-Engine in den instanzweiten Overlay-Zustand materialisiert. Ein Workspace indexiert nur Overlay-Namen — er ist nie ein zweiter Materialisierer, es gibt also keinen Config-Namespace und keine Sandbox pro Workspace.
Go deeper
Tiefer einsteigen