Forge
markdowndeeb25a2
1# ADR 0159: Bracket `[FRG:…]` — ссылки на артефакты Forge (расширение 0128)
2
3**Статус:** Accepted · Design
4**Дата:** 2026-06-11
5
6## Резюме
7
8- Расширить **bracket-нотацию** ([0128](0128-intercom-attachment-anchors-and-code-references.md) §5) **второй семьёй** токенов **`[FRG:…]`** — ссылки на forge-артефакты (repo, issue, MR), **ортогонально** code-bracket `[F:…; M:…; L:…; S:…]`.
9- **Код** по-прежнему → `AttachmentAnchor` / `CodeAnchor`; **forge** → `ForgeArtifactRef` (wire).
10- **Prose / markdown / Intercom / slash** — bracket; **браузер / OS** — `forge://` ([FORGE-ADR-0007](../../../agent-forge/design/FORGE-ADR-0007-forge-magic-link-protocol.md)); **MCP** — structured JSON (как у attach).
11- Один parse tree, два discriminant: `FRG` vs code (`F`/`M`/`L`/`S`).
12
13## Связанные ADR
14
15| ADR | Роль |
16|-----|------|
17| [0128](0128-intercom-attachment-anchors-and-code-references.md) | `AttachmentAnchor`, оси `F`/`M`/`L`/`S`, attach/reveal |
18| [0131](0131-editor-slash-select-code-by-bracket-reference.md) | `/editor select code [M:…]` — shared code parse |
19| [0156](0156-correspondence-mfd-surface-and-reverse-code-anchors.md) | Bracket / CodeAnchor в correspondence |
20| [0157](0157-cide-magic-link-protocol.md) | `cide://reveal?…&b=…` — bracket в query |
21| [0158](0158-forge-lens-crs-overlay.md) | Forge Lens в CRS |
22| [FORGE-ADR-0003](../../../agent-forge/design/FORGE-ADR-0003-forge-lens-cide-code-anchors.md) | Lens, storage `anchors[]`, MCP-first write |
23| [FORGE-ADR-0007](../../../agent-forge/design/FORGE-ADR-0007-forge-magic-link-protocol.md) | `forge://` deep links |
24| [FORGE-ADR-0012](../../../agent-forge/design/FORGE-ADR-0012-bracket-notation-and-lens-anchors.md) | Forge storage, thin web, round-trip |
25
26---
27
28## Контекст
29
30[0128](0128-intercom-attachment-anchors-and-code-references.md) зафиксировал: slash, скобки, chips и MCP сходятся в **один wire-тип** для **кода** (`AttachmentAnchor`). Оси bracket — `F`, `M`, `L`, `S`.
31
32[FORGE-ADR-0003](../../../agent-forge/design/FORGE-ADR-0003-forge-lens-cide-code-anchors.md) добавляет **forge-артефакты** (issue, MR) с тем же `CodeAnchor` в metadata, плюс `forge://` для навигации.
33
34**Проблема v0.3:** forge pilot показывает anchors как `file:line`; агент и оператор мыслят **`[M:…]`** и **`[FRG:…]`**, не JSON и не сырой path. Три параллельных «языка» (Intercom bracket, forge JSON, web `file:line`) ломают принцип §5.1 0128.
35
36**Решение:** не добавлять ось `I:` внутрь code-bracket (смешивает artifact и code), а ввести **отдельный namespace** `FRG:` — симметрично тому, как `cide://` и `forge://` разделены по схеме, но связаны делегированием.
37
38---
39
40## Решение
41
42<a id="adr0159-p1"></a>
43
44### 1. Две семьи bracket
45
46| Семья | Пример | Wire | Эффект клика |
47|-------|--------|------|--------------|
48| **Code** | `[M:Run]`, `[F:src/Foo.cs; M:Run; S:for:2]` | `AttachmentAnchor` | `intercom.reveal_attachment` / `cide://reveal` / editor select |
49| **Forge** | `[FRG:pilot/issues/7]`, `[FRG:pilot/mr/3]` | `ForgeArtifactRef` | Forge Lens panel / `forge://` / HTTPS view |
50
51**Не смешивать** в одной оси: code-bracket **не** содержит номер issue; forge-bracket **не** заменяет `F`/`M`/`L`/`S`.
52
53<a id="adr0159-p2"></a>
54
55### 2. Грамматика `[FRG:…]` (v1)
56
57Форма (BNF-подобно):
58
59```text
60forge-bracket ::= "[FRG:" forge-path "]"
61forge-path ::= repo "/" kind "/" number [ ";" code-tail ]
62repo ::= slug (URL-safe, как в forge API)
63kind ::= "issues" | "mr" | "repos"
64number ::= 1..9 digit+
65code-tail ::= тот же L2, что 0128 §5.1 (F/M/L/S), **без** повторного "["
66```
67
68Примеры:
69
70```text
71[FRG:pilot/issues/1]
72[FRG:cad-tools/mr/3]
73[FRG:pilot/repos/pilot]
74[FRG:pilot/issues/7; F:src/Foo.cs; M:CreateIssue]
75```
76
77| Форма | Смысл |
78|-------|--------|
79| `…/issues/N` | Issue #N в repo |
80| `…/mr/N` | Merge request #N |
81| `…/repos/{slug}` | Repo profile (редко в prose) |
82| `; F:…; M:…` *(optional)* | **Primary code anchor** inline — удобно в одной скобке в body issue; в wire всё равно split: `ForgeArtifactRef` + `AttachmentAnchor` |
83
84**Короткая форма (v1.1, optional):** `[FRG:#7]` при известном default `repo` из `[workspace.forge]` — только CIDE/slash, не в persisted forge body без repo.
85
86<a id="adr0159-p3"></a>
87
88### 3. Wire: `ForgeArtifactRef`
89
90Минимальный контракт (JSON; shared с forge API):
91
92| Поле | Обязательность | Смысл |
93|------|----------------|--------|
94| `repo` | да | Repo slug |
95| `kind` | да | `issue` \| `mr` \| `repo` |
96| `number` | для issue/mr | 1-based номер |
97| `primaryAnchor` | нет | `AttachmentAnchor` при compound bracket |
98
99**Code anchors на issue** (массив) остаются **`AttachmentAnchor[]`** / `CodeAnchor[]` — не вложены в `ForgeArtifactRef`, кроме optional `primaryAnchor` для inline compound.
100
101<a id="adr0159-p4"></a>
102
103### 4. Parse → resolve → act
104
105```
106Prose "[FRG:…]" → BracketForgeReferenceParser
107 → ForgeArtifactRef
108 → forge_lens.open | forge://issue | HTTPS fallback
109
110Prose "[F:…; M:…]" → BracketCodeReferenceParser (0128/0131)
111 → AttachmentAnchor
112 → reveal / select / attach
113```
114
115| Поверхность | Code bracket | Forge bracket |
116|-------------|--------------|---------------|
117| Intercom attach | да | **нет** v1 *(forge — не chat attachment)* |
118| Issue/MR body (forge) | да | да |
119| ADR / docs markdown | да | да |
120| `/editor select code` | да | нет |
121| `/forge goto [FRG:…]` | — | да |
122| MCP | `anchor_json` / поля | `forge_ref_json` или bracket string |
123
124<a id="adr0159-p5"></a>
125
126### 5. Связка с `forge://` и `cide://`
127
128| Bracket | Magic link (canonical build) |
129|---------|------------------------------|
130| `[FRG:pilot/issues/7]` | `forge://{host}/issue?repo=pilot&n=7` |
131| `[FRG:pilot/mr/3]` | `forge://{host}/mr?repo=pilot&n=3` |
132| `[FRG:pilot/issues/7; F:src/Foo.cs; M:Run]` | `forge://{host}/lens?repo=pilot&n=7&root=…&b=F%3A…%3B%20M%3A…` → делегирует [0157](0157-cide-magic-link-protocol.md) |
133
134**Thin web:** рендер bracket как `<a href="https://…/view/…" data-forge-uri="forge://…">` — HTTPS первым ([FORGE-0007](../../../agent-forge/design/FORGE-ADR-0007-forge-magic-link-protocol.md)).
135
136**Display label** (UI): `[FRG:pilot/issues/7]` или chip «issue #7 · pilot» — derived from wire, не hand-authored.
137
138<a id="adr0159-p6"></a>
139
140### 6. Code anchor: полный `AttachmentAnchor`
141
142Forge Lens storage и отображение **не** используют урезанный `file:line`:
143
144- wire / DB: те же поля, что [0128 §3](0128-intercom-attachment-anchors-and-code-references.md) (`file`, `memberKey`, `lineStart`, `lineEnd`, `syntaxScope`, …);
145- human/agent surface: **`BracketCodeReferenceSerializer`** ↔ `AttachmentAnchor` (symmetric parse/format);
146- subset в correspondence index: `CodeAnchor` ([0156](0156-correspondence-mfd-surface-and-reverse-code-anchors.md)).
147
148Пример в issue body:
149
150```markdown
151Регрессия в [F:src/hello.py; L:4-5; M:main] — см. [FRG:pilot/issues/1].
152```
153
154Structured block в metadata (FORGE-0012) дублирует anchors[]; body может содержать те же bracket для читаемости.
155
156<a id="adr0159-p7"></a>
157
158### 7. Парсер: discriminant
159
1601. Если inner начинается с `FRG:` (case-sensitive) → **forge family**.
1612. Иначе → **code family** (0128 §5.1).
162
163Общий entry point: `BracketReferenceParser.TryParse(string)` → `BracketReferenceKind.Code | Forge`.
164
165**Fenced code** ([0129](0129-intercom-message-body-markdown-and-fenced-code.md)): обе семьи **не** парсятся внутри fenced — как attach в 0128.
166
167<a id="adr0159-p8"></a>
168
169### 8. Фазы
170
171| Фаза | Содержание | Репо |
172|------|------------|------|
173| **0** | Этот ADR + [FORGE-0012](../../../agent-forge/design/FORGE-ADR-0012-bracket-notation-and-lens-anchors.md) | cascade-ide, agent-forge |
174| **1** | `ForgeArtifactRef` + parse/format unit tests; `ForgeLinkBuilder` bracket ↔ `forge://` | agent-forge |
175| **2** | Thin web: anchor list as code-bracket; issue link as `[FRG:…]` | agent-forge |
176| **3** | CIDE: `/forge goto`, markdown expander для `[FRG:…]`; CRS chip | cascade-ide |
177| **4** | Intercom: опционально «copy as [FRG:…]» из Lens (не attach) | cascade-ide |
178
179---
180
181## Последствия
182
183### Positive
184
185- Один mental model: **код** = `[F/M/L/S]`, **forge** = `[FRG:…]` — как Intercom, без третьего dialect.
186- Агент пишет bracket в issue body; MCP может слать JSON — round-trip.
187- Symmetry с [0157](0157-cide-magic-link-protocol.md) / [FORGE-0007](../../../agent-forge/design/FORGE-ADR-0007-forge-magic-link-protocol.md).
188
189### Negative / trade-offs
190
191- Два парсера / один facade — нужны тесты на `[FRG:…]` vs `[F:…]` (false positive маловероятен: `FRG:` vs `F:`).
192- Compound bracket `[FRG:…; F:…]` — optional; можно отложить до v1.1.
193- Intercom attach forge v1 **не** входит — issue refs живут в forge/docs, не в chat event log.
194
195## Alternatives considered
196
197| Alternative | Why not |
198|-------------|---------|
199| Ось `I:` внутри `[F;M;L;S]` | Смешивает artifact и code; ломает 0128 §5.1 |
200| Только `forge://` без bracket | Плохо для агента, Intercom, plain markdown |
201| Только `file:line` в web | Нет re-resolve, не AttachmentAnchor |
202| `[FRG:]` как attach в Intercom v1 | Scope creep; forge refs — persistence layer |
203
204---
205
206## История
207
208| Дата | Изменение |
209|------|-----------|
210| 2026-06-11 | Accepted (design): `[FRG:…]` + полный AttachmentAnchor display для Lens. |
211
View only · write via MCP/CIDE