@nitra/cursor 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.9.1] - 2026-05-11
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **rego `k8s.base_kustomization` — defense-in-depth deny на HPA/PDB у `base/kustomization.yaml::resources:`:** додано пер-документне правило, що відмовляє, якщо `resources:` локально містить запис із basename `hpa.yaml`/`pdb.yaml`/`hpa.yml`/`pdb.yml` (у будь-якому підкаталозі). Канон k8s.mdc — HPA/PDB у sibling `components/` (Kustomize Component) і підключаються з overlay. Рекурсивний обхід дерева `resources:`/`components:`/`bases:` (із зануренням у вкладені kustomization.yaml) лишається у JS-оркестраторі `verifyK8sBaseKustomizeHasNoHpaPdb` (потребує fs-доступу). Rego-deny ловить найпоширеніший локальний випадок навіть якщо JS-крок упаде з винятку раніше. 5 нових rego-тестів (`hpa.yaml`/`pdb.yaml`/`hpa.yml` у `resources:`, чистий `resources:`, lookalike basename `myhpa.yaml`); `opa test` зелений (10/10).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`check-k8s.mjs`:** додано константу `GATEWAY_API_GROUP_PREFIX = 'gateway.networking.k8s.io/'`. Її відсутність кидала `ReferenceError` у `indexOneK8sYamlForHasuraCanon` (на лінії з `av.startsWith(GATEWAY_API_GROUP_PREFIX)`), яку ловив outer try/catch у `bin/n-cursor.js` і **тихо пропускав** усі наступні JS-валідатори в `check-k8s.mjs::check()` — серед них `validateKustomizeHpaPdbOnlyWithBaseDeployment`, `validateConfigMapNameMatchesDeployment`, `validateDeploymentHpaPdbAndTopology`, `validateProdKustomizationOverrides`. Наслідок у репах споживачів: правило «HPA/PDB заборонені у `k8s/base/`» не спрацьовувало (хоча `verifyK8sBaseKustomizeHasNoHpaPdb` логіку містив правильну), бо exception вилітав раніше за чергу JS-кроків. Rego-крок (`runAllK8sRego`) ішов **до** crash-точки й тому продовжував працювати — пер-документні перевірки залишалися активними, а cross-file JS — ні.
|
|
16
|
+
|
|
7
17
|
## [1.9.0] - 2026-05-11
|
|
8
18
|
|
|
9
19
|
### Changed
|
package/mdc/ci4.mdc
CHANGED
|
@@ -15,6 +15,54 @@ C4-діаграми проєкту живуть у Markdown поряд із ко
|
|
|
15
15
|
тримати знання разом із кодом — версійно, в одному PR і доступно для агентів. Якщо щось
|
|
16
16
|
важливе про систему існує лише у Confluence/Notion/месенджері — для проєкту цього **немає**.
|
|
17
17
|
|
|
18
|
+
## Специфікація як джерело істини (Spec-as-Source)
|
|
19
|
+
|
|
20
|
+
У AI-native розробці первинним артефактом, який підтримують інженери, є **специфікація**, а не
|
|
21
|
+
кодова база. Код — похідний артефакт, «будівельне риштування», яке агент генерує, верифікує або
|
|
22
|
+
повністю відновлює зі специфікації. Якщо поведінка системи має змінитися — **спочатку оновлюємо
|
|
23
|
+
специфікацію (C4/ADR/опис компонента), і лише потім** агент генерує відповідний код. Зворотний
|
|
24
|
+
порядок («код вже написаний, доку напишемо потім») перетворює специфікацію на декорацію.
|
|
25
|
+
|
|
26
|
+
## Тест на повне відтворення (Rebuild Test)
|
|
27
|
+
|
|
28
|
+
Документація вважається повною лише тоді, коли проходить бінарний тест: якщо видалити теку
|
|
29
|
+
`src/`, відкрити нову LLM-сесію з чистим контекстом і дати агенту доступ лише до `.md`-файлів —
|
|
30
|
+
агент має **відтворити робочу кодову базу**. Архітектурні збої (агент не знає структуру тек),
|
|
31
|
+
інтеграційні (неописані міжсервісні контракти), падіння юніт-тестів (неописана бізнес-логіка) —
|
|
32
|
+
це не «складність задачі», а **прогалини документації**, які треба заповнити у тому ж PR.
|
|
33
|
+
|
|
34
|
+
## Ефективність токенів і чистий Markdown
|
|
35
|
+
|
|
36
|
+
Вікно контексту LLM обмежене, а якість міркування деградує в міру його заповнення (ефект
|
|
37
|
+
«Lost in the Middle»). Тому документація **очищається від HTML-розмітки, CSS-класів,
|
|
38
|
+
навігаційних обгорток** і всього, що не несе семантичного навантаження. Чистий Markdown замість
|
|
39
|
+
HTML економить 80–90 % токенів (≈16 000 → 1 600 на сторінку), що прямо впливає на точність
|
|
40
|
+
згенерованого коду і знижує вартість API. Жодних `<div>`/`<span>`/класів у тілі `.md`/`.mdc`.
|
|
41
|
+
|
|
42
|
+
## Контекстна незалежність розділів
|
|
43
|
+
|
|
44
|
+
RAG витягує **фрагменти**, не цілі документи. Тому кожен розділ має сенс **без сусіднього
|
|
45
|
+
тексту**. Заборонено: «як було згадано вище», «ця змінна», «попередній метод», «той самий
|
|
46
|
+
сервіс». Замість цього — щоразу явно повторюємо назву сутності: «автентифікація OAuth 2.0»,
|
|
47
|
+
«функція `calculateTotal()`», «контейнер `api-gateway`». Коли агент отримає лише цей фрагмент,
|
|
48
|
+
у нього має бути повний словник для коректної генерації.
|
|
49
|
+
|
|
50
|
+
## Docs-as-Code
|
|
51
|
+
|
|
52
|
+
Документація живе у Git поруч із кодом, проходить **той самий Code Review**, версіонується і
|
|
53
|
+
автоматично перевіряється у CI (лінтери Markdown, валідатори посилань, `npx @nitra/cursor
|
|
54
|
+
check`). Биті посилання й документація, що «не компілюється», — **блокуючий баг**, не
|
|
55
|
+
косметика. Це продовження принципу «оновлення синхронно зі змінами» нижче: один PR — код +
|
|
56
|
+
схема + ADR + тести.
|
|
57
|
+
|
|
58
|
+
## Трасування як документація недетермінованої поведінки
|
|
59
|
+
|
|
60
|
+
Для компонентів, де рішення приймає нейромережа або складна евристика, класичної форми
|
|
61
|
+
«вхід X → вихід Y» **недостатньо** — поведінка недетермінована. Джерелом істини стає
|
|
62
|
+
**пайплайн логування й трасування**: які інструменти використав агент, який контекст мав,
|
|
63
|
+
чому ухвалив це рішення. У C4-компоненті такого типу — посилання на дашборд/storage трасувань
|
|
64
|
+
обов'язкове нарівні з посиланнями на тести.
|
|
65
|
+
|
|
18
66
|
## Розташування
|
|
19
67
|
|
|
20
68
|
C4-діаграми проєкту живуть у теці `docs/ci4/` — це **канонічне місце** для всіх рівнів
|
package/package.json
CHANGED
|
@@ -34,7 +34,37 @@ deny contains base_namespace_required_msg if {
|
|
|
34
34
|
trim_space(ns) == ""
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
# HPA/PDB у base заборонені — канон k8s.mdc: тримати у sibling каталозі `components/`
|
|
38
|
+
# і підключати з overlay (`components: [- ../components]`). Цей deny — швидкий gate
|
|
39
|
+
# на *локальний* `resources:` base/kustomization.yaml (точне ім'я `hpa.yaml`/`pdb.yaml`,
|
|
40
|
+
# у будь-якому підкаталозі). Рекурсивний обхід `resources:`/`components:`/`bases:`
|
|
41
|
+
# (із зануренням у вкладені kustomization.yaml) — JS-оркестратор
|
|
42
|
+
# `verifyK8sBaseKustomizeHasNoHpaPdb` у `check-k8s.mjs` (потребує fs-доступу). Цей
|
|
43
|
+
# rego-deny — defense-in-depth: спрацює навіть якщо JS-крок упаде з винятку раніше.
|
|
44
|
+
deny contains hpa_pdb_in_base_resources_msg(r) if {
|
|
45
|
+
is_kustomization
|
|
46
|
+
some r in object.get(input, "resources", [])
|
|
47
|
+
is_string(r)
|
|
48
|
+
is_hpa_or_pdb_filename(r)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
hpa_pdb_in_base_resources_msg(file) := sprintf(
|
|
52
|
+
concat("", [
|
|
53
|
+
"у base/kustomization.yaml `resources:` містить '%v' — HPA/PDB заборонені у base, ",
|
|
54
|
+
"перенесіть у sibling каталог components/ і підключайте з overlay (k8s.mdc)",
|
|
55
|
+
]),
|
|
56
|
+
[file],
|
|
57
|
+
)
|
|
58
|
+
|
|
37
59
|
is_kustomization if {
|
|
38
60
|
input.kind == "Kustomization"
|
|
39
61
|
startswith(object.get(input, "apiVersion", ""), "kustomize.config.k8s.io/")
|
|
40
62
|
}
|
|
63
|
+
|
|
64
|
+
is_hpa_or_pdb_filename(p) if {
|
|
65
|
+
basename(p) in {"hpa.yaml", "pdb.yaml", "hpa.yml", "pdb.yml"}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
basename(p) := parts[count(parts) - 1] if {
|
|
69
|
+
parts := split(p, "/")
|
|
70
|
+
}
|
|
@@ -34,3 +34,40 @@ test_allow_non_kustomization if {
|
|
|
34
34
|
"metadata": {"name": "cm"},
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
base_kust_ok := object.union(base_kust, {"namespace": "dev"})
|
|
39
|
+
|
|
40
|
+
test_deny_hpa_yaml_in_resources if {
|
|
41
|
+
count(base_kustomization.deny) > 0 with input as object.union(
|
|
42
|
+
base_kust_ok,
|
|
43
|
+
{"resources": ["deployment.yaml", "hpa.yaml"]},
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
test_deny_pdb_yaml_in_resources if {
|
|
48
|
+
count(base_kustomization.deny) > 0 with input as object.union(
|
|
49
|
+
base_kust_ok,
|
|
50
|
+
{"resources": ["pdb.yaml"]},
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test_deny_hpa_yml_in_subdir if {
|
|
55
|
+
count(base_kustomization.deny) > 0 with input as object.union(
|
|
56
|
+
base_kust_ok,
|
|
57
|
+
{"resources": ["nested/dir/hpa.yml"]},
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test_allow_resources_without_hpa_pdb if {
|
|
62
|
+
count(base_kustomization.deny) == 0 with input as object.union(
|
|
63
|
+
base_kust_ok,
|
|
64
|
+
{"resources": ["deployment.yaml", "service.yaml", "configmap.yaml"]},
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
test_allow_lookalike_basename if {
|
|
69
|
+
count(base_kustomization.deny) == 0 with input as object.union(
|
|
70
|
+
base_kust_ok,
|
|
71
|
+
{"resources": ["myhpa.yaml", "pdb-extra.yaml"]},
|
|
72
|
+
)
|
|
73
|
+
}
|
package/scripts/check-k8s.mjs
CHANGED
|
@@ -309,6 +309,8 @@ const YANNH_GROUPS = new Set([
|
|
|
309
309
|
'storagemigration.k8s.io'
|
|
310
310
|
])
|
|
311
311
|
|
|
312
|
+
const GATEWAY_API_GROUP_PREFIX = 'gateway.networking.k8s.io/'
|
|
313
|
+
|
|
312
314
|
const MODELINE_RE = /^#\s*yaml-language-server:\s*\$schema=(\S+)\s*$/
|
|
313
315
|
const PATH_SPLIT_RE = /[/\\]/u
|
|
314
316
|
const YAML_EXTENSION_RE = /\.ya?ml$/iu
|