nxsflow

Changelog

What's new

0.205.0

2026-10-08
  • ChangedThe views no longer lean on the op log. The parent of an item, the weight of a thread link, the created and updated instants of an item and the order and date of a note are now kept in the folded rows themselves instead of being looked up in the log on every read, so the same views can be folded where no log is kept beside them. A message and a new thread now belong to the op that creates them: their id is minted from that op's own id, and another op claiming the same id never takes the message or the thread over, whatever its coordinate — where it used to be whichever op arrived first, so two replicas could show different messages under one id. For an id minted before this version, the claimant with the lowest coordinate is the message, and an op numbered below 1 is kept but never shown. The first time this version opens a workspace it rebuilds the board's and the chat's views once from the log; that also corrects messages an old build had recorded under the sender their payload claimed. An item's created and updated instants now count the ops this version folds; an op it keeps but cannot fold no longer moves them. The workspace schema goes to v8; older versions keep working on the same file — an item still shows one parent and a thread link one weight, and a parent or weight they set there takes effect when this version next opens the file — and that open rebuilds whatever they wrote. For embedding apps: a `Reducer` no longer folds into a connection — it returns the changes an op makes (`Reducer::changes`, built from `nxs_foundation::change`), and the substrate carries them out; a reducer declares `fold_revision` when its rules change. New: `Change::register_ranked`, `Store::emit_owned`, `Op::owns_target` and `ChatStore::open_new_thread`. `Image` gains `fold_revision` and `RefoldReason` gains `OtherRevision`.
  • ChangedWhich tickets are ready, in progress, deferred or blocked — and how `next` tiers them — is now decided by one Rust library over the active tickets and their dependencies and parents (`nexus_flow_core::graph`). The board selects those tickets with SQL; a server can select them from its own index and get exactly the same lanes. One rule comes with it: a ticket that is not active — closed, archived or deleted — counts as closed wherever another ticket depends on it or hangs under it. Four answers change, all in states only a merge of concurrent edits — or an edge that arrives before its parent — produces: a dependency on an archived ticket that was opened again no longer blocks; an open ticket whose only parent was deleted, or whose parent no ticket has been seen for, rests as it would under a closed parent (`nxf search` files it under closed, and `nxf show` names the parent as the reason); and a dependency cycle that runs through a closed ticket no longer blocks anything. `nxf blocked` lists only active blockers, and the "blocks N" count in `nxs prime` counts only active dependents.
  • AddedA server can now fold a stream itself, with the same rules as a local replica: the new `nxs-fold-ddb` crate carries the board's, the chat's and the memory's described changes out as conditional writes against one DynamoDB table, keeps two sparse indexes beside the rows — `active` (the active tickets and the dependencies and parents that touch one) and `dated` (the closed and the archived lanes, newest first) — and reads next, blocked and deferred through the same `nexus_flow_core::graph` library a local board uses, with one Query and no Scan. The order ops arrive in and ops delivered twice do not change the result, and a run that stops half way and folds its batch again ends where an unbroken run ends. An op too large for DynamoDB's limits is refused and reported instead of stopping the fold. A snapshot for starting such a fold is the tables plus the relay position they reach, without the log; one can be taken from a server stream or from a local replica's views. The table it expects is written down in the crate and in the sync spec. The DynamoDB adapter is behind the crate's `dynamodb` feature, so a build without it needs no AWS SDK. For embedding apps: `nxs_foundation::reducer::folder_for` is the one rule for which reducer folds an op, and `nexus_flow_core::model::is_active` the active rule over three cells. Finding dependency cycles now takes time linear in the board.

0.204.0

