Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions changes/7k58-one-definition-many-repos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
type: added
facade: breaking
---
[en]
One persona definition for every repository on a machine. Beside a workspace's own
`.nxs-personas/`, every workspace now reads the user-level folder `~/.nexusflow/personas/` —
personas and a `channels.yaml`, laid out the same way — merged per name: a persona or channel the
repository declares itself hides the user-level one of the same name, everything else is added. One
`pm` written once runs in every repository as that repository's own participant, with its board,
memory and working copy. `nxc list` marks each entry that comes from the user-level folder and says
which ones the repository hides, and so does `nxs prime`; a persona from it is told at session start
where its declaration lives, because a path in its instructions relative to "this repository" means
the repository it runs in. The per-operation freeze holds for it as for the repository's own folder.
A user-level `external` admits callers only from workspaces the running repository itself trusts
by name. A development build reads a user-level folder only under a named service instance
(`~/.nexusflow-<name>/personas/`), never the production one. A machine without the folder behaves
as before. For embedding apps: `Engine::definitions`,
`Engine::directory` and `Engine::prime_as` read the same folder (an app cannot choose another);
new `user_path`, `from_user` and `shadowed` on `DeclarationSource`, `origin` on `PersonaEntry` and
`ChannelEntry`, `declared_in` on `PersonaBrief`, `user_declarations_dir`, `merged_channels`, and
`Definitions::resolve_with_user_dir` / `Definitions::from_source` — a struct literal of those four
types needs the new fields.
[de]
Eine Persona-Definition für jedes Repository auf einer Maschine. Neben dem eigenen `.nxs-personas/`
liest jeder Arbeitsbereich jetzt den Ordner auf Benutzerebene `~/.nexusflow/personas/` — Personas
und eine `channels.yaml`, genauso aufgebaut — und führt beide je Name zusammen: Eine Persona oder ein
Kanal, den das Repository selbst deklariert, verdeckt den gleichnamigen Eintrag auf Benutzerebene,
alles andere kommt hinzu. Ein einmal geschriebener `pm` läuft in jedem Repository als dessen eigener
Teilnehmer, mit dessen Board, Gedächtnis und Arbeitskopie. `nxc list` markiert jeden Eintrag aus dem
Ordner auf Benutzerebene und sagt, welche das Repository verdeckt, `nxs prime` ebenso; eine Persona
von dort erfährt beim Sitzungsstart, wo ihre Deklaration liegt, denn ein Pfad in ihren Anweisungen
relativ zu „diesem Repository" meint das Repository, in dem sie läuft. Das Einfrieren je Vorgang gilt
dort wie für den eigenen Ordner. Ein `external` auf Benutzerebene lässt Aufrufer nur aus Arbeitsbereichen
zu, denen das laufende Repository selbst beim Namen vertraut. Ein Entwicklungs-Build liest einen
Ordner auf Benutzerebene nur unter einer benannten Dienst-Instanz (`~/.nexusflow-<name>/personas/`),
nie den produktiven. Eine Maschine ohne den Ordner verhält sich wie bisher. Für einbettende
Apps: `Engine::definitions`, `Engine::directory` und `Engine::prime_as` lesen denselben Ordner (eine
App kann keinen anderen wählen); neu sind `user_path`, `from_user` und `shadowed` an
`DeclarationSource`, `origin` an `PersonaEntry` und `ChannelEntry`, `declared_in` an `PersonaBrief`,
`user_declarations_dir`, `merged_channels` sowie `Definitions::resolve_with_user_dir` / `Definitions::from_source` — ein Struct-Literal dieser
vier Typen braucht die neuen Felder.
20 changes: 20 additions & 0 deletions changes/dcpd-border-follow-ups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
type: fixed
facade: changed
---
[en]
Across a workspace border: when the caller's workspace stops trusting the receiver while a
commission is open, the answer no longer sits unread until the two-hour deadline — the commission is
canceled at once and the commissioner is woken with "the answer arrived from <workspace>, a
workspace no longer trusted here". A peer's handover that runs past its 60-second limit is now
stopped and reaped instead of being left running, so the background service no longer collects
stuck processes. The guide now says plainly that the depth cap across the border rests on the
trusted workspace's word.
[de]
Über die Grenze eines Arbeitsbereichs: Entzieht der Arbeitsbereich des Aufrufers dem Empfänger das
Vertrauen, während ein Auftrag offen ist, liegt die Antwort nicht mehr ungelesen bis zur
Zwei-Stunden-Frist — der Auftrag wird sofort abgebrochen und der Auftraggeber mit „the answer arrived
from <workspace>, a workspace no longer trusted here" geweckt. Eine Übergabe beim Nachbarn, die ihre
60-Sekunden-Grenze überschreitet, wird jetzt gestoppt und eingesammelt, statt weiterzulaufen; der
Hintergrunddienst sammelt so keine hängenden Prozesse mehr an. Der Guide sagt jetzt deutlich, dass
die Tiefengrenze über die Grenze auf dem Wort des vertrauten Arbeitsbereichs beruht.
8 changes: 8 additions & 0 deletions crates/chat/docs/guide/de/limits-and-safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ seinen Posts, keine Wiederaufnahme, keine Sitzung, der sich irgendetwas zurechne
Lücke zu schließen braucht ein fälschungssicheres Merkmal, das dem gestarteten Prozess übergeben
wird, statt einer Umgebungsvariablen — das ist ein Zugriffsentwurf, nicht diese Bremse.

