docs(history): раскладка HISTORY по тематическим папкам (75 файлов)

Было: 41 файл в корне HISTORY/ + авторские папки OPUS/ и SONNET/ (34 файла).
Стало — тематическая нумерация в стиле NOTES/ (10_, 20_, …):

  10_reviews/    ревью кода и разборы от LLM (2)
  20_releases/   заливки версий в реестр, чистки реестра, нумерация версий (8)
  30_provider/   ядро провайдера: архитектура, модификаторы, UUID, nested (6)
  40_generator/  генератор YAML/спеки, формат MAN (3)
  50_docs/       пайплайн документации, навигация, публикация, хостинг S3 (9)
  60_stands/     стенды и примеры: CRUD, FullPipe, Штурвал, TEST_STAND (7)
  70_infra/      реестр, API Gateway, DDoS-Guard, VPN/213, зеркала (4)
  90_llm/        диалоги и промпты с LLM вне тематики: OPUS/, SONNET/, gemini/ (34)

OPUS/ и SONNET/ перенесены как есть в 90_llm/ — чтобы не рвать пары
«бриф → ответ» внутри диалогов. Все переносы — через git mv (история сохранена).
Перед правкой: TMP/backup_2026-10-02/HISTORY_before_restructure.tar.gz.
Перекрёстные ссылки обновляются следующим коммитом.
This commit is contained in:
Repinoid
2026-10-02 07:35:32 +03:00
parent 9cc7b3f260
commit 2c196e8cc8
75 changed files with 0 additions and 0 deletions
@@ -0,0 +1,66 @@
# MAN Format Fix — 2026-08-10
**Проблема:** `service_man` из YAML содержит Markdown (`#`, `##`, `---`, `**`) + HTML (`<br/>`, `&quot;`), но рендерился как сырой текст. Теги `##`, `**`, `---` выводились буквально, не форматируя текст.
**Корень проблемы:** `md_in_html` расширение mkdocs не обрабатывает Markdown внутри `<div markdown="1">` и `<details>` — всё содержимое выводится как plain text.
**Решение:** использовать **нативный mkdocs admonition** `??? note` вместо HTML-тегов.
### Было (сломано)
```go
b.WriteString("<details class=\"man-content\">\n<summary>Справка (MAN)</summary>\n\n")
b.WriteString(htmlToMarkdown(man))
b.WriteString("\n\n</details>\n")
```
```html
<!-- Рендерилось как: -->
# Инструкция --- ## 1. Общая информация **текст**
<!-- Все теги видны буквально -->
```
### Стало (работает)
```go
b.WriteString("??? note \"Справка (MAN)\"\n\n")
md := htmlToMarkdown(man)
for _, line := range strings.Split(md, "\n") {
b.WriteString(" " + line + "\n")
}
b.WriteString("\n")
```
```markdown
??? note "Справка (MAN)"
# Инструкция по развертыванию
---
## 1. Общая информация
**текст**
```
```html
<!-- Рендерится как: -->
<details class="note">
<summary>Справка (MAN)</summary>
<h1>Инструкция по развертыванию</h1>
<hr />
<h2>1. Общая информация</h2>
<p><strong>текст</strong></p>
</details>
```
### Ключевые требования `???` admonition
1. **Пустая строка** после `??? note "Заголовок"` — ОБЯЗАТЕЛЬНА
2. **Все строки контента** с отступом ровно 4 пробела — включая пустые строки
3. `pymdownx.details` должен быть в `markdown_extensions` (уже есть)
### Затронутые файлы
| Файл | Изменение |
|------|-----------|
| `writers/writers.go:buildManualPage()` | `??? note` вместо `<details>` |
| `extra.css` | Убран `.man-content` CSS (больше не нужен) |
@@ -0,0 +1,85 @@
# Баг Dev-генератора: рассинхрон nested-параметра
**Дата:** 2026-09-03
**Статус:** план решения, изменения не выполнены
## Симптом
Сборка Dev-провайдера падает на сгенерированном `95_nodejs_resource.go`:
```text
plan.JsonEnv.IsNull undefined
plan.JsonEnv.IsUnknown undefined
plan.JsonEnv.ValueString undefined
```
## Причина
В Dev API один и тот же параметр `jsonEnv` описан по-разному:
- в `create` — `map` с `sub_params` (`DB_PASS`), то есть nested-параметр;
- в `modify` — `map` без `sub_params`, то есть параметр выглядит плоским.
Генератор объединяет параметры через `params.Merge`. Поэтому в канонической
`SchemaParams` `jsonEnv` становится nested и модель содержит
`*NodejsJsonEnvModel`.
Однако `params.AlignParamTypes` переносит вложенные параметры только когда у
параметра операции уже установлен `HasSubParams`. У `modify.jsonEnv` этот флаг
ложный, поэтому `ModifyParams` сохраняет scalar-представление.
Шаблон `Update` видит `modify.jsonEnv` как scalar и генерирует вызовы
`IsNull()`, `IsUnknown()` и `ValueString()`. В сгенерированной модели это
указатель на nested-структуру, поэтому Go-код не компилируется.
## Универсальное решение
Генератор не должен содержать условий для Dev, Test, Prod или конкретного
сервиса. Нужна единая нормализация всех operation params относительно общей
канонической схемы:
```text
schemaParams = Merge(createParams, modifyParams, deleteParams)
createParams = NormalizeAgainstSchema(createParams, schemaParams)
modifyParams = NormalizeAgainstSchema(modifyParams, schemaParams)
deleteParams = NormalizeAgainstSchema(deleteParams, schemaParams)
```
Нормализация должна рекурсивно переносить из канонической схемы структурные
свойства:
- `Type`;
- `HasSubParams`;
- `SubParams` и их типы.
Собственные свойства конкретной операции должны сохраняться: `ID`,
`Required`, `Default`, описания и остальные operation-specific поля.
После нормализации `SchemaParams.jsonEnv` и `ModifyParams.jsonEnv` будут иметь
одинаковую nested-структуру, а шаблон сгенерирует nested-обработку вместо
scalar-методов.
## Граница ответственности
Расхождение Dev API остаётся дефектом входной схемы, но не должно ломать
универсальный генератор. Исправление только YAML Dev или специальная проверка
`jsonEnv` были бы стендовыми обходами и не решают общий класс проблем.
## Обязательная проверка
Добавить генераторный тест на общий случай:
```text
create: map-fixed/map с sub_params
modify: тот же code без sub_params
ожидание: modify после нормализации — nested
```
Проверка результата: сгенерированный Go-код должен компилироваться, а nested
параметр не должен получать scalar-вызовы в `Update`.
## Текущий статус стендов
- Test `3.0.0` опубликован.
- Prod `1.0.0` опубликован.
- Dev `2.0.0` не опубликован: сборка остановилась на компиляции generated Go.
@@ -0,0 +1,258 @@
# 2026-09-30 — Ужесточение пайплайна генерации YAML (безопасная замена, чистка легаси)
> Связанные материалы:
> `TOOLS/README.md` (канонический пайплайн, порядок шагов),
> `TOOLS/ARCHITECTURE.md` (спецификация),
> память репозитория: `pipeline-legacy.md`.
## Задача
1. Разобрать, что в генерации YAML устарело.
2. Перегенерировать YAML по всем стендам так, чтобы **старое удалялось безопасно**,
а новое создавалось атомарно.
3. Задокументировать всё, чтобы история изменений прослеживалась.
Команда пользователя: «делай как ПОЛОЖЕНО, как в best practices».
## Что было не так (до правок)
### `TOOLS/scripts/01_generate_yamls.sh`
| Место (до) | Проблема |
|---|---|
| стр. 230 `rm -f "$output_glob"` | старый YAML удалялся **до** генерации → при сбое API файл исчезал, новый не создавался (неатомарно per-service) |
| стр. 122 «Полной очистки нет» | YAML исключённого сервиса оставался в каталоге и попадал в сборку |
| стр. 91 `ls -t "${ROOT_DIR}"/*.token` | легаси-фолбэк токена: в корне токенов нет; поиск «последнего» мог подхватить чужой токен |
| стр. 109 `API_ENDPOINT="${NUBES_API_ENDPOINT:-https://lk-api-gateway.ngcloud.ru/...}"` | молчаливый уход в **PROD**, если в профиле нет endpoint |
| стр. 167 `svc_name=""` | имя всегда пустое, хотя шапка обещала парсинг из списка → лишний HTTP-запрос на каждый сервис (2 запроса вместо 1) |
| стр. 46–50 | дубль `SERVICES_FILE_DEFAULT` (одинаковое присваивание в `if`) |
| шапка vs код | «REQUEST_DELAY по умолчанию 0.2», в коде 0.5; путь вывода указан как `provider/resources_yaml` (устарел) |
### `TOOLS/yaml-generator/internal/config/config.go`
| Место (до) | Проблема |
|---|---|
| `Load()` стр. 33 | свой дефолт endpoint = **PROD** gateway |
| `loadToken()` + `findLatestToken()` | легаси-фолбэк «последний `*.token` в корне репо» |
| `Load()` стр. 60 | путь `filepath.Join(repoRoot, "devops", "config", "services_list.txt")`, причём `FindRepoRoot()` возвращает каталог `provider/` → путь заведомо не существовал |
### Легаси-скрипты
`10_yaml_stability_run.sh`, `11_yaml_stability_run_latest.sh`,
`12_generate_yamls_latest.sh`, `13_generate_yamls_clean.sh`,
`02_generate_resources_and_docs_template_v2.sh` — **мертвы**: зовут `01` без
`--profile` (→ `exit 2`), ищут `*.token` в корне репо, а `13` вдобавок делал
`rm -f provider/resources_yaml/*.yaml`. Ни один рабочий скрипт их не вызывает
(ссылки есть только в исторических `HISTORY/`, `NOTES/`).
## Что сделано
### 1. `01_generate_yamls.sh` — безопасная запись по принципу staging → атомарная замена
Новый алгоритм:
```
staging = generated/<stand>/resources_yaml.staging.<pid>
↓ генерация всех сервисов пачкой per-service в staging
↓ при пустом failures:
mv resources_yaml → resources_yaml.bak-<UTC> (бэкап, ротация KEEP_BACKUPS=5)
mv staging → resources_yaml (атомарный rename в том же FS)
↓ при непустом failures:
замена ОТМЕНЯЕТСЯ, рабочий каталог не тронут, staging оставлен для разбора, exit 1
```
Прочие изменения:
- каталог помечается маркером `.stand`; генерация в каталог чужого стенда запрещена (`exit 2`);
- `embed.go` создаётся теперь в staging (обязательный `go:embed *.yaml`);
- `NUBES_API_ENDPOINT` обязателен, иначе `exit 2`;
- токен берётся только из `NUBES_API_TOKEN`/`TOKEN_FILE`; легаси-поиск удалён;
- имя сервиса — из 2-го поля `services_list.txt`; лишний python-запрос удалён;
- удалён дубль `SERVICES_FILE_DEFAULT`; синхронизированы комментарии.
### 2. `yaml-generator/internal/config/config.go`
- `NUBES_API_ENDPOINT` обязателен (нет PROD-дефолта);
- `loadToken()` больше не ищет `*.token` в корне репо; `findLatestToken` и `getenvDefault` удалены как мёртвые;
- при отсутствии `NUBES_SERVICE_ID` требуется явный `NUBES_SERVICES_FILE` (угадывание пути удалено).
### 3. Легаси-скрипты отключены (fail-fast)
В начало каждого добавлен guard: сообщение `DEPRECATED` + `exit 2`. Файлы **не удалены**
(удаление — отдельное решение владельца), но теперь они не могут сделать ничего вредного.
### 4. Документация
- `TOOLS/README.md` — добавлен раздел «Канонический пайплайн (порядок шагов)» и описание безопасной генерации;
- `TOOLS/ARCHITECTURE.md` — ссылки `devops/…` заменены на `TOOLS/config/<stand>/…`;
- память репозитория — `pipeline-legacy.md` уточнена.
## Прогон по всем стендам (результат)
Токены проверены прямыми запросами к API (с браузерным `User-Agent`, иначе DDoS-Guard отдаёт 403):
| Стенд | Endpoint | HTTP | YAML после генерации | Stale | Failures |
|---|---|---|---|---|---|
| dev | `lk-api-gateway-dev.ngcloud.ru` | 200 | 40 | нет | нет |
| test | `lk-api-gateway-test.ngcloud.ru` | 200 | 36 | нет | нет |
| prod | `lk-api-gateway.ngcloud.ru` | 200 | 35 | нет | нет |
Команды:
```bash
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/dev
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/test
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/prod
```
Проверка целостности: `diff -rq` нового каталога dev с бэкапом даёт различия только
в случайных `default`-суффиксах, которые API генерирует при каждом запросе
(`db-ievgpdvu` → `db-ujama5rb`, `flask-seqtiq3t` → `flask-xwfdxdqh` и т.п.) —
структурной регрессии нет. Это же объясняет, почему побайтовое сравнение двух
прогонов не может быть использовано как «детектор дрейфа».
## Коммиты
- `12b3932` — `fix(tools): безопасная генерация YAML — staging + атомарная замена, без легаси-фолбэков`
- `ad4daab` — `chore(tools): легаси-скрипты генерации отключены (fail-fast DEPRECATED)`
## Проверки
- `bash -n` для `01_generate_yamls.sh` и всех guard-скриптов — OK;
- `go vet ./...` + `go build` для `yaml-generator` — OK;
- guard отдаёт `exit 2`;
- прогон dev/test/prod — 0 failures, stale отсутствует, staging не остаётся;
- бэкапы создаются: `generated/dev/resources_yaml.bak-20260930T063152Z` и т.д.
## Этап 2 — удаление мёртвого (по команде «удаляй всё старое, аккуратно»)
**Удалено (`1e796c8`)** — 100% мёртвый код/данные, ничего их не вызывает:
| Файл | Почему удалён |
|---|---|
| `TOOLS/scripts/10_yaml_stability_run.sh` | зовёт `01` без `--profile` (exit 2), ищет `*.token` в корне |
| `TOOLS/scripts/11_yaml_stability_run_latest.sh` | цепочка на `10`, та же поломка |
| `TOOLS/scripts/12_generate_yamls_latest.sh` | зовёт `01` без `--profile`, ищет `*.token` в корне |
| `TOOLS/scripts/13_generate_yamls_clean.sh` | цепочка на `12` + делал `rm -f provider/resources_yaml/*.yaml` |
| `TOOLS/scripts/02_generate_resources_and_docs_template_v2.sh` | легаси-дубль канонического `02_generate_resources_and_docs_v2.sh` |
| `TOOLS/config/services_list.txt` (общий) | код его не читает; как «объединение» устарел: активный `27` (в test/prod — «нет в UI»), нет `87/88/97/153`, которые есть в dev |
Проверка «ничего не вызывает»: `grep` по всему репо находил ссылки только в
исторических `HISTORY/`, `NOTES/`, `docs/` (не исполняются).
**Правки ссылок (`c822ae2`)**: `README.md`, `HOW_TO/README.md`,
`HOW_TO/DEVOPS_BUILD_PIPELINE.md`, `HOW_TO/HOWTO_ADD_NEW_SERVICE.md` (включая
переписанный блок «Быстрый старт» с `devops/` на `./TOOLS/scripts/*`),
`DOCS_PIPELINE/README.md`, `scripts/publish-doc-page.sh`, `.gitignore`.
**Проверка после удаления**: `bash -n` для всех `TOOLS/scripts/*.sh` и
`scripts/publish-doc-page.sh` — OK; smoke-прогон `01 --profile TOOLS/config/dev` —
40 YAML, замена атомарная, бэкап создан.
**Не удалено (осознанно):**
- поддержка легаси-прокси `index.cfm` в `01` и `yaml-generator` — это совместимость
с работающими пользователями старого API (провайдер v5.0.75, `secrets/stands.md`);
- `DOCS_PIPELINE/publish-docs.sh` — сам файл помечен «справочная копия, не подменяет пайплайн»;
- `HISTORY/`, `NOTES/`, `docs/` — исторические документы (в них `devops/` и легаси-скрипты
упоминаются как история, это нормально);
- `scripts/*.py` и `s3_notification_example.sh` — ручные утилиты, вызываются вручную.
## Этап 3 — сверка списков стендов с облачным каталогом (источник истины)
Принято: **истина — то, что перечислено в облаке**. Определяется эндпоинтом каталога:
```bash
# «перечислено в облаке» (продакшен-готовые сервисы стенда)
GET {NUBES_API_ENDPOINT}/services?limit=200&isProductionReady=true
# для сравнения: без фильтра отдаются ВСЕ сервисы платформы, включая
# DEPRECATED и не заявленные в каталоге (48 у prod, 49 у test, 60 у dev)
```
Требуется браузерный `User-Agent` (иначе DDoS-Guard отдаёт 403) и `Referer`.
Результат на 2026-09-30:
| Стенд | Облако (`isProductionReady=true`) | Активных в `services_list.txt` | Лишние в файле | Не хватало |
|---|---|---|---|---|
| dev | 40 | 40 | нет | нет |
| test | 36 | 36 | нет | нет |
| prod | 36 | 35 → **36** | нет | **`151 k8sOpenbao` (Vault)** |
У остальных 12 закомментированных prod-сервисов, присутствующих в API, `isProductionReady=false` —
они закомментированы обоснованно. Четыре id в файле отсутствуют в каталоге prod вовсе
(`32 vmpostgre`, `87 k8svalkey`, `153 nifi`, `175 k8sGo`).
Исправлено коммитом `a68a36a`: `151 k8sOpenbao` раскомментирован (комментарий «нет в PROD UI»
устарел), prod перегенерирован — 36 YAML, ровно как в облаке.
## Этап 4 — перепроверка после полной перегенерации + подводные камни
Команда: «сгенери YAML для всех стендов, проследи чтобы старого ничего не осталось,
перепроверь после генерации всё». Выполнено три прогона `01`:
```bash
for s in dev test prod; do ./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/$s; done
# все три: exit=0
```
### Результат перепроверки (2026-09-30)
| Стенд | YAML | = активных в списке | = облако (`isProductionReady=true`) | Stale | Дубли id | Failures |
|---|---|---|---|---|---|---|
| dev | 40 | ✅ 40 | ✅ 40 | нет | нет | пусто |
| test | 36 | ✅ 36 | ✅ 36 | нет | нет | пусто |
| prod | 36 | ✅ 36 | ✅ 36 | нет | нет | пусто |
Дополнительно проверено:
- staging-каталоги (`resources_yaml.staging.*`) — не осталось ни одного;
- в `resources_yaml/` только `*.yaml`, `.stand`, `embed.go` — посторонних файлов нет;
- `.stand` в каждом каталоге совпадает с профилем (`dev`/`test`/`prod`);
- бэкапы прошлых версий: dev 3, test 2, prod 2 (ротация `KEEP_BACKUPS=5`);
- `generated/<стенд>/tmp/yaml_gen_failures.txt` — пусты;
- `git status` — чисто.
### ⛔ Подводные камни, найденные при перепроверке (важно на будущее)
1. **API отдаёт случайные `default`.** Часть параметров приходит со случайным
суффиксом (`db-ievgpdvu` → `db-ujama5rb`, `kvname-grzjes7l` → `kvname-g3s0uof2`,
`flask-seqtiq3t` → `flask-xwfdxdqh`). Поэтому **побайтовое сравнение двух прогонов
не является детектором дрейфа** — различия в этих строках не регрессия.
2. **Случайный `default` вшивается в сгенерированный Go-код.**
Пример: `generated/dev/go/151_k8s_openbao_kv_resource.go` содержит
`Default: stringdefault.StaticString("kvname-XXXX")`. Следствие:
`check_generated_drift.sh dev` показывает **дрейф 15 файлов сразу после любой**
перегенерации YAML — это не ошибка оператора.
3. **Производные артефакты стареют молча.** `generated/<стенд>/go` и `generated/<стенд>/docs`
создаются шагом `02` и после нового `01` становятся старше своих источников
(на момент проверки: `go`/`docs` dev — 08:46, YAML dev — 10:18). Отдельно живёт
эфемерная копия `provider/internal/resources_gen` + `provider/resources_yaml`
(её кладёт `dev-materialize.sh`, маркер `.stand` = стенд). Их нужно обновлять
шагом `02` после каждого `01`.
4. **Прямые HTTP-запросы к API без браузерного `User-Agent` получают 403**
(DDoS-Guard). С `User-Agent` + `Referer` — 200.
### Актуальная карта пайплайна на 2026-09-30
- Единственный путь генерации YAML: `TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/<стенд>`
(`--profile` обязателен, без него `exit 2`).
- Один универсальный движок на все стенды: `TOOLS/bin/yaml-generator`, стенд задаётся
переменными окружения (`NUBES_API_ENDPOINT`, `NUBES_API_TOKEN`, `NUBES_SERVICE_ID`,
`NUBES_SERVICE_NAME`, `NUBES_OUTPUT_DIR`); список сервисов — свой у каждого стенда.
- Стенд-специфичных хардкодов в коде нет — контролируется `check_hardcoded_service_ids.sh`.
- Токены: `secrets/{dev,test,prod}.token` (валидны на 2026-09-30, срок до 2026-12-27);
обновление — `TOOLS/scripts/00_token_manager.sh` (keycloak refresh, `THRESHOLD_MIN=10`).
- Удалены как мёртвые (`1e796c8`): `10/11/12/13_yaml_*.sh`,
`02_generate_resources_and_docs_template_v2.sh`, общий `TOOLS/config/services_list.txt`.
- Оставлены осознанно: поддержка легаси-прокси `index.cfm`, справочная копия
`DOCS_PIPELINE/publish-docs.sh`, исторические `HISTORY/`/`NOTES/`/`docs/`,
ручные утилиты `scripts/*.py`.
## Открытые вопросы (на решение владельца)
1. Поддержка легаси-прокси `index.cfm`: оставляем или выпиливаем (README уже помечает
закрытые API как «не использовать»)?
2. Прочие `.gitignore`-паттерны мёртвых каталогов (`universal_rebuild/*`, `provider/generated/`)
— чистить?