mirror of
https://github.com/block/buzz.git
synced 2026-10-28 13:01:10 +00:00
docs(protocol): propose channel artifacts and shared client contracts
Signed-off-by: Fizz <400e8babadcee6a7f420103f10a2849d84c4a9c71d5bd04f3948c814216648a3@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -152,7 +152,13 @@ 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 proposed [channel artifact model](docs/nips/NIP-AR.md) replaces the earlier
|
||||
“branches are channels” direction: channels contain work records and may host
|
||||
several projects, tasks, or branches. A dedicated branch channel remains a useful
|
||||
workspace choice, not a required identity or one-to-one relationship. Completing
|
||||
one record does not automatically archive the shared channel. Existing Git
|
||||
objects and review authority retain their separate protocol contracts during
|
||||
migration. This is proposed direction, not shipped artifact support.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -8,6 +8,27 @@ This document is the software-forge slice of the broader Buzz platform. [VISION.
|
||||
|
||||
---
|
||||
|
||||
## Proposed evolution: channels contain work
|
||||
|
||||
The [channel artifact proposal](docs/nips/NIP-AR.md) and its
|
||||
[client contracts](docs/nips/NIP-AR-CLIENTS.md) supersede this document's earlier
|
||||
one-branch/one-channel assumption as proposed direction, not shipped behavior.
|
||||
Projects and tasks are editable records inside channels or threads; a channel
|
||||
may contain many of either. Projects organize related work without being a
|
||||
prerequisite for a task or granting access to it. Dedicated workspaces remain
|
||||
an option. Completing a task, retiring a branch record, or archiving a project
|
||||
does not automatically archive its channel.
|
||||
|
||||
The forge workflows below remain useful examples of dedicated workspaces,
|
||||
not mandatory identity/cardinality rules. NIP-34 remains authoritative for Git
|
||||
identity and operations. Existing NIP-MP projects remain the current shipped
|
||||
representation pending an explicit migration; the proposal must not create a
|
||||
second competing project record. Private artifact access does not automatically
|
||||
make existing repository metadata, code, or reviews private.
|
||||
|
||||
For the intended experience without protocol details, see
|
||||
[Work lives alongside the conversation](docs/channel-artifacts-design-note.md).
|
||||
|
||||
## The Project Model
|
||||
|
||||
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.
|
||||
|
||||
67
docs/channel-artifacts-design-note.md
Normal file
67
docs/channel-artifacts-design-note.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Work lives alongside the conversation
|
||||
|
||||
We’re proposing a simple way to bring tasks and projects into Buzz: **they live
|
||||
inside channels and threads, alongside the conversations about them.** The
|
||||
conversation captures how people reached a decision; the task or project keeps
|
||||
the current agreed state easy to find and update.
|
||||
|
||||
A channel is still a place for people to work together—not a project in disguise.
|
||||
A team can keep several projects in one channel, or give a larger project its own
|
||||
channel. A thread can contain several tasks. People don’t need to create a
|
||||
project or repository before they can track a task.
|
||||
|
||||
## What this means for the experience
|
||||
|
||||
- **Add structure where work starts.** Turn an emerging piece of work into a
|
||||
task without replacing or relocating the conversation. Keep its outcome,
|
||||
status, and assignees alongside the discussion.
|
||||
- **One record, several useful views.** A task can appear in a project overview
|
||||
and in its original conversation. These are views of the same task, not
|
||||
copies. Adding it to a project does not move it.
|
||||
- **The home channel determines who can see it.** A task in a private channel
|
||||
stays private when linked from a broader project. A link does not invite
|
||||
people, reveal private details, or give access to its discussion. Threads
|
||||
share their channel’s audience; they are not separately private spaces.
|
||||
- **Sharing follows the channel, not a separate task guest list.** Someone who
|
||||
cannot access the home channel cannot be given access to just that task. Give
|
||||
them access to the channel, or move the task to a channel they can access.
|
||||
A separately shared summary is not access to the original task.
|
||||
- **Editing follows the channel’s permissions.** People allowed to contribute
|
||||
in the home channel can update the work; it is not locked to whoever created
|
||||
it. Moving requires permission to contribute in both places; deleting requires
|
||||
channel administration. In an open channel, people allowed to post can also
|
||||
update its work, even if they have not joined the channel.
|
||||
- **Give growing work more room deliberately.** A task can move from a thread
|
||||
into a new channel and remain the same task. Before moving, show who will be
|
||||
able to see its current details. The old discussion stays where it was and
|
||||
keeps its original access; it is not automatically shared with the new group.
|
||||
- **Keep progress distinct from conversation.** Changes should appear promptly
|
||||
wherever the work is shown, without making every status edit look like a new
|
||||
chat message or marking a conversation as read.
|
||||
- **Finishing work does not close the room.** Completing a task or archiving a
|
||||
project leaves the channel and other work in it alone.
|
||||
|
||||
For example: a timeout bug is discussed in a team thread. Someone adds a task
|
||||
there, then includes it in the reliability project. People can follow it from
|
||||
either place if they have access to its home channel. If a contractor later
|
||||
needs to collaborate, an authorized person can move the task’s current details
|
||||
to a shared channel, deliberately selecting any context to bring along. The
|
||||
private team discussion does not come with it.
|
||||
|
||||
## What we need to design
|
||||
|
||||
Make it clear **what the work is, where its main conversation is, who can see
|
||||
it, and whether you are editing the existing record or creating another one**.
|
||||
Design for several tasks or projects in one place—not a single special card.
|
||||
Project overviews must handle work with different audiences without exposing
|
||||
private information or implying everyone sees the same complete list.
|
||||
|
||||
The same approach should accommodate other kinds of shared records later.
|
||||
Desktop and mobile should agree on familiar types, and show a useful basic
|
||||
card when they encounter a type they do not yet support. People should not have
|
||||
to configure plugins to get a coherent default experience.
|
||||
|
||||
We’re using “artifact” as a working umbrella term. In the product, prefer the
|
||||
specific name—“task,” “project,” and so on—rather than making people learn it.
|
||||
Code repositories and reviews keep their own access rules; connecting them to a
|
||||
task must not imply that its audience can also see the code.
|
||||
140
docs/nips/NIP-AR-CLIENTS.md
Normal file
140
docs/nips/NIP-AR-CLIENTS.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Artifact client contracts
|
||||
|
||||
`draft` `clients` `not implemented`
|
||||
|
||||
Depends on [NIP-AR](NIP-AR.md). These are shared desktop, mobile, CLI, SDK, and
|
||||
agent contracts, **not relay business validation rules**. All types use the same
|
||||
artifact envelope and lifecycle. Each may live in a channel or a thread.
|
||||
|
||||
## Common content rules
|
||||
|
||||
Known version-1 payloads are JSON objects with an integer `version: 1`. Reject
|
||||
ambiguous duplicate JSON keys when interpreting a known payload. Unknown fields
|
||||
are permitted. Editors preserve unknown fields and annotation tags or refuse an
|
||||
unsafe edit. Required title exists only in the envelope, not a second writable
|
||||
payload field. Markdown descriptions are untrusted and rendered safely.
|
||||
Version-1 writers limit the complete content to 65,536 UTF-8 bytes and
|
||||
`status_label` to 128 UTF-8 bytes; they must also respect the relay event limit.
|
||||
Clients use a bounded fallback rather than deeply parsing oversized content.
|
||||
|
||||
A malformed known type uses the fallback card with a clear unavailable-details
|
||||
state. Unknown types/versions show type, title, last editor, and a route to the
|
||||
home conversation when available. They must not disappear from the workspace.
|
||||
The default experience works without selecting or installing plugins.
|
||||
|
||||
## `buzz.project`
|
||||
|
||||
A grouping of related work, not a channel identity or access-control container.
|
||||
|
||||
| Payload field | Type | Meaning |
|
||||
|---------------|------|---------|
|
||||
| `version` | integer | `1` |
|
||||
| `description` | string | Project purpose/outcome, Markdown |
|
||||
| `archived` | boolean | Inactive in default project views, not deleted |
|
||||
|
||||
Tasks declare their project relationships; the project must not contain a second
|
||||
independently editable task-membership list. Project-owned `repository` tags may
|
||||
reference multiple NIP-34 coordinates; `channel` tags may reference related
|
||||
channel UUIDs. These are organizational references only. The project view is not
|
||||
proof of completeness for work the reader cannot access.
|
||||
|
||||
A project may have a dedicated channel or share a team channel with other
|
||||
projects. Archiving hides neither its channel nor its tasks and does not revoke
|
||||
access. Clients may warn about adding work to an archived project; this is not a
|
||||
relay rule or permission boundary.
|
||||
|
||||
## `buzz.task`
|
||||
|
||||
A living definition of work, independent of any prerequisite project or repo.
|
||||
|
||||
| Payload field | Type | Meaning |
|
||||
|---------------|------|---------|
|
||||
| `version` | integer | `1` |
|
||||
| `description` | string | Intended outcome and agreed scope, Markdown |
|
||||
| `state` | string | `open`, `done`, or `cancelled` |
|
||||
| `status_label` | string, optional | Display refinement, e.g. “In review” |
|
||||
|
||||
The broad state supports common views; teams may use labels without a relay
|
||||
deploy. Clients must not infer done/cancelled solely from an arbitrary label.
|
||||
Assignees use repeatable `assignee` tags containing public keys. Assignment here
|
||||
is task metadata, not permission to read or edit, and does not automatically
|
||||
notify or subscribe the assignee. `assignee` deliberately differs from `p`,
|
||||
which participates in mention/notification and recipient-validation behavior.
|
||||
Notification policy is separate.
|
||||
|
||||
| Relationship tag | Count | Value |
|
||||
|------------------|-------|-------|
|
||||
| `project` | 0..N | Project artifact UUID |
|
||||
| `parent` | 0..1 | Parent task artifact UUID |
|
||||
| `branch` | 0..N | Branch artifact UUID |
|
||||
|
||||
Tags have two string elements and duplicate values are collapsed by conforming
|
||||
clients. Relationships are editable; they need not have the same home channel.
|
||||
A parent is organizational, not necessarily a conversation ancestor. A client
|
||||
should prevent known cycles during authoring and must safely render cycles from
|
||||
other writers. Missing/unreadable links do not invalidate the remaining task.
|
||||
|
||||
Completing or cancelling a task does not close its conversation, archive its
|
||||
channel, or imply that a Git branch merged. Adding it to a project does not move
|
||||
it or widen its audience. The same task may appear in several project views.
|
||||
|
||||
## `buzz.branch`
|
||||
|
||||
A collaboration record concerning a Git branch; not the ref itself or a second
|
||||
review/merge authority.
|
||||
|
||||
| Payload field | Type | Meaning |
|
||||
|---------------|------|---------|
|
||||
| `version` | integer | `1` |
|
||||
| `description` | string | Purpose/context, Markdown |
|
||||
| `retired` | boolean | This collaboration record is no longer active |
|
||||
|
||||
Required tags are `repository` (a NIP-34 `30617:<owner-hex>:<identifier>`
|
||||
coordinate) and `ref` (a valid full `refs/heads/...` Git ref). Split the coordinate
|
||||
on its first two colons only. Clients validate Git ref syntax with established
|
||||
Git-compatible validation, not an invented permissive approximation.
|
||||
|
||||
A record may precede first push. Its repository/ref pair is immutable in this
|
||||
client contract: another ref or a recreated branch lifecycle gets a new record.
|
||||
Retirement is terminal for this client version. These are client semantics;
|
||||
the relay does not certify them. Multiple records may reference the same ref;
|
||||
clients may offer duplicate cleanup without merging their identities silently.
|
||||
|
||||
Git tips, push rights, checks, approval, and merge state come from authoritative
|
||||
Git/review sources under their own access checks, never this editable payload.
|
||||
A branch card must not fetch and expose code to everyone who can see the card.
|
||||
Automatic summaries/notifications must respect the destination audience too.
|
||||
|
||||
## Example: task started in a conversation
|
||||
|
||||
Standard signed-event fields are omitted. Placeholder IDs below are explanatory.
|
||||
The numeric kind is proposed; see the allocation note in NIP-AR.
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 45010,
|
||||
"tags": [
|
||||
["ar", "1"],
|
||||
["d", "<task-uuid>"],
|
||||
["h", "<channel-uuid>"],
|
||||
["root", "<conversation-event-id>"],
|
||||
["type", "buzz.task"],
|
||||
["title", "Handle payment timeout"],
|
||||
["op", "create"]
|
||||
],
|
||||
"content": "{\"version\":1,\"description\":\"Preserve the pending payment\",\"state\":\"open\"}"
|
||||
}
|
||||
```
|
||||
|
||||
Later adding `["project", "<project-uuid>"]` uses `op=update` and `prev` naming
|
||||
the current accepted revision. It preserves `d`, `h`, and `root`. The project may
|
||||
live elsewhere; viewing that project does not grant access to this task.
|
||||
|
||||
## Compatibility release gate
|
||||
|
||||
Publish shared valid/invalid fixtures before shipping editors. Exercise them
|
||||
in desktop, mobile, CLI/SDK, and agent tooling: unsupported versions, unknown
|
||||
fields/tags, bad JSON, cycles, inaccessible links, and concurrent updates.
|
||||
The relay suite should demonstrate both envelope rejection and acceptance of
|
||||
opaque content that a business client declines to interpret. Acceptance by one
|
||||
layer is not a correctness certificate from the other.
|
||||
315
docs/nips/NIP-AR.md
Normal file
315
docs/nips/NIP-AR.md
Normal file
@@ -0,0 +1,315 @@
|
||||
NIP-AR
|
||||
======
|
||||
|
||||
Channel Artifacts
|
||||
-----------------
|
||||
|
||||
`draft` `optional` `relay` `not implemented`
|
||||
|
||||
**Depends on:** NIP-01 (signed events), NIP-29 (channel access).
|
||||
|
||||
## Decision
|
||||
|
||||
An **artifact** is a durable, editable record inside a channel, optionally
|
||||
attached to a conversation within it. Channels contain artifacts; they are not
|
||||
artifacts themselves. Projects, tasks, and branch collaboration records use the
|
||||
same envelope, with client-defined content types.
|
||||
|
||||
- One current home channel determines an artifact's current audience.
|
||||
- Any number of artifacts, including of the same type, may share a location.
|
||||
- Relationships organize work; they never grant access or operational authority.
|
||||
- The relay validates the common envelope and lifecycle, not business schemas.
|
||||
- Clients share published contracts for first-party types; unknown types remain
|
||||
readable as a basic card.
|
||||
|
||||
This replaces the conversation-subject proposal in
|
||||
[PR #7656](https://github.com/block/buzz/pull/7656): no exclusive/primary claims,
|
||||
no type-specific event kinds or relay relationship validation, no required
|
||||
project for a task, and no permanently fixed home. This is a specification PR,
|
||||
not a claim that these behaviors are available in existing clients or relays.
|
||||
|
||||
MUST, MUST NOT, SHOULD, and MAY have their RFC 2119 meanings.
|
||||
|
||||
## Identity and envelope
|
||||
|
||||
The proposal uses one regular stored event kind, **45010**, for all artifact
|
||||
revisions. This number is proposed, not allocated by this docs-only change;
|
||||
implementation must reserve it in `crates/buzz-core/src/kind.rs`. It replaces,
|
||||
not interoperates with, the unshipped project-kind proposal using that number.
|
||||
|
||||
Each revision is a complete snapshot, not a field patch. Common tags have
|
||||
exactly two string elements. Required tags occur exactly once; optional tags at
|
||||
most once. Duplicate common tags are invalid even if their values agree. The
|
||||
relay MUST reject more than 256 total tags per revision, including common and
|
||||
unknown tags; its advertised event-size limit also applies.
|
||||
|
||||
| Tag | Count | Meaning |
|
||||
|-----|-------|---------|
|
||||
| `ar` | 1 | Envelope version, initially `1` |
|
||||
| `d` | 1 | Stable artifact UUID: canonical lowercase, hyphenated, non-nil |
|
||||
| `h` | 1 | Current home channel UUID, same encoding |
|
||||
| `root` | 0..1 | Conversation anchor event ID, 64 lowercase hex characters |
|
||||
| `prev` | 0..1 | Immediately preceding accepted revision ID, 64 lowercase hex characters |
|
||||
| `type` | 1 | Namespaced content type, e.g. `buzz.task` |
|
||||
| `title` | 1 | Non-whitespace display title, at most 512 UTF-8 bytes |
|
||||
| `op` | 1 | `create`, `update`, `move`, or `delete` |
|
||||
|
||||
`content` is opaque UTF-8 to the relay, subject to the relay's advertised event
|
||||
size and rate limits. Type names are lowercase ASCII dot-separated components,
|
||||
each starting with a letter and otherwise containing letters, digits, `_`, or
|
||||
`-`; total length is at most 128 bytes. `buzz.*` names are reserved by convention
|
||||
for published Buzz contracts, not proof of trusted authorship.
|
||||
|
||||
Identity is `(community, d)`, independent of signer, type, and home. It is not a
|
||||
NIP-01 addressable coordinate. The relay retains accepted revisions and an
|
||||
explicit current-head index; neither timestamps nor signer changes select a
|
||||
winner. `type` and `d` are immutable. A new business-schema version belongs in
|
||||
the content contract, not in a replacement artifact identity.
|
||||
|
||||
Unknown tags are bounded annotations. They MUST NOT override common tags or
|
||||
confer authority. Business relationship tags are specified by clients, not
|
||||
resolved or validated by this NIP. Unsupported envelope versions are rejected.
|
||||
Missing `h` is a rejection, never a fallback to global visibility.
|
||||
|
||||
The existing canvas kind (40100) provides a channel-scoped, non-chat precedent;
|
||||
see `required_scope_for_kind` and `requires_h_channel_scope` in
|
||||
`crates/buzz-relay/src/handlers/ingest.rs`. Artifact implementation must explicitly
|
||||
join that scoped class, not `is_global_only_kind`. Kind allocation alone does
|
||||
not establish unread, quota, or fan-out behavior: implementation must audit and
|
||||
test those paths. Artifacts remain subject to bounded storage/write quotas even
|
||||
though they do not count as chat messages.
|
||||
|
||||
## Conversations and multiple artifacts
|
||||
|
||||
Without `root`, the artifact lives directly in its channel. When supplied or
|
||||
changed, `root` must identify an existing, live (not deleted at commit time)
|
||||
conversation anchor in `h` in the same community. An unchanged anchor may
|
||||
subsequently become unavailable without preventing edits to the artifact.
|
||||
Clients must retain an unavailable-discussion state and allow an authorized
|
||||
writer to choose another anchor.
|
||||
|
||||
A nested conversation may be an anchor; its existing ancestry does not change.
|
||||
Replies use the conversation's existing protocol, not the latest artifact
|
||||
revision ID. An artifact revision is not a chat reply and MUST NOT increment
|
||||
reply counters, chat-message counts, or unread-chat state.
|
||||
|
||||
There is no exclusive slot, primary slot, or per-type singleton. Two tasks in
|
||||
one thread are two records, not competing declarations of what the thread is. A
|
||||
preferred card, pin, or dedicated workspace is a presentation choice, not
|
||||
uniqueness or authority. Archiving one project MUST NOT archive its channel or
|
||||
other artifacts in it.
|
||||
|
||||
## Authorization and concurrency
|
||||
|
||||
Channel read access controls artifact read access. Threads introduce no
|
||||
additional audience. This applies to direct-ID lookups, history, current state,
|
||||
search, counts, previews, exports, and live delivery, within the host-derived
|
||||
community boundary. No title or relationship metadata may leak through errors or
|
||||
unreadable search/count results.
|
||||
|
||||
The proposed generic write policy is **channel collaboration**. “Write
|
||||
permission” means the same authorization that admits a kind-9 message in that
|
||||
channel: channel membership of any role (including agents/Bot), or open
|
||||
visibility, plus existing authentication, token-channel restrictions,
|
||||
moderation, and archive gates. It is not a `MemberRole` threshold. Thus an
|
||||
otherwise authorized community participant may create/edit in an open channel
|
||||
without first joining it. “Administration” means Admin/Owner channel authority
|
||||
under existing channel checks. Implementation must choose the artifact API scope
|
||||
consistently across clients; message-equivalent channel authority must not
|
||||
exclude agents.
|
||||
|
||||
| Operation | Required authority at commit time |
|
||||
|-----------|------------------------------------|
|
||||
| Create | Write permission in destination channel |
|
||||
| Update content, title, or anchor within home | Write permission in current home |
|
||||
| Delete | Administration permission in current home |
|
||||
| Move to another channel | Write permission in source AND destination |
|
||||
|
||||
Being an artifact's creator is attribution, not a permanent exclusive editing
|
||||
right. A reader without write permission cannot edit. Removed members lose
|
||||
write permission in private channels even if they created the record; an open
|
||||
channel still follows the open-channel admission rule above. These rules do not vary by
|
||||
`type` and do not introduce per-artifact access lists or plugin-defined rules.
|
||||
Existing authenticated-agent/channel authorization applies; provenance alone
|
||||
must not create a new grant. Signed authorship remains the actual event signer.
|
||||
|
||||
Creation uses `op=create`, no `prev`, and an unused `d`. All subsequent operations
|
||||
must name the current accepted head in `prev`. Ordinary updates and deletion
|
||||
must keep `h`; only `move` may change it. A move must actually change channels.
|
||||
`op` MUST agree with these structural constraints; it cannot disguise a move as
|
||||
an update or an update as a new creation.
|
||||
The relay checks permission, previous head, envelope, and anchor, then stores
|
||||
the event and advances the head in one transaction. Two writes against the
|
||||
same head cannot both succeed. Knowing `prev` is not edit authorization.
|
||||
On conflict, clients must refetch the current accessible head and reconcile
|
||||
their intended change, not blindly retry or silently overwrite newer state. Retrying an accepted event ID is idempotent;
|
||||
permission to retrieve its contents is still checked.
|
||||
|
||||
Rejected writes never enter history, search, or live delivery. Error responses
|
||||
must not reveal an inaccessible current head or destination. The relay MUST reject NIP-09
|
||||
kind-5 requests targeting artifact revisions, with an explicit reason such as
|
||||
`invalid: use artifact delete operation`; it must not report successful deletion
|
||||
or release an artifact identity through that path.
|
||||
|
||||
Deletion is a terminal tombstone on the same identity, with empty content.
|
||||
Delete events permit only the common envelope tags and the existing validated
|
||||
authentication/provenance tags (such as NIP-OA `auth`), not annotation tags.
|
||||
This generic allowlist removes relationships without recognizing business types.
|
||||
Its type, `h`, `root`, and title must equal the head it deletes; deletion must
|
||||
not rename or relocate it. Its type and last title remain under its home
|
||||
permissions for an unavailable/deleted display. It is absent from active lists;
|
||||
subscribers receive a removal. Retained history follows retention/moderation
|
||||
policy; deletion is not a promise to erase downloaded copies. A deleted `d` is
|
||||
never reused. Business completion/archive is not deletion.
|
||||
|
||||
## Moving and audience changes
|
||||
|
||||
Moving preserves identity and publishes a complete current snapshot into the
|
||||
destination. The UI MUST identify the destination audience and what fields and
|
||||
context will be shared before confirmation. This includes titles, descriptions,
|
||||
relationship labels, attachments, and any private material copied into content.
|
||||
A reference does not automatically authorize copying its resolved contents.
|
||||
|
||||
The move revision is scoped to the destination. Its anchor must be absent or
|
||||
belong to that destination; the old conversation does not move with it. **Moving
|
||||
an artifact moves zero conversation messages.** Prior revisions stay under the
|
||||
visibility of the channel in which each was published. A destination-only reader
|
||||
can load the current snapshot without reading the old chain. A source-only
|
||||
reader retains access to permitted source history, not later destination
|
||||
revisions. Moves cannot revoke previously learned data.
|
||||
|
||||
One commit must advance the single head and durably record changes for both
|
||||
scopes: removal/invalidation in the source and arrival in the destination. These
|
||||
are two scoped change records, not one signed event pretending to have two `h`
|
||||
values. The destination carries the client-signed revision; the source uses a
|
||||
relay-authenticated invalidation in the platform sync change envelope. That
|
||||
envelope must bind community, source scope, artifact identity, and replay
|
||||
position, with no destination payload. If transported as a Nostr event instead,
|
||||
it requires a separately allocated relay-only sidecar kind and rejection of
|
||||
client-authored substitutes. Its wire schema is a sync-integration release gate,
|
||||
not an already allocated part of kind 45010. Publication must be retryable after
|
||||
a crash. The source removal must not contain the destination channel, title, or
|
||||
payload; a forward link may resolve only with the requesting reader's
|
||||
destination permissions. Concurrent edits/moves cannot create two active copies.
|
||||
Moving back to a former home still uses the one current head.
|
||||
|
||||
Channel membership and open/private visibility changes are also audience
|
||||
changes: artifact access follows channel policy, rather than being frozen to the
|
||||
membership at creation. Every subsequent read and delivery must honor the new
|
||||
access policy; clients must invalidate cached views when access is revoked.
|
||||
Offline copies cannot be recalled; UI must not promise otherwise.
|
||||
|
||||
## Current state, history, and sync
|
||||
|
||||
Use the generic signed-event ingestion path, not artifact-specific CRUD APIs.
|
||||
Artifact changes belong in channel/thread sync alongside other scoped changes,
|
||||
but **applied updates and human read positions are different state**. Receiving
|
||||
an update neither marks conversation read nor necessarily notifies someone.
|
||||
Explicit notification policy may call attention to meaningful changes. Clients
|
||||
MUST NOT add the artifact kind to conversational-unread kind sets; rendering a
|
||||
card alongside `root` is a separate presentation decision.
|
||||
|
||||
A compliant implementation must offer:
|
||||
|
||||
1. Bounded, authorized current-state queries without replaying all revisions or
|
||||
conversation history, including for a newly joined destination after a move.
|
||||
2. Explicit revision-history queries, distinct from current-state listing.
|
||||
3. Snapshot-to-live handoff with no gaps, durable replay after disconnect,
|
||||
tombstones/removals, and a reset/snapshot path for expired checkpoints.
|
||||
4. Current-head filtering before pagination and counting. A task removed from
|
||||
project P must not still match P because an older revision carried its tag.
|
||||
5. Aggregate-view invalidation for transitions OUT of a filter, not just events
|
||||
that still match it. This includes project/assignee/anchor changes, moves, deletion,
|
||||
and revoked access. No previously inaccessible content may be disclosed.
|
||||
6. Bounded discovery of readable artifacts across channels; loading a task list
|
||||
must not require opening every thread. Channel visibility still gates results.
|
||||
|
||||
Multi-character tag filters are a Buzz extension, not portable NIP-01 behavior.
|
||||
Unsupported predicates must be rejected, never silently ignored. `EOSE` is not a
|
||||
durable applied checkpoint. A cursor must identify its covered stream/filter; a
|
||||
filtered result cannot stand in for a complete channel checkpoint.
|
||||
|
||||
**Implementation boundary:** this draft specifies those observable guarantees,
|
||||
not a second sync protocol. The current-state query mode, snapshot/checkpoint
|
||||
wire shape, move-removal delivery, and expired-cursor/reset response must be
|
||||
bound to the platform sync specification before implementing or advertising NIP-
|
||||
AR support. Ordinary timestamp-sorted `REQ` history alone does not satisfy this
|
||||
contract. Until move delivery is supported, relays must reject `move`, not
|
||||
approximate it as independent delete/create writes.
|
||||
|
||||
## Client types and privileged operations
|
||||
|
||||
[Artifact client contracts](NIP-AR-CLIENTS.md) define the initial Buzz types.
|
||||
The relay accepting `type=buzz.task` does not certify a valid task. Clients must
|
||||
validate before interpreting business fields; unsupported or malformed content
|
||||
falls back to a safe card. Render title/type as untrusted text, not executable
|
||||
markup. A type namespace is not a publisher identity or installed capability.
|
||||
|
||||
Unknown versions must not be edited destructively. Editors must preserve unknown
|
||||
content fields and annotation tags when safe, or decline editing that version.
|
||||
Relations may be missing, inaccessible, deleted, self-referential, or cyclic.
|
||||
Traversals must be bounded and fail visibly without crashing.
|
||||
|
||||
Links and payloads cannot authorize push, merge, membership changes, payments,
|
||||
or another privileged action. Those actions require their own trusted service
|
||||
checks. A client-observed status is not a relay-certified operational fact.
|
||||
|
||||
## Existing Git and project objects
|
||||
|
||||
This proposal does not create a second Git repository or replace NIP-34
|
||||
identity. Repository announcements (30617), ref state (30618), reviews, patches,
|
||||
and their statuses remain their existing protocol objects. Branch artifacts
|
||||
describe collaboration around a ref, not authoritative Git state.
|
||||
|
||||
**Legacy exception, not completed consolidation:** existing global
|
||||
project/issue/ repository objects do not acquire private channel reads or
|
||||
channel sync simply because they contain `buzz-channel`. A push binding alone is
|
||||
insufficient. NIP-34 read, search, clone/fetch, preview, and live-delivery
|
||||
scoping require a separate coordinated migration before promising private
|
||||
repository workspaces. This draft does not tighten or widen existing Git access
|
||||
as a side effect.
|
||||
|
||||
Existing 30621 projects and 1621 issues must not be dual-written as competing
|
||||
new artifacts. Migration requires an explicit canonical mapping and cutover,
|
||||
retaining old links, ownership information, and existing access until changed
|
||||
through an authorized migration. Previously published data cannot be made secret
|
||||
retroactively. No automatic conversion is specified here.
|
||||
|
||||
## Acceptance scenarios
|
||||
|
||||
These are implementation requirements, not tests claimed to pass in this PR.
|
||||
|
||||
| Scenario | Required result |
|
||||
|----------|-----------------|
|
||||
| Two projects or tasks at the same location | Both valid and separately discoverable |
|
||||
| Task without project or repository | Valid |
|
||||
| Another channel writer edits an artifact | Valid with current head; author accurately recorded |
|
||||
| Read-only principal or removed creator in a private home edits | Rejected |
|
||||
| Non-member permitted to post in an open home edits | Valid under the same message-admission gates |
|
||||
| Two revisions race on one head | Exactly one accepted |
|
||||
| A task joins/leaves a project in another channel | Same identity/home; both affected views refresh; no access grant |
|
||||
| Malformed known type or unknown type/version | Safe fallback; no destructive edit |
|
||||
| Cyclic parent references | Bounded client handling; relay does not walk the graph |
|
||||
| Move races edit; relay crashes after commit | One head; durable source removal and destination arrival |
|
||||
| Destination member cannot read source | Can load current state, not source history |
|
||||
| Source member cannot read destination | Receives removal, not destination metadata |
|
||||
| Access revoked while a request is in flight | No newly unauthorized delivery; stale local view invalidated |
|
||||
| Reconnect after deletion or filter exit | No ghost current records |
|
||||
| Background revision arrives | State updates; chat unread/read position unchanged |
|
||||
| Generic repository tag on a task | No push, clone, review, or membership capability gained |
|
||||
|
||||
## Non-goals and naming
|
||||
|
||||
No per-artifact audience lists, relay-executed plugins, schema-registration
|
||||
service, generic automation engine, or one-record-per-room requirement. Existing Buzz DMs are channels: an artifact with their `h` inherits participant
|
||||
access just like their messages (see `handle_dm_open` in
|
||||
`crates/buzz-relay/src/handlers/command_executor.rs`). This is not a new DM ACL.
|
||||
Administrative deletion remains gated by actual channel administration; merely
|
||||
being a DM participant does not manufacture that role. Personal/no-room artifacts
|
||||
need a separately specified scope; missing `h` must not become an accidental
|
||||
global/private mode.
|
||||
|
||||
“Artifact” is the protocol/design term. User interfaces should usually say
|
||||
“task,” “project,” or the specific type name. “Work item” sounds task-specific;
|
||||
“card” describes a presentation. Neither is a better common identity name.
|
||||
Build-output artifacts/files remain distinct from these editable records.
|
||||
Reference in New Issue
Block a user