diff --git a/rules/patterns/build-version-display/README.md b/rules/patterns/build-version-display/README.md
new file mode 100644
index 0000000..738668f
--- /dev/null
+++ b/rules/patterns/build-version-display/README.md
@@ -0,0 +1,29 @@
+# Build Version Display Pattern Notes
+
+This file keeps examples. The normative rules live in `contract.md`.
+
+## Frontend (JS/TS build tools)
+
+```ts
+// vite.config.ts / webpack.config.js
+define: {
+ __APP_VERSION__: JSON.stringify(process.env.APP_VERSION ?? "dev"),
+}
+
+// Footer component
+
+```
+
+## Go (server-rendered HTML)
+
+```go
+// main.go
+var Version = "dev"
+
+// Build: go build -ldflags "-X main.Version=1.4.2"
+```
+
+```html
+
+
+```
diff --git a/rules/patterns/build-version-display/contract.md b/rules/patterns/build-version-display/contract.md
index 44daa68..7a34f22 100644
--- a/rules/patterns/build-version-display/contract.md
+++ b/rules/patterns/build-version-display/contract.md
@@ -1,18 +1,13 @@
# Contract: Build Version Display
-Version: 1.0
-
-## Purpose
-
-Every web application must display the current build version in the page footer so that users and support staff can identify exactly which version is running.
-
----
+Version: 1.1
## Rule
-The build version **must** be visible in the footer on every page of the web application.
+The build version **must** be visible in the footer on every page of the web application,
+so users and support staff can identify exactly which version is running.
----
+See `README.md` for implementation snippets.
## Requirements
@@ -22,42 +17,6 @@ The build version **must** be visible in the footer on every page of the web app
- Format: any human-readable string that uniquely identifies the build — a semver tag, a git commit SHA, or a combination (e.g. `1.4.2`, `1.4.2-abc1234`, `abc1234`).
- The version text must be legible but visually subordinate — use a muted color and small font size so it does not compete with page content.
----
-
-## Recommended implementation
-
-**Frontend (JS/TS build tools)**
-
-Expose the version through an environment variable at build time and reference it in the footer component:
-
-```ts
-// vite.config.ts / webpack.config.js
-define: {
- __APP_VERSION__: JSON.stringify(process.env.APP_VERSION ?? "dev"),
-}
-
-// Footer component
-
-```
-
-**Go (server-rendered HTML)**
-
-Inject via `-ldflags` at build time and pass to the template:
-
-```go
-// main.go
-var Version = "dev"
-
-// Build: go build -ldflags "-X main.Version=1.4.2"
-```
-
-```html
-
-
-```
-
----
-
## What is NOT allowed
- Omitting the version from any page, including error pages.
diff --git a/rules/patterns/identifier-normalization/README.md b/rules/patterns/identifier-normalization/README.md
new file mode 100644
index 0000000..c6aefb6
--- /dev/null
+++ b/rules/patterns/identifier-normalization/README.md
@@ -0,0 +1,54 @@
+# Identifier Normalization Pattern Notes
+
+This file keeps examples. The normative rules live in `contract.md`.
+
+## Go — сравнение
+
+```go
+import "strings"
+
+func SameIdentifier(a, b string) bool {
+ return strings.EqualFold(a, b)
+}
+```
+
+## Go — дедупликация
+
+```go
+func deduplicateBySerial(items []Device) []Device {
+ seen := make(map[string]struct{})
+ result := items[:0]
+ for _, item := range items {
+ key := strings.ToLower(item.SerialNumber)
+ if _, exists := seen[key]; !exists {
+ seen[key] = struct{}{}
+ result = append(result, item)
+ }
+ }
+ return result
+}
+```
+
+## SQL — поиск и уникальность
+
+Поиск:
+
+```sql
+SELECT * FROM devices WHERE LOWER(serial_number) = LOWER(?);
+```
+
+Уникальный индекс (MySQL / MariaDB):
+
+```sql
+-- Collation ci обеспечивает case-insensitive уникальность
+ALTER TABLE devices MODIFY serial_number VARCHAR(255)
+ CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+ALTER TABLE devices ADD UNIQUE INDEX uniq_serial (serial_number);
+```
+
+SQLite:
+
+```sql
+CREATE UNIQUE INDEX uniq_serial ON devices (LOWER(serial_number));
+```
diff --git a/rules/patterns/identifier-normalization/contract.md b/rules/patterns/identifier-normalization/contract.md
index 45ae460..34d36d8 100644
--- a/rules/patterns/identifier-normalization/contract.md
+++ b/rules/patterns/identifier-normalization/contract.md
@@ -1,13 +1,13 @@
# Contract: Identifier Normalization
-Version: 1.0
+Version: 1.1
## Purpose
Правила хранения и сравнения идентификаторов оборудования:
серийные номера, вендоры, версии прошивок, партномера, артикулы.
----
+See `README.md` for Go and SQL examples.
## Правило
@@ -17,11 +17,8 @@ Version: 1.0
```
Пришло: "SN-001-ABC" → хранится: "SN-001-ABC"
Пришло: "sn-001-abc" → это тот же объект, не дубликат
-Пришло: "Sn-001-Abc" → то же самое
```
----
-
## Применяется к полям
- Серийный номер (`serial_number`, `serial`)
@@ -30,61 +27,13 @@ Version: 1.0
- Партномер (`part_number`, `part_no`)
- Артикул (`article`, `sku`)
----
-
## Реализация
-### Go — сравнение
-
-```go
-import "strings"
-
-func SameIdentifier(a, b string) bool {
- return strings.EqualFold(a, b)
-}
-```
-
-### Go — дедупликация
-
-```go
-func deduplicateBySerial(items []Device) []Device {
- seen := make(map[string]struct{})
- result := items[:0]
- for _, item := range items {
- key := strings.ToLower(item.SerialNumber)
- if _, exists := seen[key]; !exists {
- seen[key] = struct{}{}
- result = append(result, item)
- }
- }
- return result
-}
-```
-
-Ключ в map — всегда `strings.ToLower(value)`. Сам объект сохраняется с оригинальным значением.
-
-### SQL — поиск и уникальность
-
-Поиск:
-```sql
-SELECT * FROM devices WHERE LOWER(serial_number) = LOWER(?);
-```
-
-Уникальный индекс (MySQL / MariaDB):
-```sql
--- Collation ci обеспечивает case-insensitive уникальность
-ALTER TABLE devices MODIFY serial_number VARCHAR(255)
- CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-
-ALTER TABLE devices ADD UNIQUE INDEX uniq_serial (serial_number);
-```
-
-SQLite:
-```sql
-CREATE UNIQUE INDEX uniq_serial ON devices (LOWER(serial_number));
-```
-
----
+- Go: сравнение только через `strings.EqualFold`, никогда `==`.
+- Go: ключ дедупликации в map — `strings.ToLower(value)`; сам объект хранит оригинал.
+- SQL-поиск: `WHERE LOWER(col) = LOWER(?)`.
+- Уникальность: MySQL/MariaDB — case-insensitive collation (`utf8mb4_unicode_ci`) +
+ unique index; SQLite — `CREATE UNIQUE INDEX ... ON t (LOWER(col))`.
## Что не делать
diff --git a/rules/patterns/no-hardcoded-vendors/README.md b/rules/patterns/no-hardcoded-vendors/README.md
new file mode 100644
index 0000000..efa1625
--- /dev/null
+++ b/rules/patterns/no-hardcoded-vendors/README.md
@@ -0,0 +1,42 @@
+# No Hardcoded Vendors Pattern Notes
+
+This file keeps examples. The normative rules live in `contract.md`.
+
+## Запрещено
+
+```go
+if device.Vendor == "Dell" { ... }
+if strings.Contains(model, "PowerEdge") { ... }
+switch vendor {
+case "HP", "HPE", "Hewlett Packard": ...
+}
+```
+
+```go
+// Запрещено — список вендоров в коде
+var knownVendors = []string{"Dell", "HP", "Cisco", "Lenovo"}
+```
+
+## Правильно
+
+```go
+// Смотрим на возможности объекта, не на имя вендора
+if device.HasIPMI { ... }
+if device.ParserType == "redfish" { ... }
+```
+
+Маппинг в конфиге:
+
+```yaml
+# config.yaml
+vendor_parsers:
+ dell: redfish
+ hp: ilo
+ cisco: ucs
+```
+
+Маппинг в БД:
+
+```sql
+SELECT parser_type FROM vendor_registry WHERE LOWER(vendor) = LOWER(?);
+```
diff --git a/rules/patterns/no-hardcoded-vendors/contract.md b/rules/patterns/no-hardcoded-vendors/contract.md
index 06f0d35..0186cee 100644
--- a/rules/patterns/no-hardcoded-vendors/contract.md
+++ b/rules/patterns/no-hardcoded-vendors/contract.md
@@ -1,64 +1,23 @@
# Contract: No Hardcoded Vendors or Models
-Version: 1.0
+Version: 1.1
## Purpose
Запрет на хардкод названий вендоров, моделей и партномеров в коде.
----
+See `README.md` for code examples.
## Правило
Названия вендоров, моделей, серий оборудования и партномеров **не появляются в коде**.
Они приходят из данных: БД, конфига, входного документа, справочника.
----
-
-## Что запрещено
-
-```go
-// Запрещено
-if device.Vendor == "Dell" { ... }
-if strings.Contains(model, "PowerEdge") { ... }
-switch vendor {
-case "HP", "HPE", "Hewlett Packard": ...
-}
-```
-
-```go
-// Запрещено — список вендоров в коде
-var knownVendors = []string{"Dell", "HP", "Cisco", "Lenovo"}
-```
-
----
-
-## Что делать вместо
-
-Логика определяется по полям из данных, не по названию вендора:
-
-```go
-// Правильно — смотрим на возможности объекта, не на имя вендора
-if device.HasIPMI { ... }
-if device.ParserType == "redfish" { ... }
-```
-
-Если нужен маппинг — он живёт в конфиге или справочной таблице БД, не в коде:
-
-```yaml
-# config.yaml
-vendor_parsers:
- dell: redfish
- hp: ilo
- cisco: ucs
-```
-
-```sql
--- справочник в БД
-SELECT parser_type FROM vendor_registry WHERE LOWER(vendor) = LOWER(?);
-```
-
----
+- Запрещены сравнения и switch по имени вендора (`if device.Vendor == "Dell"`) и
+ списки вендоров в коде (`var knownVendors = []string{...}`).
+- Логика определяется по полям из данных (`device.HasIPMI`, `device.ParserType`),
+ не по названию вендора.
+- Если нужен маппинг вендор → поведение, он живёт в конфиге или справочной таблице БД.
## Исключения
@@ -70,8 +29,6 @@ SELECT parser_type FROM vendor_registry WHERE LOWER(vendor) = LOWER(?);
В этих местах название вендора — идентификатор модуля, не условие в логике.
----
-
## Почему
Хардкод вендора делает код хрупким: новый вендор требует правок в коде, а не в данных.
diff --git a/rules/patterns/vendor-installer-verification/README.md b/rules/patterns/vendor-installer-verification/README.md
new file mode 100644
index 0000000..a1cc18f
--- /dev/null
+++ b/rules/patterns/vendor-installer-verification/README.md
@@ -0,0 +1,46 @@
+# Vendor Installer Verification Pattern Notes
+
+This file keeps examples. The normative rules live in `contract.md`.
+
+## Download Order
+
+```sh
+BASE_URL="https://vendor.example.com/downloads/${VERSION}"
+BIN_FILE="/var/cache/vendor-${VERSION}.run"
+SHA_FILE="/var/cache/vendor-${VERSION}.run.sha256sum"
+
+# 1. Download checksum first
+wget -q -O "$SHA_FILE" "${BASE_URL}/vendor-${VERSION}.run.sha256sum"
+
+# 2. Download installer
+wget --show-progress -O "$BIN_FILE" "${BASE_URL}/vendor-${VERSION}.run"
+
+# 3. Verify
+cd /var/cache
+sha256sum -c "$SHA_FILE" || { echo "ERROR: sha256 mismatch"; rm -f "$BIN_FILE"; exit 1; }
+```
+
+## Cache with Verification
+
+```sh
+verify_cached() {
+ [ -s "$SHA_FILE" ] || return 1 # sha256 file missing or empty
+ [ -s "$BIN_FILE" ] || return 1 # binary missing or empty
+ cd "$(dirname "$BIN_FILE")"
+ sha256sum -c "$SHA_FILE" --status 2>/dev/null
+}
+
+if ! verify_cached; then
+ rm -f "$BIN_FILE" "$SHA_FILE"
+ # ... download and verify
+else
+ echo "verified from cache"
+fi
+```
+
+## Version Validation
+
+```sh
+curl -sIL "https://vendor.example.com/downloads/${VERSION}/installer.run" \
+ | grep -i 'http/\|content-length'
+```
diff --git a/rules/patterns/vendor-installer-verification/contract.md b/rules/patterns/vendor-installer-verification/contract.md
index 3ca3def..d352831 100644
--- a/rules/patterns/vendor-installer-verification/contract.md
+++ b/rules/patterns/vendor-installer-verification/contract.md
@@ -1,6 +1,6 @@
# Contract: Vendor Installer Verification
-Version: 1.0
+Version: 1.1
## Purpose
@@ -8,75 +8,19 @@ Rules for downloading and verifying proprietary vendor installers (`.run`, `.exe
where the vendor publishes a checksum alongside the binary.
Applies to: NVIDIA drivers, vendor CLI tools, firmware packages.
----
-
-## Download Order
-
-Always download the checksum file **before** the installer:
-
-```sh
-BASE_URL="https://vendor.example.com/downloads/${VERSION}"
-BIN_FILE="/var/cache/vendor-${VERSION}.run"
-SHA_FILE="/var/cache/vendor-${VERSION}.run.sha256sum"
-
-# 1. Download checksum first
-wget -q -O "$SHA_FILE" "${BASE_URL}/vendor-${VERSION}.run.sha256sum"
-
-# 2. Download installer
-wget --show-progress -O "$BIN_FILE" "${BASE_URL}/vendor-${VERSION}.run"
-
-# 3. Verify
-cd /var/cache
-sha256sum -c "$SHA_FILE" || { echo "ERROR: sha256 mismatch"; rm -f "$BIN_FILE"; exit 1; }
-```
-
-Reason: if the download is interrupted, you have the expected checksum to verify against on retry.
-
----
-
-## Cache with Verification
-
-Never assume a cached file is valid — a previous download may have been interrupted (0-byte file):
-
-```sh
-verify_cached() {
- [ -s "$SHA_FILE" ] || return 1 # sha256 file missing or empty
- [ -s "$BIN_FILE" ] || return 1 # binary missing or empty
- cd "$(dirname "$BIN_FILE")"
- sha256sum -c "$SHA_FILE" --status 2>/dev/null
-}
-
-if ! verify_cached; then
- rm -f "$BIN_FILE" "$SHA_FILE"
- # ... download and verify
-else
- echo "verified from cache"
-fi
-```
-
-**Never check only for file existence.** Check that the file is non-empty (`-s`) AND passes checksum.
-
----
-
-## Version Validation
-
-Before writing build scripts, verify the version URL actually exists:
-
-```sh
-curl -sIL "https://vendor.example.com/downloads/${VERSION}/installer.run" \
- | grep -i 'http/\|content-length'
-```
-
-A `404` or `content-length: 0` means the version does not exist on that CDN.
-Vendor version numbering may have gaps (e.g. NVIDIA skips minor versions on some CDNs).
-
----
+See `README.md` for shell snippets.
## Rules
-- Download checksum before installer — never after.
+- Download the checksum file **before** the installer — never after. If the download is
+ interrupted, you still have the expected checksum to verify against on retry.
- Verify checksum before extracting or executing.
- On mismatch: delete the file, exit with error. Never proceed with a bad installer.
+- Never assume a cached file is valid — a previous download may have been interrupted.
+ **Never check only for file existence**: the file must be non-empty (`-s`) AND pass checksum.
- Cache by `version` + any secondary key (e.g. kernel version for compiled modules).
+- Before writing build scripts, verify the version URL actually exists (`curl -sIL`).
+ A `404` or `content-length: 0` means the version is absent on that CDN; vendor version
+ numbering may have gaps.
- Never commit installer files to git — always download at build time.
- Log the expected hash when downloading so failures are diagnosable.
diff --git a/scripts/lint.sh b/scripts/lint.sh
index c72e6f4..3f13466 100755
--- a/scripts/lint.sh
+++ b/scripts/lint.sh
@@ -22,9 +22,9 @@ for ref in $(grep -o 'bible/rules/patterns/[a-z-]*/contract\.md' AGENT-BOOTSTRAP
fi
done
-# 3. No machine-local absolute paths in committed markdown.
-if grep -rn '/Users/' --include='*.md' . --exclude-dir=.git; then
- echo "FAIL: machine-local absolute paths found (see above)"
+# 3. No machine-local absolute paths or stale path prefixes in committed markdown.
+if grep -rn '/Users/\|kit/patterns' --include='*.md' . --exclude-dir=.git; then
+ echo "FAIL: machine-local absolute paths or stale kit/patterns references found (see above)"
fail=1
fi