конвенции: перенести механизируемое в golangci-lint и internal/archrules

Правило, которое проверяет машина, не должно оставаться прозой: файл конвенций
на сотни строк размазывает внимание по тривиальному — модель добросовестно
проверит именование полей лога и не дойдёт до формы решения.

Включены sloglint (константный msg, стиль ключ-значение), forbidigo (fmt.Print*,
os.Getenv, time.Now мимо store.Now), errorlint (сравнение ошибок), depguard
(сторонние пакеты ошибок). internal/archrules — сканеры на то, что линтером не
выражается: направление зависимостей ядро↔транспорты, AUTOINCREMENT и серверное
время в новых миграциях, матчинг ошибки по тексту.

Код приведён к правилам: logging.StartCall как единая точка отсчёта длительности
внешних вызовов, store.Now вместо time.Now в httpapi и часах воркера,
slog.DiscardHandler в тестах.

Перенесённое вычеркнуто из docs/conventions/* и openspec/config.yaml — прозой
осталось только то, что правилом не выражается.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-07-23 18:17:40 +03:00
co-authored by Claude Opus 4.8
parent 6792f7082a
commit 612344bab3
28 changed files with 325 additions and 98 deletions
+6 -6
View File
@@ -141,7 +141,7 @@ func (c *Client) login(ctx context.Context) error {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Referer", c.base.String()) // qBit проверяет Referer/Host
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "auth/login", Start: time.Now()}
call := logging.StartCall(logging.ServiceQBittorrent, "auth/login")
resp, err := c.hc.Do(req)
if err != nil {
call.Failure(log, err)
@@ -221,7 +221,7 @@ func (c *Client) Add(ctx context.Context, ar AddRequest) error {
payload := buf.Bytes()
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/add", Start: time.Now()}
call := logging.StartCall(logging.ServiceQBittorrent, "torrents/add")
resp, err := c.do(ctx, func() (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.endpoint("/api/v2/torrents/add"), bytes.NewReader(payload))
@@ -278,7 +278,7 @@ func (c *Client) Delete(ctx context.Context, hashes []string, deleteFiles bool)
body := form.Encode()
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/delete", Start: time.Now()}
call := logging.StartCall(logging.ServiceQBittorrent, "torrents/delete")
resp, err := c.do(ctx, func() (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.endpoint("/api/v2/torrents/delete"), strings.NewReader(body))
@@ -319,7 +319,7 @@ func (c *Client) RenameTorrent(ctx context.Context, hash, name string) error {
body := form.Encode()
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/rename", Start: time.Now()}
call := logging.StartCall(logging.ServiceQBittorrent, "torrents/rename")
resp, err := c.do(ctx, func() (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.endpoint("/api/v2/torrents/rename"), strings.NewReader(body))
@@ -350,7 +350,7 @@ func (c *Client) RenameTorrent(ctx context.Context, hash, name string) error {
// Torrents возвращает задачи указанной категории (пустая — все).
func (c *Client) Torrents(ctx context.Context, category string) ([]Torrent, error) {
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/info", Start: time.Now()}
call := logging.StartCall(logging.ServiceQBittorrent, "torrents/info")
resp, err := c.do(ctx, func() (*http.Request, error) {
u := c.endpoint("/api/v2/torrents/info")
if category != "" {
@@ -386,7 +386,7 @@ func (c *Client) Torrents(ctx context.Context, category string) ([]Torrent, erro
// распознаванию как один из сигналов; абсолютный путь — join(save_path, Name).
func (c *Client) Files(ctx context.Context, hash string) ([]File, error) {
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/files", Start: time.Now()}
call := logging.StartCall(logging.ServiceQBittorrent, "torrents/files")
resp, err := c.do(ctx, func() (*http.Request, error) {
u := c.endpoint("/api/v2/torrents/files?hash=" + url.QueryEscape(hash))
return http.NewRequestWithContext(ctx, http.MethodGet, u, nil)