docs(specs): drop type/status from folder names — header + Roadmap only

Folder names were NNN-T-S-NAME, so the status (S) letter forced a rename on
every status change — and refs to the full name went stale each time. One was
already broken in-tree (client.go pointed at 002-F-O-… while the folder was
002-F-C), and the submodule needed a cosmetic commit once already (010 O→C).

Make the number the only stable anchor:
- Rename all 12 folders NNN-T-S-NAME → NNN-NAME (git mv, history preserved).
- Type/status now live in a table header at the top of each SPEC.md (canon),
  aggregated by the Roadmap in SPECS/README.md (added missing 010, 012).
- Fix every ref to the old full name: README(.ru), docs/lx-changelog,
  docs/lx-config, intra-SPECS cross-links, PROBE.md git-apply path,
  TASKS.md titles, and transport/v2rayxhttp/client.go:5 (also un-stales O).
- Rewrite the convention + Workflow in SPECS/README.md and the DoD ritual in
  IMPLEMENTATION_PROMPT.md ("rename folder to …-C-…" → "set status in header
  + Roadmap").
- Bump submodule wireguard-go (0c0c10b): fix comments point at the new folder
  name SPECS/010-WG_ENDPOINT_GRO_SPLIT_BRAIN. Comment-only, no behavior change.

Not touched: gro-probe.patch (historical diagnostic diff artifact; §010 closed,
no longer applied).
This commit is contained in:
Leadaxe
2026-06-22 11:36:37 +03:00
parent 78fc9189e6
commit 09944c0103
57 changed files with 113 additions and 47 deletions
@@ -0,0 +1,72 @@
# PLAN: 007 — AWG_OVER_WIREGUARD_DETOUR_GUARD
## Архитектура решения
Guard живёт в **ленивом резолве detour** (`common/dialer/detour.go`
`DetourDialer.init()`), рядом с upstream-проверкой `empty direct outbound`. Это
даёт **вариант B** тем же механизмом, что и empty-direct: ошибка кэшируется в
`initErr` (через `sync.Once`), `DialContext`/`ListenPacket` возвращают её вместо
соединения. Ядро стартует без ошибок, прочие узлы работают, AWG-нода фейлит
каждый dial. Не fatal на старте.
### Откуда «источник AWG»
`DetourDialer` сам по себе не знает тип владельца. Прокидываем флаг:
`dialer.Options.IsAmneziaWG` → `NewDetour(..., ownerIsAmneziaWG)`.
`protocol/wireguard.NewEndpoint` выставляет его из `options.AmneziaWGOptions.IsSet()`.
### Как «цель WireGuard»
Резолвленная detour-цель приводится к `adapter.Outbound`; триггер —
`Type() == C.TypeWireGuard` (покрывает и плоский WG, и AWG — тип один). Группы
(`adapter.OutboundGroup`) раскрываются рекурсивно через `All()` +
`OutboundManager.Outbound(tag)`, с set'ом посещённых против циклов.
## Изменённые файлы
| Файл | Зона | Что |
|------|------|-----|
| `common/dialer/detour.go` | `// lx:` upstream | поле `ownerIsAmneziaWG`, guard в `init()`, рекурсивный `detourTargetIsWireGuard`, новый параметр `NewDetour` |
| `common/dialer/dialer.go` | `// lx:` upstream | поле `Options.IsAmneziaWG`, проброс в `NewDetour` |
| `protocol/wireguard/endpoint.go` | `// lx:` upstream (AWG-шов) | `IsAmneziaWG: options.AmneziaWGOptions.IsSet()` в `dialer.Options` |
| `common/dialer/awg_detour_guard_test.go` | **новый lx-файл** | unit-тест матрицы + init-пути |
## Логика guard (как реализовано)
```go
// detour.go init(), после empty-direct:
if d.ownerIsAmneziaWG && detourTargetIsWireGuard(mgr, dialer, map[string]bool{}) {
d.initErr = E.New("amneziawg endpoint cannot detour through a wireguard-based "+
"endpoint (detour: ", d.detour, "): AmneziaWG inside a WireGuard tunnel "+
"hangs the kernel on Android; use a non-wireguard detour (e.g. vless)")
return
}
func detourTargetIsWireGuard(mgr, ob, visited) bool {
if o, ok := ob.(adapter.Outbound); ok && o.Type() == C.TypeWireGuard { return true }
if g, ok := ob.(adapter.OutboundGroup); ok {
for _, tag := range g.All() {
if visited[tag] { continue }
visited[tag] = true
if m, loaded := mgr.Outbound(tag); loaded && detourTargetIsWireGuard(mgr, m, visited) { return true }
}
}
return false
}
```
## DoD (IMPLEMENTATION_PROMPT §2) — факт
- [x] `go build ./...` без тегов — ок.
- [x] `go build -tags "...,with_awg" ./cmd/sing-box` — ок.
- [x] `go test ./common/dialer/...` — зелёный (8 подтестов).
- [x] `gofmt -l` изменённых файлов — пусто.
- [x] `go vet ./common/dialer/... ./protocol/wireguard/...` — чисто.
## Зона касания upstream (для ребейза)
- `common/dialer/detour.go`, `common/dialer/dialer.go`,
`protocol/wireguard/endpoint.go` — upstream-файлы, правки только в `// lx:`
блоках. Конфликт на ребейзе — лишь если upstream перепишет `DetourDialer.init`
/ сигнатуру `NewDetour` / `dialer.Options` / конструктор endpoint.
- `awg_detour_guard_test.go` — lx-собственный, конфликтов не даёт.
- Сигнатура `NewDetour` расширена 4-м параметром — единственный внешний вызов в
`dialer.go` обновлён; других вызовов в дереве нет.