**Über die Grenze eines Arbeitsbereichs gilt das Wort des Aufrufers.** Ein Auftrag aus einem anderen
Arbeitsbereich auf dieser Maschine trägt seine Tiefe im signierten Stempel, den dessen Koordinator
geschrieben hat, und der Empfänger rechnet von dieser Zahl weiter: Er kann sie nicht gegen eine
Kette prüfen, die im Log des anderen Arbeitsbereichs liegt. Ein vertrauter Arbeitsbereich, der jeden
Auftrag mit Tiefe 0 stempelt, hebelt die Grenze zwischen beiden aus. Das liegt innerhalb dessen, was
Vertrauen in einen Arbeitsbereich heißt — seinem Koordinator wird geglaubt, wer fragt, in welcher
Rolle und wie tief —, also ist `nxs sync trust add --workspace` für Arbeitsbereiche, deren
Koordinator Sie selbst laufen ließen.
## Das menschliche Tor

**Kein Mensch steht mitten im Ablauf.** Ein Mensch eröffnet einen Vorgang und ein Mensch liest sein
Expand Down
81 changes: 70 additions & 11 deletions crates/chat/docs/guide/de/personas.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,21 @@ addressable:
on this machine*, *not trusted*, *role not admitted*, *no such persona* oder *depth limit
reached*.

Was dann geschieht, ist eine Konsultation mit einer Übergabe in der Mitte. Der Auftrag des
Aufrufers ist ein Faden in dessen eigenem Log, und nur dieser Faden geht hinüber: Der Empfänger
startet seine Persona in SEINER Arbeitskopie, mit SEINEM Board und Gedächtnis, und was diese Persona
beauftragt, um zu antworten, bleibt in ihrem Arbeitsbereich. Eine Rückfrage (`--escalate`) geht an
den Auftraggeber zurück — nie an ihm vorbei zum Besitzer des anderen Arbeitsbereichs —, und die
Antwort des Auftraggebers auf demselben Faden setzt die Persona fort. Die Tiefengrenze gilt für die
ganze Kette über beide Arbeitsbereiche. `nxc status` zeigt den Faden in beiden Arbeitsbereichen mit
der Gegenseite und einem von vier Zuständen: `submitted`, `working`, `input-required`, `completed`
(oder `rejected` / `canceled`). Zwei Stunden ohne Lebenszeichen — keine Nachricht auf dem Faden,
nichts im Transkript der empfangenden Persona — brechen ihn ab und wecken den Auftraggeber mit dem
Grund; `nxc withdraw` auf der Operation zieht ihn auch im anderen Arbeitsbereich zurück.
Was dann geschieht, ist eine Konsultation mit einer Übergabe in der Mitte. Der Auftrag des Aufrufers
ist ein Faden in dessen eigenem Log, und nur dieser Faden geht hinüber: Der Empfänger startet seine
Persona in SEINER Arbeitskopie, mit SEINEM Board und Gedächtnis, und was diese Persona beauftragt,
um zu antworten, bleibt in ihrem Arbeitsbereich. Eine Rückfrage (`--escalate`) geht an den
Auftraggeber zurück — nie an ihm vorbei zum Besitzer des anderen Arbeitsbereichs —, und die Antwort
des Auftraggebers auf demselben Faden setzt die Persona fort. Die Tiefengrenze gilt für die ganze
Kette über beide Arbeitsbereiche — auf das Wort des Aufrufers: Die Tiefe reist im signierten Stempel
des Auftrags, und der Empfänger kann sie nicht gegen eine Kette prüfen, die er nie sieht. Einem
Arbeitsbereich zu vertrauen heißt, seinem Koordinator zu vertrauen, und ein feindseliger könnte
Tiefe 0 stempeln; vertrauen Sie nur Arbeitsbereichen, deren Koordinator Sie selbst laufen ließen.
`nxc status` zeigt den Faden in beiden Arbeitsbereichen mit der Gegenseite und einem von vier
Zuständen: `submitted`, `working`, `input-required`, `completed` (oder `rejected` / `canceled`).
Zwei Stunden ohne Lebenszeichen — keine Nachricht auf dem Faden, nichts im Transkript der
empfangenden Persona — brechen ihn ab und wecken den Auftraggeber mit dem Grund; `nxc withdraw` auf
der Operation zieht ihn auch im anderen Arbeitsbereich zurück.

