Forge
markdown3407750f
1# FORGE-ADR-0021: Repository groups (namespace spine)
2
3| | |
4|---|---|
5| **Status** | Accepted (catalog layer; path prefix superseded by [0024](FORGE-ADR-0024-orgs-visibility-and-repo-catalog.md)) |
6| **Date** | 2026-06-12 · amended 2026-06-14 |
7| **Relates to** | [FORGE-ADR-0001](FORGE-ADR-0001-agent-first-not-gitlab-clone.md), [FORGE-ADR-0006](FORGE-ADR-0006-auth-vpn-and-plugins.md), [FORGE-ADR-0009](FORGE-ADR-0009-agent-driven-vs-human-driven-repos.md), [FORGE-ADR-0015](FORGE-ADR-0015-vertical-domain-plugins-and-doi-commands.md) §6.1, [FORGE-ADR-0017](FORGE-ADR-0017-repository-import-export-zoo.md), [FORGE-ADR-0020](FORGE-ADR-0020-commercial-plugins-and-repo-boundaries.md) |
8| **Target** | v0.6 (flat groups) · v0.7+ (nested, ACL inheritance) |
9
10## Context
11
12Self-hosted **Forge** держит все git-репозитории в одном WitDB. Сегодня список **плоский**: `ForgeRepoEntity.Name`, `list_repos` без иерархии ([`ForgeRepoEntity`](../src/AgentForge/Data/Entities/ForgeRepoEntity.cs)).
13
14На **GitHub** org тоже плоский список репо; вложенных **Repository Group** нет ([0020 §4](FORGE-ADR-0020-commercial-plugins-and-repo-boundaries.md)). Частый вопрос: **«а GitHub Projects — не оно?»** — **нет** (см. §2).
15
16**GitLab Groups/Subgroups** — ближайший аналог **namespace для git**: path `group/subgroup/repo`, наследование прав, UI-папка. Forge import ([0017](FORGE-ADR-0017-repository-import-export-zoo.md)) из GitLab естественно мапит subgroup → forge group.
17
18**Зачем spine, не zoo:** без namespace multi-repo forge (десятки agent/human repos) превращается в flat dump; агенту нужен routing hint (`create in cad/`, `list agents/*`). Это **git/hosting**, не sprint board.
19
20**Не путать с Plugin.Projects** ([0015 §6.1](FORGE-ADR-0015-vertical-domain-plugins-and-doi-commands.md)): work-контейнер **над issue/MR** (Jira Space) — zoo, links, не ownership git path.
21
22## Decision
23
24### 1. Три разных «Project / Group» (terminology lock)
25
26| Concept | Platform | Что группирует | Меняет URL git? | ACL на repos? |
27|---------|----------|----------------|-----------------|---------------|
28| **Repository Group** | **Forge spine** (this ADR) | **Git repos** | ✓ (`cad/models`) | ✓ (v0.7+) |
29| **GitHub Projects** | GitHub | **Issues + PRs** (work items) | ✗ | ✗ |
30| **Plugin.Projects** | Forge zoo | **Issues/MR** (initiative) | ✗ | ✗ (links only) |
31| **GitLab Group** | GitLab | **Git repos** (+ CI, registry) | ✓ | ✓ |
32| **GitHub org** | GitHub | Account boundary | `org/repo` only | teams on repos |
33
34**GitHub Projects (v2):** org/user **board/table** — туда **добавляют** issues и pull requests из разных репо; kanban, fields, views. Репозитории **не** становятся дочерними, **не** получают общий path prefix, **не** наследуют permissions через project. Это **work portfolio**, ближе к **Forge Plugin.Projects**, чем к Repository Group.
35
36**Forge naming:** в docs и MCP — **`repo_group`** / **Repository Group**; слово **Project** зарезервировано за work zoo ([0015 §6.1](FORGE-ADR-0015-vertical-domain-plugins-and-doi-commands.md)).
37
38### 2. Repository Group — определение
39
40**Repository Group** = **catalog folder** on an **org profile** ([0024](FORGE-ADR-0024-orgs-visibility-and-repo-catalog.md)) — groups repos for humans/agents in UI and MCP filters:
41
42- stable **slug** unique **within org** (`showcase`, `backup`, `zoo`);
43- optional **parent group** (nested v0.7+);
44- optional **default visibility** / **drive_mode** hints for new repos;
45- **does not** change git path in v0.6 (correction 2026-06-14).
46
47**Org** owns git namespace and membership; **Repository Group** does not substitute org.
48
49**Canonical repo identity** (v0.6):
50
51```
52fullName = "{orgSlug}/{repoSlug}" e.g. ai-guiders/agent-forge
53catalog = repo_group slug on org e.g. showcase (UI only)
54```
55
56**Root group:** repos with empty `groupPath` keep today’s flat names (`agent-forge`) for backward compatibility.
57
58**Git on disk:** `FORGE_REPOS_PATH/{fullName-with-slashes-as-dirs}/` — slashes = directories, not separate org accounts.
59
60### 3. WitDB schema (spine)
61
62**v0.6 — flat groups only:**
63
64```sql
65-- repo_groups
66id, slug, display_name, description, created_at
67-- repos (extend ForgeRepoEntity)
68group_id NULL, slug, full_name UNIQUE, drive_mode, ...
69```
70
71**v0.7 — nested:**
72
73```sql
74-- repo_groups + parent_group_id NULL | FK
75-- materialized path or ltree-style group_path for queries
76```
77
78**Invariants:**
79
80- `full_name` globally unique on host (like GitLab path).
81- `slug` unique **within parent group**.
82- Move repo between groups = admin API; git dir rename + WitDB transaction.
83- Issue/MR rows stay **repo-scoped**; no `group_id` on issues.
84
85### 4. API and MCP (kernel)
86
87| Operation | REST | MCP (DOI) |
88|-----------|------|-----------|
89| List groups | `GET /api/v1/repo-groups` | `forge.repo_group.list` |
90| Create group | `POST /api/v1/repo-groups` | `forge.repo_group.create` |
91| Get group | `GET /api/v1/repo-groups/{path}` | `forge.repo_group.get` |
92| List repos in group | `GET /api/v1/repo-groups/{path}/repos` | `forge.repo.list` + `group` filter |
93| Create repo | `POST /api/v1/repos` body `{ group, name, driveMode }` | `forge.repo.create` |
94| Existing | `GET /api/v1/repos/{fullName}` | `list_repos`, `get_repo` — **accept slash names** |
95
96**Backward compat:** bare `name` without `/` = root-level repo; existing clients unchanged until they opt into groups.
97
98**Slash commands (ViewShell, Phase B′+):** `/repo create cad/models`, `/group list`.
99
100### 5. Auth
101
102| Phase | Rule |
103|-------|------|
104| **v0.6** | Same as today per repo; group = **organizational label** only |
105| **v0.7** | Capability `ForgeRepoGroupAdmin`; optional **inherit** `read`/`write` from group to member repos |
106| **Zoo** | LDAP/SSO group ↔ forge repo_group sync — **Plugin.EnterpriseAuth**, not core |
107
108Enforce in handler ([0015 §3.4](FORGE-ADR-0015-vertical-domain-plugins-and-doi-commands.md)): catalog declares capability; REST checks on mutate.
109
110### 6. Human + agent UX
111
112**ViewShell (standard):** home `/view` — tree **Groups → Repos** instead of flat list only; badge `drive_mode` unchanged ([0009](FORGE-ADR-0009-agent-driven-vs-human-driven-repos.md)).
113
114**Agent routing:** MCP `forge.repo.create` with `group: "agents"` for sandboxes; policy docs per group in `.forge/group.md` (optional convention, not core schema v0.6).
115
116**CIDE:** bracket paths `forge://cad/models/issue/5` — future; v0.6 fullName in URLs sufficient.
117
118### 7. Import / export mapping
119
120| Source | Target | Importer |
121|--------|--------|----------|
122| GitLab `group/sub/repo` | forge `group/sub/repo` | [Repository.Import.Organization.GitLab](FORGE-ADR-0017-repository-import-export-zoo.md) |
123| GitHub org `org/repo` | forge `{forgeOrgSlug}/{repoSlug}` when org set ([0024](FORGE-ADR-0024-orgs-visibility-and-repo-catalog.md)) | [Repository.Import.Organization.GitHub](FORGE-ADR-0017-repository-import-export-zoo.md) — visibility **`inherit` opt-in** via TOML/env |
124| GitHub org (legacy flat) | forge repo at root **or** catalog group named `org` | importer option `flatLegacy` |
125| GitHub org topics | **tags** on repo/group metadata — **not** groups ([0020](FORGE-ADR-0020-commercial-plugins-and-repo-boundaries.md)) | — |
126
127[0017](FORGE-ADR-0017-repository-import-export-zoo.md) Organization importers support optional `preserveGroupPath` (GitLab) and `targetRepoGroup` (catalog only).
128
129### 8. Monetization
130
131| Layer | License |
132|-------|---------|
133| Flat + nested groups, API, tree UI | **MIT spine** |
134| LDAP↔group sync, quota per group, compliance «all regulated/*» | **Zoo** ([0015 §8.6](FORGE-ADR-0015-vertical-domain-plugins-and-doi-commands.md)) |
135
136External **GitHub org layout** ([0020](FORGE-ADR-0020-commercial-plugins-and-repo-boundaries.md)) and **internal forge groups** are **independent**: one host can mirror `AI-Guiders/forge-zoo-cad` → group `zoo/cad` without GitHub having subgroups.
137
138### 9. Phased delivery
139
140| Phase | Deliverable |
141|-------|-------------|
142| **G0** | ADR + `ForgeRepoGroupEntity`; migration `group_id` nullable |
143| **G1** | CRUD groups, `create_repo` with group, slash names in git paths |
144| **G2** | ViewShell tree; MCP `forge.repo_group.*`; `list_repos` filter |
145| **G3** | Nested groups + ACL inheritance |
146| **G4** | `Repository.Import.Organization.*` + mapping ([0017](FORGE-ADR-0017-repository-import-export-zoo.md)) |
147
148**Not v0.5 blocker** — follows repo spine hardening (auth [0006], transport [0004]).
149
150## Consequences
151
152### Positive
153
154- Self-hosted forge получает то, чего **нет у GitHub org** — namespace для git.
155- Чёткое отличие от **GitHub Projects** и **Plugin.Projects** — меньше путаницы в docs/MCP.
156- GitLab migration path без «всё в flat list».
157- Agent-first: группы как **policy boundary** (`agents/*` vs `product/*`).
158
159### Negative / trade-offs
160
161- Slash in repo names — escape rules в URLs, MCP, git remote URLs.
162- Move/rename group — filesystem + WitDB coordination.
163- Nested ACL — complexity; defer to G3.
164
165## Alternatives considered
166
167| Alternative | Why not |
168|-------------|---------|
169| Prefix-only (`cad-models`) | No hierarchy, weak ACL, ugly renames |
170| Tags on repo instead of groups | No inheritance, no tree, agent routing weak |
171| Repository Group as zoo plugin | Every serious host needs namespace; too late as paid gate |
172| Use GitHub Projects as model | Wrong layer — work items, not git repos |
173| Merge with Plugin.Projects | Couples git path to work graph; violates [0015 §6.1](FORGE-ADR-0015-vertical-domain-plugins-and-doi-commands.md) links-not-FK |
174
175## Open questions
176
1771. **Default group for new repos** — `""` root vs mandatory group? → v0.6 **optional**; empty = root (compat).
1782. **Max nesting depth** — GitLab allows many; forge v0.7 cap **5**? → decide in G3.
1793. **Group-owned wiki/settings** — v0.6 **no**; only repo rows; group `.md` convention optional.
180
181## Links
182
183- [FORGE-ADR-0015 §6.1](FORGE-ADR-0015-vertical-domain-plugins-and-doi-commands.md) — Plugin.Projects (work zoo)
184- [FORGE-ADR-0020 §4](FORGE-ADR-0020-commercial-plugins-and-repo-boundaries.md) — external GitHub org vs GitLab subgroups
185- [FORGE-ADR-0009](FORGE-ADR-0009-agent-driven-vs-human-driven-repos.md) — `drive_mode` per repo (unchanged)
186- [GitHub Projects docs](https://docs.github.com/en/issues/planning-and-tracking-with-projects) — issues/PRs, not repo namespace
187
188**One line:** **Repository Group** = git namespace on forge (GitLab-like); **GitHub Projects** = board over issues/PRs; **Plugin.Projects** = work initiative over issues — три разных слоя, не substitutes.
189
View only · write via MCP/CIDE