CRM на Markdown, частина 2: 27 годин, 23 коміти й каталог провалів
У першій частині я показав, як поставити CRM на markdown-файлах за вечір. Тут — як вона будувалася насправді: з цифрами, хронологією і, головне, провалами. Провали — не додаток до історії, а її скелет: майже кожна частина системи з'явилася як відповідь на конкретну аварію, і без цих аварій устрій незрозумілий.
Контекст: я робив аутрич для свого відеогенератора — шукав перших користувачів і розмови з ними. Допомагав Claude Code. Усе, що нижче, сталося за 27 календарних годин 22–23 серпня 2026 року: 23 коміти, 16 карток контактів, 12 документів, 307 рядків коду. І кожен факт тут підтверджується файлом, комітом або дослівною цитатою — правило «нічого не додумувати» поширюється й на статті про систему.
Навіщо — і чому при трьох контактах
Систему я завів, коли контактів було три. Не тридцять — три. Формулювання з коміту, з якого все почалося: «зараз три живі контакти в трьох різних місцях (листування в телезі, профіль у LinkedIn, голосове), і жодного місця, де видно, чий хід і що обіцяно».
Застереження одразу: це мій єдиний випадок, а не закономірність. Але в нього є логіка. Хаос не настає при сотні контактів — він настає при четвертому, просто помітите ви його пізніше, коли відновлювати листування вже не буде чим.
Друга причина глибша. У мене зараховуються два результати: клієнт або інсайт. Розмова, з якої я дізнався, що болю немає, — не провал, а результат. Тому в картці є розділ «чого ми не чекали» і немає поля «сума угоди». Мета визначає схему даних, а не навпаки — у CRM, що продає, і CRM дослідницької різні поля при однаковому скелеті.
І третє, про ролі. Машина збирає, готує тексти й пам'ятає все. Людина вирішує і надсилає. Цю межу проведено жорстко і, як з'ясується нижче, недарма.
Устрій: дошку не вели руками жодного дня
Картка, дошка і скрипт народилися одним комітом. Це важливіше, ніж здається: рішення «джерело правди — frontmatter картки, таблиця — похідна» було ухвалене до того, як з'явився перший рядок таблиці. Дошку не довелося «переводити на генерацію» — її взагалі ніколи не вели руками.
Три рішення в устрої, які я вважаю переносними куди завгодно.
Два пороги простроченості, а не один. Свій хід горить через два дні, чужий — через п'ять. Обґрунтування етичне, а не технічне: свій борг горить швидше за чужий — людина не зобов'язана нам відповідати, а ми зобов'язані. Одна колонка «днів з останнього дотику» злила б ці дві метрики докупи й сховала б власну бездіяльність за чужим мовчанням.
Три стани гіпотези, а не два. Скрипт друкує «НЕ ПИТАЛИ» словом, а не нулем. «Не підтвердилося» і «не питали» — різні діагнози. Перевірилося на живих даних майже одразу: одна гіпотеза дала 0 підтверджень із 3 — і це виявився промах таргетингу, у вибірці просто не було жодної людини потрібного типу. Нуль, надрукований як нуль, прочитався б як спростування.
Дві породи гіпотез — два різні поля. Гіпотеза про продукт («у них ціна непередбачувана») агрегується за всіма картками. Гіпотеза про людину («думаю, вирішує не він») не агрегується ніколи й живе всередині картки. Різні поля роблять підміну одного іншим фізично неможливою. Чому це найцінніший прийом усієї конструкції — трохи нижче, у провалах інтерпретації: дисципліна тут не спрацювала навіть у того, хто правило написав.
Межа «що генерується, а що пишеться руками» проходить по сенсу: дату прогону пише скрипт, рішення прогону — людина, рядком аргументу. Механічне — машині, смислове — людині.
Провали. Порода перша: дані
Система запам'ятала неправду або втратила правду.
Чужа фраза стала голосом власника — у трьох файлах. Репліка співрозмовниці потрапила у профіль мого голосу як моя. Умова, яка це дозволила: дослівного логу листування не існувало, картку було зібрано переказом. Переказ втрачає автора — репліки без явного «хто сказав» злипаються в один голос, і злипання спливає через тижні, коли джерело вже не відновити. Закрито правилом «дослівно, а не переказом» і ⚠-врізкою в зіпсованих файлах — не видаленням: мовчки стерти помилку означає стерти й урок.
Агент придумав офер — і записав його як мій. У полі «що ми пропонуємо людям» з'явилися «заміри по відеомоделях» — те, чого я ніколи нікому не пропонував. Агент це вигадав. Закрито таблицею «що я можу дати — моїми словами» з колонкою «кому казав»: пропозиція без джерела — це вітрина бажаного, а не факт.
Контакт без адреси втрачено назавжди. Посилання на профіль не зняли в той самий захід, а стрічка, що показала людину, не відтворюється: той самий запит через десять хвилин повернув інших людей. Дві точкові спроби знайти — повз. «Допишу потім» у системі, яка наповнюється з недетермінованої стрічки, означає «втрачу».
Порода друга: процес
Правило було — і не спрацювало.
Готовий текст поїхав під пост чужої людини. Найдорожчий промах: коментар, приготований для найціннішого входу у воронці, було опубліковано під постом іншої людини — до того ж позначеної «НЕ писати». Механізм чесний, і він не про неуважність: у картці лежало посилання на профіль, а коментар пишеться під пост. Це різні адреси, і різницю видно тільки в момент відправлення. Звідси сторож: якщо наступний крок — відправлення, а точної адреси немає, прогін падає, а на дошці горить «АДРЕСИ НЕМАЄ».
Перший варіант цього сторожа брехав. Він ловив за коренем «відповід» і спрацьовував на картці «чекаємо відповіді на два запитання» — а це очікування, адреса там не потрібна. Висновок, який поїхав прямо в коментар коду: хибне спрацювання небезпечніше за пропуск — попередження, що бреше, привчаються ігнорувати.
Мої поправки не записувалися нікуди. Рішення щодо продукту писалися в один файл, профіль голосу — в інший, факти — у картки. А поправки «як зі мною розмовляти» не писалися нікуди й лишалися в чаті — і ті самі помилки повторювалися. Розкрилося прямим запитанням: «як ти фіксуєш мої фідбеки, ти їх враховуєш?» Відповідь була «ніяк», і тієї ж години з'явився журнал поправок. Якщо в категорії знання немає файлу, знання живе в листуванні й помирає зі зміною сесії — або співробітника.
Стан процесу не жив ніде. Стан кожного контакту зберігався, а стан самого циклу — ні. Запитання «завтра відкрию нову сесію — чи зрозуміє вона, що тиждень ще не минув?» мало відповідь «ніяк». Так з'явився журнал прогонів: у процесу стан такий самий, як у запису, і він потребує власної таблиці.
Правило скасувало текст, а текст лишився. Заготовка повідомлення, забракована новими правилами, три дні пролежала в картці як робоча, і поле «наступний крок» далі казало «відправити». Знайшлося тільки суцільною інвентаризацією. Правило, що скасовує артефакт, зобов'язане скасовувати й сам артефакт — інакше заборона й об'єкт заборони мирно живуть в одному файлі.
Порода третя: інтерпретація
З правильних даних — неправильний висновок. Найдорожча порода.
Мовчання користувача пояснено продуктом. Людина переїжджала. За слідом у проді — заплатив, тримає баланс, шість списань за дві хвилини, потім тиша — я вивів: мотивація є, гроші є, доступ є, отже, зупиняє продукт. Висновок було названо найчистішим випадком у вибірці. Пояснення виявилося побутовим: переїзд. Скасовано через дві хвилини після репліки власника знання — тобто мене самого, який дізнався про переїзд із життя, а не з даних.
Механізм тут варто розкрити до кінця. У моєму ж файлі з інсайтами прямо написано: поведінка з проду пояснює «що», а не «чому». Я порушив власне правило того самого дня, коли його записав. Поведінковий слід без контексту людини бреше впевнено й переконливо — і саме тому розділення «гіпотеза про людину» і «гіпотеза про продукт» зроблено структурним, різними полями, а не дисциплінарним. Дисципліна не втримала навіть автора правила.
Висновок переписано мовчки — двічі за день. Мовчки поправлений висновок через місяць виглядає так, ніби його ніколи й не переглядали, і ціна помилки губиться разом з уроком. Тепер зіткнення фіксується як запис: що стверджувалося, що прийшло проти, що саме суперечку розв'язало.
Опитувальник збирав підтвердження нас самих. Перша версія анкети — шкали від 1 до 5 за нашим же списком болів. Людина вибере з нашого списку, ми отримаємо підтвердження себе й назвемо це дослідженням. Впіймано до розсилки — а розсилати збиралися по чужій мережі, тобто ціною помилки була чужа репутація. Список варіантів відповіді — це ваші гіпотези; агрегат за ними вимірює вас, а не ринок.
Біль 9 з 9 мало не став офером. Дев'ять із дев'яти підтвердили проблему — а троє з дев'яти вже розв'язали її безкоштовно локальним інструментом і звітували про успіх, не про біль. Лічильник підтверджень без розмітки «у кого вже розв'язано» продає чужий успіх як ваш попит.
Чотири скасування власних рішень
Цього в статтях про «як я побудував X» зазвичай немає, а для читача воно цінніше за удачі.
| Що скасували | Через скільки | Чим спростовано |
|---|---|---|
| Висновок «його зупиняє продукт» | 2 хвилини | факт із життя людини: переїзд |
| «У суперечках пріоритет за власником» | 45 хвилин | сам власник: «моя мета — ефективність, а не спроба мені догодити» |
| Відмова від окремого скіла на пошук | 7 годин | критерій був хибний: скіли діляться не за частотою, а за питанням, яке ставить людина |
| Правка пошукового рядка | один прогін | вимірювання: було 1 із 3, стало 0 із 3 |
Останній рядок — окремий урок. Лагодив я не той дефект, і правка зробила гірше. З'ясувалося це тільки тому, що промах було записано: без запису я лагодив би те саме ще раз. Запис негативного результату — і є механізм покращення; правку без виміряного «до» не відрізнити від ритуалу.
Сюди ж арифметика, яка забороняє A/B на моїх обсягах: формула n = 16σ²/δ² при базі відгуку 20% і бажаному прирості в 10 процентних пунктів вимагає 256 контактів на варіант. На сорока — шістдесяти ловиться тільки дворазовий ефект. Важливе застереження: уся ця кількісна база — з чужих каналів, B2B-пошти й американських холодних дзвінків; я переношу механізми, не цифри. Замість A/B — раунди по п'ять розмов на одному формулюванні з порогом рішення, оголошеним заздалегідь.
Правило без сторожа не виживає
Головний висновок усієї історії, і він зароблений, а не придуманий.
Щоразу, коли правило було записане прозою, його порушували. Включно з випадками, коли порушував той самий чоловік, який його написав, — у тому самому файлі, двома абзацами нижче. Вижили тільки правила, під які написано скрипт, що валить прогін. П'ять із восьми сторожів — пряма відповідь на аварію, яка вже сталася; жоден не написаний «на майбутнє, раптом знадобиться».
Сторож при цьому перевіряється мутацією: ламаєш картку навмисно — прогін зобов'язаний почервоніти рівно на ній; повертаєш — позеленіти. Зелений тест, який не можна зробити червоним, — декорація. Чесна цифра з оригіналу: мутацією я перевірив три сторожі з восьми, решта жили на слово. В опублікованій версії md-crm цей борг закрито — усі сторожі накриті 24 автотестами, і сам набір тестів перевірено мутацією: вимикаєш сторож — червоніють рівно його тести.
І фінальний штрих, що підтвердив правило на самому інструменті. Єдиний баг, який мовчки псував дані, жив у єдиному місці скрипта, не накритому тестом: магічний зсув «+24» при реальній довжині розділювача 22 символи. Перший прогін ішов іншою гілкою коду й виглядав справним, а кожен наступний врізався на два символи всередину попереднього рядка журналу. Вісім ітерацій скрипт обростав перевірками карток — і жодної перевірки самого себе.
Що з цього налаштування, а що скелет
Скелет: один файл на людину, стан у frontmatter, дошка похідна, сторожі валять прогін, журнал дослівний, стан циклу окремо від стану контактів. Це переноситься куди завгодно — включно з наймом: ATS відрізняється від CRM словником стадій, не архітектурою.
Налаштування: пороги 2 і 5 днів, контрольні точки 5/12/25 розмов, коди гіпотез. Мої значення — орієнтири із зазначеними джерелами, не константи; кожен добере свої.
Поставити все це собі — дві команди, інструкція в першій частині. Каталог провалів у знеособленому вигляді їде разом зі скілом — файлом references/failures.md: мені здається правильним, щоб інструмент возив із собою історію власних аварій.