Read in:
Русский

Имя файла, которое объясняет файл

Команда AI Mindset называет заметки в своих хранилищах по одной схеме:

{project} {type} title – YYYY-MM-DD – tool.md

Например, {blog} {article} Weekly summary – 2026-10-18 – Cursor.md. Проект и тип текста идут первыми, в фигурных скобках. Дата всегда стоит ближе к концу, после тире. Последняя часть называет инструмент, который написал заметку: Cursor, Claude Code, ChatGPT или Manual, если заметку писали руками.

Почему это работает

Имя отвечает на три вопроса до того, как файл открыли: к чему он относится, что это за текст и когда его написали. Это видит человек, который листает папку. Это видит и агент, которому дали список файлов и нужно выбрать один: пока он не открыл ни одного файла, кроме имён у него часто ничего нет.

Инструмент в конце говорит, откуда взялся текст. Заметке из Claude Code и заметке, написанной руками, доверяешь по-разному.

Почему это ломается

Правила самого соглашения называют дату в середине имени критической ошибкой и возвращаются к этому в обоих неправильных примерах. Так громко правило пишут, когда его постоянно нарушают.

Нарушают его агенты: модель каждый раз пишет имя чуть иначе. {2026-10-18} в фигурных скобках, обычный дефис вместо тире, подчёркивания вместо пробелов. В моём собственном хранилище по этому соглашению названы четыре заметки, и три из них получились такими: {rule}_…_–_2025_12_18_–_Cursor.md. Соглашение, которое живёт только в заметке, — это пожелание.

Проверка в trip2g

В trip2g такое правило становится валидатором: заметкой с блоком Jsonnet, который при каждой синхронизации проверяет имя каждой заметки. Положите его в validators/file_name.md:

local re = std.native('regexMatch');
local parts = std.split(path, '/');
local file = parts[std.length(parts) - 1];
local name = std.substr(file, 0, std.length(file) - 3);
local chunks = std.split(name, ' – ');
local tools = [
  'Claude Code', 'Cursor', 'ChatGPT', 'Gemini', 'Windsurf', 'Manual',
];
local err(msg) = {level: 'error', msg: msg};
local warn(msg) = {level: 'warning', msg: msg};

local checkTool(tool) =
  if std.member(tools, tool) then []
  else if re('^[A-Z][A-Za-z0-9 .]*$', tool) then [
    warn('new tool "%s"? add it to the list in this validator' % tool),
  ] else [
    err('tool "%s" must be a name like %s' % [tool, std.join(', ', tools)]),
  ];

if std.length(chunks) != 3 then [
  err('name must be "{project} {type} title – YYYY-MM-DD – tool"'),
] else
  local head = chunks[0], date = chunks[1], tool = chunks[2];
  (
    if re('^\\{[a-z0-9]+\\} \\{[a-z]+\\} .+$', head) then []
    else [err('name must start with {project} {type} and a title')]
  ) + (
    if re('^\\d{4}-\\d{2}-\\d{2}$', date) then []
    else [err('date "%s" must be YYYY-MM-DD' % date)]
  ) + checkTool(tool) + (
    local fmDate = std.toString(std.get(meta, 'date', date));
    if fmDate == date then []
    else [err('frontmatter date %s differs from %s in the name'
              % [fmDate, date])]
  )

Валидатор делит имя по –, то есть по тире с пробелами с обеих сторон, поэтому обычный дефис сразу не проходит. Если во frontmatter заметки есть date, она должна совпадать с датой в имени. Инструмент сверяется со списком. claude-code — ошибка, а правильно написанное имя, которого в списке ещё нет, например Codex, — только предупреждение с просьбой его добавить: новые инструменты появляются быстрее, чем кто-то обновляет список, и новый инструмент не должен ломать каждую синхронизацию.

Чтобы соглашению следовало всё хранилище, не добавляйте validate в каждую заметку. Один frontmatter-патч подключает валидатор ко всем заметкам, включая новые:

---
type: frontmatter-patch
include: ["**/*.md"]
exclude: ["validators/**", "patches/**"]
---

Все заметки хранилища следуют соглашению об именах.

```jsonnet
local own = std.get(meta, 'validate', []);
{
  validate: (if std.isString(own) then [own] else own)
    + ['[[file_name]]'],
}
```

Патч добавляет file_name к валидаторам, которые у заметки уже есть, а не заменяет их. exclude исключает сами валидаторы и патчи, а заодно служебные заметки, имена которых живут по другому правилу.

При следующей синхронизации заметка с неправильным именем получит сообщение:

[CRITICAL] validate validators/file_name.md: date "18.10.2026" must be YYYY-MM-DD

CLI синхронизации завершится с кодом 1, и агент, который написал заметку, увидит, что её нужно переименовать. Админка покажет все такие заметки, а trip2g lint найдёт их в CI ещё до синхронизации.

Чего он не делает

Валидатор помечает заметку, но не переименовывает её и не блокирует синхронизацию. Список инструментов вы правите сами, а предупреждение подскажет, когда пора. Заметки, которые живут по другим правилам, например ежедневные 2026-10-18.md, идут в exclude или получают свой валидатор.