block / block/buzz

Desktop: optional repository name on Create project

Open
#6,209 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

**Motivation**

Create project slugifies **Name** and writes that slug plus display string onto both the project (`kind:30621`) and the first repository (`kind:30617`). A workspace that is one project with several repositories therefore gets the first repo’s identity as the project identity.

That contradicts the product model in `VISION_PROJECTS.md` / NIP-MP (project `platform`, members `buzz` and `buzz-infra`). **Add repository** already has its own name. Create project is the leftover coupling.

Who hits it: anyone creating a multi-repo project from Desktop (for example project **Bee Garden**, first repo `anhle128/buzz`).

Duplicate search: none found. Closest related work is #5052 (owner-signed repository creation), not this naming split.

**Proposed solution**

Add an optional **Repository name** field to the Desktop **Create a new project** dialog. Empty keeps today’s events. Filled names only the first repository.

### UI

Field order:

1. **Name** (required) — unchanged
2. **Repository access channel** — unchanged
3. **Description** — unchanged
4. **Repository name** — new, optional
5. **Initial repository clone URL** — unchanged
6. **Initial repository web URL** — unchanged

- Label `Repository name` plus the existing `Optional` marker
- Field starts empty on every open (no live prefill from **Name**)
- Helper: `Defaults to the project name.`
- Placeholder: `bee-garden-ios`
- `data-testid="create-project-repository-name"`
- Submit does not require it

### Event mapping

Publish order stays `kind:30617` then `kind:30621`.

| Input | Project `30621` | First repo `30617` |
|-------|-----------------|--------------------|
| **Name** only | `d` + `name` from **Name** | Same as project (today) |
| **Name** + **Repository name** | Still only from **Name** | `d` + `name` from **Repository name** |

Slug: existing `repositoryDtagFromName` (lowercase, `[^a-z0-9]+` → `-`, trim dashes).

`/` is allowed in the **display** `name` tag only. The relay and `/git//` require a single `[a-zA-Z0-9._-]{1,64}` `d` tag, so `/` must not appear in the repo id.

Example: **Name** `Bee Garden`, **Repository name** `anhle128/buzz`

- Project: `d=bee-garden`, `name=Bee Garden`
- Repo: `d=anhle128-buzz`, `name=anhle128/buzz`
- Project `a` tag: `30617::anhle128-buzz`

Do not autofill clone/web URLs from `owner/repo`. Description, access channel, and URLs stay as they are.

### Write path

- `CreateProjectInput` / `buildInitialProjectEventTemplates` gain `repositoryName?: string`
- `InitialProjectEventTemplates.dtag` remains the **project** slug; add `repositoryDtag`
- Move `repositoryDtagFromName` into `projectCreation.ts` and re-export from `projectRepositoryCreation.ts` (avoid a second slugger and an import cycle)
- Project collision / resume stay keyed on the project slug
- When `repositoryDtag !== dtag`, fetch `kinds:[30617] authors:[owner] #d:[repositoryDtag] limit:1` and refuse if a head exists (same clobber copy as **Add repository**). Skip that query when the slugs are equal so today’s legacy/resume path is unchanged

### Validation

| Situation | Result |
|-----------|--------|
| Filled name slugs to empty (`///`) | `Repository name must include letters or numbers.` No fallback |
| Display name > 256 bytes | `Repository name must not exceed 256 bytes.` |
| Slug > 64 characters | `Repository name slug must not exceed 64 characters.` |
| Distinct slug, existing `30617` | clobber error; publish nothing |

A filled field that cannot become a valid slug is an error, not a silent copy of the project name.

**Alternatives considered**

- Required repository name — rejected; empty must stay compatible with current tests and the single-repo case.
- Live-prefill the field from **Name** — rejected; blank plus helper is the honest default.
- Derive `owner/repo` from clone/web URL — rejected; no URL autofill in this slice.
- Skip creating the initial repo when the name is empty — rejected; still publish a first `30617` named after the project.
- Put `/` in the repo `d` tag — rejected; relay `validate_repo_id` and the git path are one segment.
- Split the dialog into Project vs Initial repository groups — rejected; smallest override on the current form.

**Additional context**

- Desktop only. Not CLI (`buzz projects create` already takes a slug plus `--repo`), mobile (no create-project form), web, relay ingest, or **Add repository**.
- Design write-up (local, not yet on `block/buzz` main): `docs/superpowers/specs/2026-08-18-create-project-repository-name-design.md`

### Tests

- Extend `desktop/src/features/projects/projectCreation.test.mjs` for empty vs `anhle128/buzz`, `///`, and 65-char slug.
- Add `desktop/src/features/projects/useCreateProject.test.mjs` for the clobber fetch (inject seams like `useAddProjectRepository.test.mjs`).
- Existing create-project e2e that only fill **Name** stay green. Add dialog coverage for the optional field and helper text.

### Success criteria

- **Bee Garden** + `anhle128/buzz` → project named Bee Garden, first repo display `anhle128/buzz`, git id `anhle128-buzz`.
- **Name** only → one shared slug, same events as today.
- `///` or a slug that collides with another owned `30617` does not publish.
- Relay repo ids never contain `/`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.