`send` und `reply` tragen einen Grenzfaden sofort hinüber, die Zustellung braucht also keinen
Hintergrunddienst; den Rest trägt der Dienst in seinem Takt. Ein Fall braucht ihn doch, wie
Expand Down Expand Up @@ -499,6 +503,61 @@ gemeldet. Der Rat oben heißt deshalb nicht „das Werkzeug passt schon auf": es
Versionsverwaltung, die aufpasst, und dies ist ein zweites Paar Augen auf der Hälfte des Ordners, die
es sehen kann.

## Eine Definition für jedes Repo

Eine Persona, die Sie in jedem Repo haben wollen — denselben `pm` überall —, müssen Sie nicht in
jedes kopieren. Neben dem eigenen `.nxs-personas/` eines Workspace gibt es einen **Ordner auf
Benutzerebene**, `~/.nexusflow/personas/`, genauso aufgebaut: eine `<handle>.yaml` je Persona und
eine `channels.yaml`. Jeder Workspace auf der Maschine liest ihn.

- **Zusammengeführt je Name.** Eine Persona oder ein Kanal, den das Repo selbst deklariert,
**verdeckt** den gleichnamigen Eintrag auf Benutzerebene; alles andere kommt hinzu. Ein Kanal
kommt mit den Personas, die er voraussetzt, und seine Mitglieder lösen gegen das zusammengeführte
Team auf — ein Kanal `planning` auf Benutzerebene, der `coder` nennt, erreicht den eigenen `coder`
jedes Repos.
- **Die Identität bleibt je Repo.** Aus einer Definition `pm` wird in jedem Repo, in dem sie läuft,
ein eigener Teilnehmer, mit Board, Gedächtnis und Arbeitskopie dieses Repos.
- **Woher etwas kommt, wird gezeigt.** `nxc list` markiert jeden Eintrag aus dem Ordner auf
Benutzerebene und sagt, welche Einträge das Repo verdeckt; `nxc list --json` trägt an einem
solchen Eintrag `"origin": "user"` und unter `declarations` die Felder `user_path`, `from_user`
und `shadowed`. `nxs prime` sagt dasselbe unter „Declarations". Eine Verdeckung wird dort gemeldet
und nirgends sonst — nicht beim Öffnen eines Vorgangs, nicht in der Sitzung, die er startet. Ein
Repo, das eine eigene Kopie hält, führt diese Kopie aus, mit ihren eigenen Hürden, Mitgliedern und
Zugangsregeln; der Ordner auf Benutzerebene kann das nicht verbieten.
- **Wie der Ordner gefüllt wird, entscheiden Sie** — ein Git-Klon, eine symbolische Verknüpfung. Der
Ordner ist der Vertrag, nicht seine Herkunft.

