docs: define the shared room and object model

Signed-off-by: Fizz <b46ef72d2feab665ebb73edf75cce1628e3e6368697f7081c51cf547d4b77a44@buzz.block.builderlab.xyz>
This commit is contained in:
Fizz
2026-09-03 18:26:38 -07:00
parent 2ac0aa1dd1
commit 7c0c43ce3d
5 changed files with 237 additions and 10 deletions

View File

@@ -23,6 +23,32 @@ against that direction. Scale validation to the change's risk and exercise the
real workflow for user-visible or integration behavior when practical; green CI
and runtime evidence answer different questions.
### Room and Object Model
For changes involving projects, tasks, repositories, branches, documents, or
channel navigation, read [VISION_ROOMS.md](VISION_ROOMS.md) before designing the
client or protocol. Apply these rules across desktop, web, mobile, CLI, and agents:
- A room is a stable channel UUID with one conversation and zero or more object
facets, not an exclusive task/branch/repo type. One object resolves to one
canonical room; several objects may share it. A task and branch can be the
**same room**. Do not create a second chat merely to add a view.
- Resolve by community-scoped object identity, never display names. Preserve
task nesting and branch-to-repository links even when navigation shows a tree.
- Containment, context inheritance, and access control are separate. Nesting or
linking must not grant membership, privatize global records, or change Git
ACLs. Repo `buzz-channel` is security-sensitive; project binding is not repo
authority.
- Do not invent per-client relationship encodings or conflict winners. Shared
metadata must round-trip through relay persistence/discovery and supported
writers; handle unsupported fields, conflicts, retries, and partial creation.
- Preserve one conversation and its history across views. Migrating issue/PR
comments needs an explicit compatibility plan, not silent duplicate streams.
A branch merge must not archive other active facets sharing its room.
The vision's metadata extension is proposed, not a shipped API. These guardrails
constrain new work; they do not authorize an implicit data migration.
---
## Ecosystem

View File

@@ -152,7 +152,9 @@ Beyond chat: channels are workspaces.
The relay hosts git repos. Smart HTTP — standard `git clone`, `git push`, nothing special. Your npub signs pushes. Same domain, same auth, same identity as everything else on the relay.
Branches are channels. Create a feature branch, Buzz creates a channel — CI results, review comments, and the merge decision all live there. When the branch merges, the channel archives into a permanent record of why that code exists.
The target is **one room, many views**. Projects, tasks, repositories, branches, and documents resolve to canonical conversation rooms; a task and its branch can share the same channel UUID. Rooms nest for navigation without changing permissions. See [VISION_ROOMS.md](VISION_ROOMS.md) for the cross-client conceptual contract and the boundary between existing behavior and proposed backend support.
In that model, creating a feature branch creates or reuses its room — CI results, review comments, and the merge decision belong there. A completed branch-only room can archive into a permanent record; merging a branch must not archive other active work sharing the room.
See [VISION_PROJECTS.md](VISION_PROJECTS.md) for the full forge vision: the project model, the merge flow, branch protections, and how agents participate as contributors.

View File