2026-10-06
  • AddedOne 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.
  • FixedAcross 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.
  • AddedA published skill is a persona. A persona may now be declared as a folder `.nxs-personas/<name>/SKILL.md` in the form of the Agent Skills specification: `name`, `description` and `allowed-tools` at the top of the frontmatter, the instructions as the body, and everything nexus-flow adds under one key, `nxs:` (`title`, `expected_output`, `stage`, `model`, `addressable`, `prime`, `claude_md`, `base_prompt`, `permissions`, `working_tree`, `machine`, and the new `requires`). A skill folder found elsewhere, copied in unchanged, is listed, addressed and answers; without `nxs:` it runs with every default. A relative path in a skill's body means the persona's own folder, so its `references/` are found from the user-level folder too, and the session is told where that folder is. `allowed-tools` keeps the three states `tools` has and takes the space-separated text, a comma-separated text or a YAML list. `license`, `compatibility` and `metadata` are passed through and shown in `nxc list --json`, which now carries each entry's file, form and folder under `declaration`. `nxs.requires` names capabilities and is only shown in this release; nothing binds it yet. Channels may be declared one per file under `.nxs-personas/channels/<name>.yaml`; `channels` is now a reserved name no persona may take. The older forms — `<handle>.yaml` and `channels.yaml` — are still read, in the repository's folder and the user-level folder alike, and a name declared in both forms is refused, naming both files. New: `nxs personas migrate` rewrites a folder into the new form (`--dry-run` to see the plan, `--user` for the user-level folder), keeps your comments, reports what it leaves out, proves every rewrite before it writes anything, refuses a key it cannot place, and finds nothing to do the second time. `nxs prime` warns about a name that breaks the Agent Skills name rule, the inert keys `session`, `sub_agents` and `reports_to`, a nexus-flow key at the top of a `SKILL.md`, and `nxs.requires` that nothing binds yet, a skill without `allowed-tools` (it runs with the full default toolset, and the shell is approved for its answer), and a persona that skips permission prompts while it admits callers from other workspaces. A handle may no longer contain whitespace or control characters, and a declaration file over 1 MiB is refused. `nxs prime` errors name the real file of a persona or channel, and a broken channel file excludes only its own channels from the roster. For embedding apps: `DeclarationSource::files` and `file_of`, `DeclarationFile` / `DeclarationForm`, `declaration` on `PersonaEntry` and `ChannelEntry`, `declared_form` on `PersonaBrief` (whose `declared_in` builder now takes the form), the modules `skill` and `persona_migration`, `role::load_declared_personas`, `channel::load_declared_channels` and `declaration_quality::warn_declarations_in`; `persona_migration::migrate_declarations` is the library seam of `nxs personas migrate`. An engine older than this one does not see a skill folder: migrate a repository an app reads only once the app runs this version.
  • Removed`address_book` is retired. Who a persona may address is derived from what every other persona and channel admits — each described in its own words — and no persona keeps a list of its own any more. A declaration that still carries the key loads unchanged; the key is ignored, `nxs prime` warns about it, and `nxs personas migrate` leaves it out. A persona that declared `address_book: []` now sees the derived directory like every other. For embedding apps: `RoleDecl::address_book` and `AddressBookEntry` are removed, and so are `why` on `PersonaEntry` and `ChannelEntry` and `unresolved` on `Directory`. The derived `address_book` of `nxc prime --json` is unchanged.

0.202.0

2026-10-04
  • FixedAn answer that reaches a caller while it is still finishing its turn is now delivered. On the command line the background service was handed the delivery as a channel deadline on a session id ("no such thread"), so a held answer was never handed over; and the caller's sidecar, seeing its sub-round already answered, reminded it and then posted a substitute reply in its name. A sub-round whose answer is held for its commissioner now counts as still in flight (`waiting_on_sub_round` in `nxc status --json` and on the facade). Also fixed: a persona that declares no `tools:` and is woken with an answer it still has to pass on is granted its `nxc reply` (the wake carries the obligation it still has), and a session spawned by a development build resolves that build's service home, not the installed one.
  • AddedA persona can commission a persona of another workspace on the same machine, and the answer comes back. `nxc send --to <owner>/<repo>/<persona>` records the commission in the caller's own workspace and hands over that one thread; the other workspace checks access, starts its persona in its own working copy, and the answer — or a question, which always goes to whoever commissioned — wakes the caller. Only the border thread crosses. Access is the receiver's: `addressable.external` admits a role from trusted workspaces (`"*/pm"`, `nxsflow/manufakt-io/pm`), and without it nothing comes in. New: `nxs name` (a workspace's stored `<owner>/<repo>` name), `nxs sync trust add --workspace <owner>/<repo>` (trust a neighbouring workspace by name; needed on both sides), and `nxc status` showing a border thread in both workspaces with the other side and its state (submitted, working, input-required, completed, rejected, canceled). Two hours without a sign of life cancel it; `nxc withdraw` takes it back on both sides. An engine older than this release cannot load a persona file that carries `external:` — add it only once every reader of the repository is on this version. For embedding apps: `EngineConfig::peers` and `Ctx::peers` (the host's way to reach other workspaces; `None` keeps everything as before), `Engine::handover`, `Addressable::OnlyWithExternal`, `Coordinator::Border`, and new `border` fields on `Refs`, `SendToReceipt`, `ReplyReceipt` and `StatusThread` — a struct literal of `EngineConfig`, `Ctx`, `Adapter` or `Refs` without `..Default::default()` needs the new field.
  • FixedSafety fix: a persona declared with `tools: []` can no longer run arbitrary shell commands when it owes a reply. Until now the engine granted every such session the bare, auto-approved `Bash` so that it could answer, which gave a persona declared to have no tools a full shell (`gh pr merge`, `git push --force`, a release). It now grants `Bash(nxc reply:*)`: the reply runs, every other command needs an approval nobody is there to give and is refused. A persona whose `tools:` is absent, or lists `Bash`, behaves as before; one that declares `Bash(nxc reply:*)` itself now stays that narrow. For an embedding app's own worker: `RoleSpec::granted_tools` (`grantedTools` in the sidecar spec) can now carry a scoped permission rule rather than a tool name — put the tool it names into the SDK's `tools` and the rule itself into `allowedTools`, as the bundled sidecar does.

0.201.0

2026-10-03
  • Changed`nxf show` now points at an item's parents in a neutral line — `Parents: <ids> (read for full context)` — instead of "URGENT RECOMMENDATION: … !!!". Agents reading the board had flagged the old line as a possible prompt injection. The `parents_notice` field in `--json` and on the facade carries the same new text; its name and when it appears are unchanged.

0.200.2

2026-10-02