**Der Preis, ausgesprochen:** Eine Deklaration außerhalb des Repos ist nicht mit ihm versioniert,
und ein Klon auf einer Maschine ohne den Ordner hat keinen `pm`. Alles, was oben über
Versionsverwaltung steht, gilt für diesen Ordner genauso — halten Sie ihn in einem eigenen Repo.

Drei Dinge gelten genau wie für den eigenen Ordner des Repos:

- **Er wird je Vorgang eingefroren.** Ein Vorgang läuft unter dem zusammengeführten Katalog, wie er
beim Öffnen des Vorgangs stand — Hürden (`preconditions:`) aus dem Ordner auf Benutzerebene
eingeschlossen. Eine Änderung dort erreicht den nächsten Vorgang, nie einen laufenden.
- **Ein relativer Pfad meint das Repo, in dem die Persona läuft.** Nichts löst einen Pfad auf, der
in einer Deklaration steht: Ein System-Prompt, der sagt „lies `knowledge/x.md`, relativ zur Wurzel
dieses Arbeitsbereichs", wird von einer Sitzung gelesen, deren Arbeitsverzeichnis das Repo ist,
und eine Hürde läuft ebenfalls dort. Dateien, die *neben* einer Deklaration auf Benutzerebene
liegen, werden so also nicht gefunden. Eine Persona aus dem Ordner auf Benutzerebene erfährt beim
Sitzungsstart, wo ihre Deklaration liegt („Declared in"), damit ihre Anweisungen dorthin zeigen
können.
- **Eine einbettende App liest denselben Ordner.** Sie wählt keinen anderen und kann es nicht: Die
Personas, die sie startet, laufen mit `nxc`, das diesen Ordner liest, und zwei Antworten auf „wer
existiert" würden eine App von ihren eigenen Sitzungen trennen.

**Ein `external` aus dem Ordner auf Benutzerebene öffnet kein Repo von selbst.** Eine Persona auf
Benutzerebene, die Aufrufer von außen zulässt, ist in jedem Repo zulässig, das sie führt, aber nur
aus den Arbeitsbereichen, denen dieses Repo in seiner eigenen Vertrauensliste beim Namen vertraut
(`nxs sync trust add --workspace`). Ein Repo, das niemandem vertraut, lässt von außen niemanden
herein.

Ein Entwicklungs-Build liest einen Ordner auf Benutzerebene nur unter einer benannten Dienst-Instanz
— `~/.nexusflow-<name>/personas/`, wenn `NXS_SERVICE_INSTANCE` eine benennt. Ein Build ohne sie (CI,
eine Shell ohne `direnv`) liest keinen, sodass ein Build im Test nie die Personas aufgreift, mit
denen Ihre installierte Suite läuft.

## Wenn eine Deklaration falsch ist

Eine fehlerhafte Datei ist ein lauter `validation`-Fehler, der den Pfad nennt — nie eine still
Expand Down
7 changes: 7 additions & 0 deletions crates/chat/docs/guide/en/limits-and-safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ anything to be attributed to. Closing that last gap needs an unforgeable credent
spawned process rather than an environment variable, which is an access-control design, not this
guard.

**Across a workspace border it is the caller's word.** A commission from another workspace on this
machine carries its depth in the signed stamp its coordinator wrote, and the receiver continues from
that number: it cannot check it against a chain that lives in the other workspace's log. A trusted
workspace that stamps depth 0 on every commission defeats the cap between the two. That is inside
what trusting a workspace means — its coordinator is trusted to say who asks, in which role, and how
deep — so `nxs sync trust add --workspace` is for workspaces whose coordinator you would run
yourself.
## The human gate

**No human stands inside the flow.** A person opens an operation and a person reads its result;
Expand Down
60 changes: 57 additions & 3 deletions crates/chat/docs/guide/en/personas.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,14 @@ thread in its own log, and only that thread crosses: the receiver starts its per
copy, with ITS board and memory, and whatever that persona commissions to answer stays in its own
workspace. A question (`--escalate`) goes back to the commissioner — never past it to the other
workspace's owner — and the commissioner's answer on the same thread resumes the persona. The depth
cap holds for the whole chain across both workspaces. `nxc status` shows the thread in both
cap holds for the whole chain across both workspaces — on the caller's word: the depth travels in
the commission's signed stamp, and the receiver cannot check it against a chain it never sees.
Trusting a workspace means trusting its coordinator, and a hostile one could stamp depth 0; trust
only workspaces whose coordinator you would run yourself. `nxc status` shows the thread in both
workspaces with the other side and one of four states: `submitted`, `working`, `input-required`,
`completed` (or `rejected` / `canceled`). Two hours without a sign of life — no message on the
thread, nothing in the receiving persona's transcript — cancel it and wake the commissioner with
the reason; `nxc withdraw` on the operation takes it back in the other workspace too.
thread, nothing in the receiving persona's transcript — cancel it and wake the commissioner with the
reason; `nxc withdraw` on the operation takes it back in the other workspace too.

`send` and `reply` carry a border thread across at once, so delivery does not need the background
service; the service's pass carries the rest. One case does need it, as it does inside a workspace:
Expand Down Expand Up @@ -466,6 +469,57 @@ that was rolled back the same way — different members, a different `working_tr
is not "the tooling has your back": it is version control that has your back, and this is a second
pair of eyes on the half of the folder it can see.

## One definition for every repository

A persona you want in every repository — the same `pm` in each — does not have to be copied into
each one. Beside a workspace's own `.nxs-personas/` there is a **user-level folder**,
`~/.nexusflow/personas/`, laid out the same way: one `<handle>.yaml` per persona, and a
`channels.yaml`. Every workspace on the machine reads it.

- **Merged per name.** A persona or channel the repository declares itself **hides** the user-level
one of the same name; everything else is added. A channel comes along with the personas it
presupposes, and its members resolve against the merged team — a user-level `planning` channel
naming `coder` reaches each repository's own `coder`.
- **Identity stays per repository.** One definition `pm` is a participant of its own in every
repository it runs in, with that repository's board, memory and working copy.
- **Where it came from is shown.** `nxc list` marks each entry from the user-level folder and says
which entries the repository hides; `nxc list --json` carries `"origin": "user"` on such an entry
and `user_path`, `from_user` and `shadowed` under `declarations`. `nxs prime` says the same under
"Declarations". A hiding is reported there and nowhere else — not when an operation opens, not in
the session it starts. A repository that keeps its own copy runs that copy, with its own hurdles,
members and access rules; the user-level folder cannot forbid it.
- **How the folder is filled is yours to decide** — a git clone, a symbolic link. The folder is the
contract, not its history.

**The price, said out loud:** a declaration outside the repository is not versioned with it, and a
clone on a machine without the folder has no `pm`. Everything said above about version control holds
for this folder too — keep it in a repository of its own.

Three things hold exactly as they do for the repository's own folder:

- **It is frozen per operation.** What an operation runs under is the merged catalogue as it stood
when the operation opened — hurdles (`preconditions:`) from the user-level folder included. An
edit there reaches the next operation, never a running one.
- **A relative path means the repository the persona runs in.** Nothing resolves a path written in a
declaration: a system prompt that says "read `knowledge/x.md`, relative to the root of this
workspace" is read by a session whose working directory is the repository, and a hurdle runs there
too. Files kept *beside* a user-level declaration are therefore not found that way. A persona from
the user-level folder is told at session start where its declaration lives ("Declared in"), so its
instructions can point there instead.
- **An embedding app reads the same folder.** It does not choose another one, and cannot: the
personas it starts run `nxc`, which reads this folder, and two answers to "who exists" would split
an app from its own sessions.

**An `external` from the user-level folder opens no repository by itself.** A user-level persona
that admits callers from outside is admissible in every repository that runs it, but only from the
workspaces that repository trusts by name in its own trust list (`nxs sync trust add --workspace`).
A repository that trusts nobody admits nobody from outside.

A development build reads a user-level folder only under a named service instance —
`~/.nexusflow-<name>/personas/` when `NXS_SERVICE_INSTANCE` names one. A build without one (CI, a
shell without `direnv`) reads none, so a build under test never picks up the personas your installed
suite runs with.

## When a declaration is wrong

A malformed file is a loud `validation` error naming the path — never a silently skipped persona. A
Expand Down
Loading
Loading