@@ -1,6 +1,6 @@
# 🐝 Buzz Projects — A Nostr-Native Forge
> Someone pushes a fix. Buzz creates a channel for the branch. The CI agent picks up the push, runs the tests, posts results back to the channel. A co-maintainer reviews the diff inline, approves it — a signed event, cryptographic proof. Merge. The workflow runs the integration. The channel archives into a permanent record of why that code exists.
> Someone pushes a fix. Buzz creates or reuses the branch’s canonical room, which can already be the task’s room. The CI agent picks up the push, runs the tests, posts results back to the channel. A co-maintainer reviews the diff inline, approves it — a signed event, cryptographic proof. Merge. The workflow runs the integration. Once all work sharing the room is finished, it can archive into a permanent record of why that code exists.
>
> Bug report to merged patch. One place. One search index. One identity system. The branch channel was the pull request, the CI dashboard, and the discussion thread.
@@ -10,6 +10,12 @@ This document is the software-forge slice of the broader Buzz platform. [VISION.
## The Project Model
The shared room contract is [VISION_ROOMS.md](VISION_ROOMS.md): a project, repo,
task, branch, or document is a facet of a canonical room, not an exclusive channel
type. Several facets may share one conversation. The room model and automatic
lifecycle described here are product direction, not a claim that provisioning,
comment migration, or inheritance already ships.
A project lives on the relay. `myproject.com` in a browser shows the project home. Click a repo and you're at `repoa.myproject.com` — README rendered, file tree navigable, code syntax-highlighted, clone URL at the top. The same URL serves HTML to a browser and git protocol to `git clone`. Content negotiation. One URL, two audiences.
Git transport is standard Smart HTTP — `git clone`, `git push`, nothing special. Your npub signs pushes. Same domain, same auth, same identity as everything else on the relay. The host in the clone/push URL is also the community selector: the same `owner/repo` name may exist in two communities without sharing refs, branch protections, workflow runs, approvals, or repo announcements.
@@ -78,7 +84,9 @@ The cost is stated plainly: a third-party NIP-34 client sees the member repos in
A feature branch is a conversation.
When you create a branch, Buzz creates a channel. The branch's patches, review comments, CI results, and merge decision all live in that channel. When the branch merges, the channel archives. The conversation becomes the permanent record of why that code exists.
When you create a branch, Buzz should create or reuse its canonical room. If the branch implements a task in an existing room, task details and the branch diff can be views of that same channel UUID. The branch's patches, review comments, CI results, and merge decision belong to that conversation, not a parallel branch chat. Room nesting preserves the task/subtask structure; a separate branch-to-repository reference preserves code ownership.
When a branch-only room's work finishes, it can archive as the permanent record of why that code exists. A merge must not automatically archive a shared room whose task or other facets are still active. The example below is the branch-only case.
```
#feat-auth-fix
@@ -126,7 +134,7 @@ Push CI Review Merge
│ │ │ │ Merge to main
│ │ │ │ kind:1631
│ │ │ │
│ │ │ │ Channel archives
│ │ │ │ Branch-only room archives
```
The approval event is signed by the maintainer's npub. The merge status references the approval. The audit log chains them together. Cryptographic proof of who approved what.
@@ -184,12 +192,18 @@ Every step traced. Every trace a signed event. Change the project CI once and ev
### Issues → Forum + NIP-34
Bug reports are NIP-34 kind:1621 events, rendered through Buzz's forum surface. Threaded comments use NIP-22 kind:1111. Labels, assignees, milestones are nostr tags. Design discussions and RFCs use the forum's long-form async surface.
Bug reports and tasks retain their NIP-34 kind:1621 identity. In the target model, task details, forum presentation, and branch work can open the same canonical room; subtasks can have nested rooms. A forum is a view, not a reason to fork the conversation.
NIP-22 kind:1111 is the intended portable threaded-comment representation in this forge vision; current issue/PR clients use separate kind:1 comments. Unifying these with room chat requires the explicit read/write compatibility transition described in [VISION_ROOMS.md](VISION_ROOMS.md#rollout-boundaries). Labels, assignees, and milestones remain object metadata, not channel identity.
NIP-34 clients can discover and interact with issues. Buzz's forum gives them a home with threading, search, and agent triage.
### Docs → Canvases
A channel canvas is the initial document facet of a room. A document may share
its project's or task's room, or have a dedicated nested room; opening the
document should not create a parallel discussion stream.
Living documents, collaboratively editable by humans and agents via MCP tools. Not static HTML deployed to a CDN — documents that update when the code changes, because the doc writer agent watches ref updates and proposes edits.
### Releases → Agent + Workflow
@@ -227,7 +241,7 @@ Standard kinds as substrate. Custom kinds only where genuinely novel.
| Layer | Standard NIP Kinds | Buzz Custom | Rationale |
|-------|-------------------|---------------|-----------|
| **Git state** | 30617, 30618, 1617, 1618, 1621, 1630-1633 (NIP-34) | — | Interop with ngit, gitworkshop.dev |
| **Comments** | 1111 (NIP-22) | — | Threaded replies everywhere |
| **Comments (target)** | 1111 (NIP-22) | — | Portable threading; transition from current kind:1 comments remains to be designed |
| **Channels** | 9000-9022, 39000-39003 (NIP-29) | — | Project workspaces |
| **HTTP auth** | 27235 (NIP-98) | — | Git push authentication |
| **Agent identity** | 0 (NIP-01 profile) | — | Agents are npubs |

183
VISION_ROOMS.md Normal file
View File

@@ -0,0 +1,183 @@
# Buzz Rooms — One Conversation, Many Views
**Status: product direction; not a shipped protocol.** This document defines the
shared conceptual contract for desktop, web, mobile, CLI, and agent integrations.
The backend shape below is a proposal to review before implementation, not an API
clients may assume exists. [VISION.md](VISION.md) covers the platform;
[VISION_PROJECTS.md](VISION_PROJECTS.md) covers its software-forge workflows.
## The Model
A **room is a channel**: one stable channel UUID and one working conversation.
Projects, tasks (including subtasks), repositories, branches, and living documents
each resolve to a canonical room. They are **facets** of that room, not mutually
exclusive room types. An ordinary conversation can have no object facets.
One object has one canonical conversation home within its community. One room can
be home to several objects. A task and its implementation branch can therefore
be two views of **the same channel UUID**, not separate channels with synchronized
messages. Switching from task details to the diff changes the view, not the
conversation, membership, or history. Adding a facet does not create another chat.
Object state stays in its own representation: a task still has status and
assignees, a repository still has refs, and a document still has content. The room
connects those representations; it does not turn every object into a chat message
or replace NIP-34 records with channel rows.
### Identity Is Not a Label
Resolve objects and rooms by stable identity, scoped to the active community:
| Object | Identity to resolve, not a display-name heuristic |
|---|---|
| Room | Channel UUID |
| Project | Address `30621:<owner-pubkey-hex>:<project-id>` |
| Repository | Address `30617:<owner-pubkey-hex>:<repo-id>` |
| Task / subtask | Issue event ID (`kind:1621` today) |
| Branch | Repository address **and full ref**, e.g. `refs/heads/fix-login` |
| Initial document view | The channel's canvas, identified by its channel UUID |
A room rename does not change its identity. The same branch name in two repos is
two different objects. Newer repository/project metadata events replace state at
the same address; their event IDs are not new object identities. A branch rename
or deletion/recreation needs an explicit binding/lifecycle policy, not a guess
based on a matching label. Independently addressable documents beyond canvases
need their own agreed identity contract before implementation.
## Nesting Without Losing Relationships
Rooms can nest. A single parent gives navigation a tree (or a forest of root
rooms); the full object relationships remain a graph. For example:
```text
Project room P [project + default repository]
├── Task room T [task: Improve sign-in]
│ └── Subtask room S [task: Fix login + branch: refs/heads/fix-login]
└── Design room D [document: sign-in design canvas]
```
The subtask and branch both open room S. S remains nested under T, while its branch
still belongs to the repository in P. A repository view may also link to S; that
is another route to the same room, not a second parent or another conversation.
A dedicated repository room is also valid; sharing the default repository with
the project home is not mandatory for every repository.
Project membership still comes from the project's repository references
([NIP-MP](docs/nips/NIP-MP.md)); nesting rooms does not redefine it. A repository
can belong to several projects and still have one canonical room. Room nesting
also does not by itself define task dependencies or a claimable-work queue.
When several task facets share a room, room containment alone cannot identify
which task is another task's parent; that needs an explicit object relationship.
### Three Separate Decisions
1. **Containment/navigation:** where a room appears and how users find it.
2. **Context:** which parent guidance or related documents an agent should read.
3. **Access:** who may read, write, administer, or push.
A parent link or object association does not grant access, copy membership, or
inherit permissions. Context inheritance is a separate, explicit, permission-
checked policy; a link is not permission to read everything behind it. Navigation
must not expose inaccessible parent/child metadata. An unreadable or unresolved
room does not justify inventing a replacement room or hiding an otherwise
readable object.
**Existing repository bindings are security-sensitive:** `buzz-channel` on a
repository announcement participates in hosted Git access control. The same tag
on a project is navigation metadata, not authority over its repositories. New
room associations must neither overwrite the repo's ACL binding as a navigation
convenience nor become a second, competing source of Git permissions. Linking a
community-visible issue or project to a private room does not make it private.
## What Exists Today
This is the implementation baseline inspected at `2ac0aa1dd`, not a claim that
all of the target model has shipped:
| Existing behavior | Source |
|---|---|
| Desktop project creation provisions a home channel and default repo sharing that home | [createProject.ts](desktop/src/features/projects/createProject.ts) |
| Projects and repos are signed addressable events; tasks are issue events, not dedicated task rows | [NIP-MP](docs/nips/NIP-MP.md), [SDK builders](crates/buzz-sdk/src/builders.rs), [events schema](migrations/0001_initial_schema.sql) |
| Channels have persisted rows; discovery metadata is reconstructed from supported fields | [channel store](crates/buzz-db/src/store/channel.rs), [side effects](crates/buzz-relay/src/handlers/side_effects.rs) |
| Hosted Git uses the repo's channel binding for access, and object storage for Git data | [binding.rs](crates/buzz-relay/src/api/git/binding.rs), [store.rs](crates/buzz-relay/src/api/git/store.rs) |
| Task creation does not provision a task room; issue/PR comments are separate `kind:1` records | [issueMutations.ts](desktop/src/features/projects/issueMutations.ts), [project hooks](desktop/src/features/projects/hooks.ts) |
| Project/issue/Git records remain community-scoped even when carrying an `h` provenance tag | [ingest.rs](crates/buzz-relay/src/handlers/ingest.rs), [CLI command guidance](crates/buzz-cli/src/commands/mod.rs) |
The channel store does not yet provide general room hierarchy or multi-object
bindings. Branch ref state is not a branch-room registry. Threads, huddle parent
links, sidebar sections, and lists of related channels are not substitutes for
shared room containment. The agent harness's [current-room canvas context](crates/buzz-acp/src/queue.rs)
does not implement inherited parent context.
## Smallest Proposed Backend Extension
Keep the channel UUID, conversation, membership, and object event models. Extend
persisted channel metadata with:
- An optional `parent_channel_id` in the same community, indexed for child lookup.
- An `object_bindings` list of typed stable references; JSONB is sufficient for an
initial representation. Several bindings may point into one room.
Illustrative binding values are a task issue ID, a repository/project address,
or a repository address plus full branch ref. These are **conceptual field
names**, not finalized database columns, tag spellings, or SDK types.
Use the existing signed channel create (`9007`), edit (`9002`), and discovery
metadata (`39000`) path. No feature-specific HTTP endpoint is needed. Proposed
edit semantics are explicit set/clear of the parent and replacement of the
binding list, with unrelated metadata preserved. The implementation must specify
omission, clearing, concurrent edits, and older-client round trips before rollout.
Arbitrary client-added tags do not persist merely because a create/edit event
accepts them: storage and discovery serialization must both support the fields.
The relay must enforce channel-management authority, bounded/valid binding
shapes, an existing same-community parent, no self-parenting or cycles (including
concurrent moves), and atomic metadata updates. It must enforce access on
relationship discovery as well as writes. Cross-object authority needs an
explicit resolution rule: managing a room is not authority to hijack somebody
else's object's canonical home. Existing owner-signed project/repo bindings must
be reconciled, not silently overruled by room-side claims.
A global unique-binding constraint is not required for the first metadata
increment. **Canonicality is still the product contract**, not permission for
each client to choose its own winner. Before enabling canonical-room creation,
define one shared resolution/reconciliation rule for conflicting claims and
idempotent retries. Surface unresolved conflicts instead of choosing whichever
room a client happened to discover first. Clients may create/reuse rooms on
object creation or discovery, but must recover from partial object/room creation
without silently provisioning duplicates. Metadata alone does not solve races.
## Rollout Boundaries
- **One conversation needs a transition.** Issue/PR comments currently have a
separate event stream; the forge vision also discusses NIP-22 comments. A
follow-up must define compatible reads, one authoritative write destination,
provenance, deduplication, and access behavior before claiming a unified room
conversation. Do not start dual-writing copies of messages or discard history.
- **Automation is separate.** Client-on-discovery provisioning can come first.
Creating a room for a Git push while every client is offline needs a persistent
hook/worker. Document which guarantee an implementation actually provides.
- **Lifecycle follows all facets.** A branch merge may archive a branch-only
room; it must not automatically archive an active task or project sharing that
room. Unlinking/deleting an object must not erase shared conversation history
or cascade-delete other facets. Shared-room archive policy needs explicit UX.
- **Private objects and inheritance are not included.** Private task records,
inherited access, inherited harness context, workflow inheritance, standalone
document objects, and richer relationship/dependency graphs need separate
designs. Do not infer them from the proposed metadata fields.
## Implementation Review Checklist
- Do desktop, web, mobile, CLI, and agents resolve the same object to the same
channel UUID, including retries, conflicts, and community switching?
- Can a task gain a branch view without replacing its identity or conversation?
- Do both task nesting and branch-to-repository relationships survive navigation?
- Are unsupported/unresolved bindings handled without guessed names, duplicate
rooms, unauthorized disclosure, or a second conversation stream?
- Are membership, object ownership, repository ACLs, and context policies unchanged
unless the change explicitly designs and tests a new authorization contract?
- Do persistence, edits, discovery, SDKs, and clients round-trip the same metadata?
Related requests: [channel hierarchy #3667](https://github.com/block/buzz/issues/3667)
and [issue relationships #3728](https://github.com/block/buzz/issues/3728). This
vision establishes direction; it does not close either implementation request.

View File

@@ -78,16 +78,18 @@ a response in seconds. The maintainer sees a pre-processed report, not a raw inb
They read the triage summary, confirm it's real, assign it. Thirty seconds of their
time instead of five minutes.
Someone picks up the bug. They create a branch. Buzz creates a channel:
`#feat-auth-fix`. They work there. They push commits. The CI agent picks up the
work — it's watching for pushes, it's just a member with compute — runs the tests,
Someone picks up the bug. They create a branch. Buzz creates or reuses its
canonical room: `#feat-auth-fix`. The task and branch can be views of the same
channel UUID, as defined in [VISION_ROOMS.md](VISION_ROOMS.md). They work there.
They push commits. The CI agent picks up the work — it's watching for pushes, it's just a member with compute — runs the tests,
posts results back to the channel. Green. The patch lands in the channel as a
reviewable diff. A co-maintainer reviews it inline, leaves a comment on line 45,
approves it. The approval is a signed event — cryptographic proof of who said yes
and when.
Merge. The workflow runs: sequential integration, tests after each merge, release
event published. The channel archives. The conversation is now the permanent record
event published. A finished branch-only room archives; a shared room stays active
while its other work continues. The conversation is now the permanent record
of why that code exists. Not a chat thread that scrolled away. Not a PR that might
get deleted. The event log, on your relay, in your database. Someone reading that
code in two years can pull up the channel and see exactly what was discussed, what