Move inline code examples out of normative contracts

identifier-normalization, no-hardcoded-vendors,
vendor-installer-verification, and build-version-display follow the
go-database split: rules in contract.md, snippets in README.md. Routed
contract reads get cheaper; examples stay available on demand. Lint now
also rejects stale kit/patterns references.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 10:00:02 +03:00
parent 421d004faf
commit a44133aff2
9 changed files with 201 additions and 221 deletions

View File

@@ -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
<footer>v{__APP_VERSION__}</footer>
```
## Go (server-rendered HTML)
```go
// main.go
var Version = "dev"
// Build: go build -ldflags "-X main.Version=1.4.2"
```
```html
<!-- base template -->
<footer>v{{ .Version }}</footer>
```

View File

@@ -1,18 +1,13 @@
# Contract: Build Version Display # Contract: Build Version Display
Version: 1.0 Version: 1.1
## 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.
---
## Rule ## 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 ## 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`). - 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. - 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
<footer>v{__APP_VERSION__}</footer>
```
**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
<!-- base template -->
<footer>v{{ .Version }}</footer>
```
---
## What is NOT allowed ## What is NOT allowed
- Omitting the version from any page, including error pages. - Omitting the version from any page, including error pages.

View File

@@ -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));
```

View File

@@ -1,13 +1,13 @@
# Contract: Identifier Normalization # Contract: Identifier Normalization
Version: 1.0 Version: 1.1
## Purpose ## 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"
Пришло: "sn-001-abc" → это тот же объект, не дубликат Пришло: "sn-001-abc" → это тот же объект, не дубликат
Пришло: "Sn-001-Abc" → то же самое
``` ```
---
## Применяется к полям ## Применяется к полям
- Серийный номер (`serial_number`, `serial`) - Серийный номер (`serial_number`, `serial`)
@@ -30,61 +27,13 @@ Version: 1.0
- Партномер (`part_number`, `part_no`) - Партномер (`part_number`, `part_no`)
- Артикул (`article`, `sku`) - Артикул (`article`, `sku`)
---
## Реализация ## Реализация
### Go сравнение - Go: сравнение только через `strings.EqualFold`, никогда `==`.
- Go: ключ дедупликации в map — `strings.ToLower(value)`; сам объект хранит оригинал.
```go - SQL-поиск: `WHERE LOWER(col) = LOWER(?)`.
import "strings" - Уникальность: MySQL/MariaDB — case-insensitive collation (`utf8mb4_unicode_ci`) +
unique index; SQLite — `CREATE UNIQUE INDEX ... ON t (LOWER(col))`.
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));
```
---
## Что не делать ## Что не делать

View File

@@ -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(?);
```

View File

@@ -1,64 +1,23 @@
# Contract: No Hardcoded Vendors or Models # Contract: No Hardcoded Vendors or Models
Version: 1.0 Version: 1.1
## Purpose ## Purpose
Запрет на хардкод названий вендоров, моделей и партномеров в коде. Запрет на хардкод названий вендоров, моделей и партномеров в коде.
--- See `README.md` for code examples.
## Правило ## Правило
Названия вендоров, моделей, серий оборудования и партномеров **не появляются в коде**. Названия вендоров, моделей, серий оборудования и партномеров **не появляются в коде**.
Они приходят из данных: БД, конфига, входного документа, справочника. Они приходят из данных: БД, конфига, входного документа, справочника.
--- - Запрещены сравнения и switch по имени вендора (`if device.Vendor == "Dell"`) и
списки вендоров в коде (`var knownVendors = []string{...}`).
## Что запрещено - Логика определяется по полям из данных (`device.HasIPMI`, `device.ParserType`),
не по названию вендора.
```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(?);
```
---
## Исключения ## Исключения
@@ -70,8 +29,6 @@ SELECT parser_type FROM vendor_registry WHERE LOWER(vendor) = LOWER(?);
В этих местах название вендора — идентификатор модуля, не условие в логике. В этих местах название вендора — идентификатор модуля, не условие в логике.
---
## Почему ## Почему
Хардкод вендора делает код хрупким: новый вендор требует правок в коде, а не в данных. Хардкод вендора делает код хрупким: новый вендор требует правок в коде, а не в данных.

View File

@@ -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'
```

View File

@@ -1,6 +1,6 @@
# Contract: Vendor Installer Verification # Contract: Vendor Installer Verification
Version: 1.0 Version: 1.1
## Purpose ## Purpose
@@ -8,75 +8,19 @@ Rules for downloading and verifying proprietary vendor installers (`.run`, `.exe
where the vendor publishes a checksum alongside the binary. where the vendor publishes a checksum alongside the binary.
Applies to: NVIDIA drivers, vendor CLI tools, firmware packages. Applies to: NVIDIA drivers, vendor CLI tools, firmware packages.
--- See `README.md` for shell snippets.
## 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).
---
## Rules ## 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. - Verify checksum before extracting or executing.
- On mismatch: delete the file, exit with error. Never proceed with a bad installer. - 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). - 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. - Never commit installer files to git — always download at build time.
- Log the expected hash when downloading so failures are diagnosable. - Log the expected hash when downloading so failures are diagnosable.

View File

@@ -22,9 +22,9 @@ for ref in $(grep -o 'bible/rules/patterns/[a-z-]*/contract\.md' AGENT-BOOTSTRAP
fi fi
done done
# 3. No machine-local absolute paths in committed markdown. # 3. No machine-local absolute paths or stale path prefixes in committed markdown.
if grep -rn '/Users/' --include='*.md' . --exclude-dir=.git; then if grep -rn '/Users/\|kit/patterns' --include='*.md' . --exclude-dir=.git; then
echo "FAIL: machine-local absolute paths found (see above)" echo "FAIL: machine-local absolute paths or stale kit/patterns references found (see above)"
fail=1 fail=1
fi fi