Bring the announced contract version to the canon the corrective minor moved, and keep the platform zone's account of what it learned before the rollback.
This commit is contained in:
parent
9c2eb31a56
commit
1c5bd2a00f
2 changed files with 172 additions and 1 deletions
|
|
@ -49,6 +49,172 @@ committed_usd, which is what would let this side say "at most Y"». Движок
|
|||
чтение оценочных строк из потока. Промт ещё не написан — если у вас есть довод против любой из четырёх
|
||||
позиций, скажите ДО того, как я его напишу.
|
||||
|
||||
## ПАК «ДВЕ ОСТАНОВКИ» — ОТМЕНЁН ВЛАДЕЛЬЦЕМ, РАБОТА ОТКАЧЕНА (10.09, `textmachine-5c`)
|
||||
|
||||
> Промт `docs/PLATFORM_SOFT_STOP_SESSION_PROMPT.md` (редакция `ac9a24d`), вход HEAD `ac9a24d`.
|
||||
> **Отменён владельцем по размаху**, а не по качеству: две остановки того не стоят. Новый инвариант
|
||||
> объявлен тем же словом: **стоп остаётся ЖЁСТКИМ, терять деньги ДОПУСТИМО, но остановка обязана быть
|
||||
> КОРРЕКТНОЙ** — без гонок и половинчатых состояний, с правильным возобновлением. Предмет переехал с
|
||||
> «не сжечь деньги» на «не оставить мусор».
|
||||
>
|
||||
> ⚠ **Эта секция — не отчёт о построенном, а НАЙДЕННОЕ.** Код зоны возвращён к HEAD целиком (кроме
|
||||
> константы версии контракта, см. ниже). Здесь остаётся то, что зона УЗНАЛА о собственном дереве:
|
||||
> оно пережило пак и стоит дороже, чем стоил бы код.
|
||||
|
||||
### Что откачено и что оставлено
|
||||
|
||||
Возвращены к HEAD **26** путей в `platform/`. Удалены девять моих новых файлов:
|
||||
`internal/gates/stopgrace_test.go` · `internal/httpapi/stopmode_test.go` ·
|
||||
`internal/pgstore/migrations/00035_stop_mode.sql` · `internal/pgstore/migrations/00036_estimated_and_stop_ledger.sql` ·
|
||||
`internal/pgstore/stopmode_test.go` · `internal/runner/stop_systemd_test.go` · `internal/runner/stop_test.go` ·
|
||||
`internal/runs/estimated_test.go` · `internal/runs/stopmode_test.go`.
|
||||
|
||||
**Оставлено ОДНО и по прямому указанию оркестратора:** `internal/httpapi/capabilities.go`, константа
|
||||
`ContractVersion`. Это чужой долг, взятый попутно: канон уехал на `0.13.1` актом `D39.235` п.6, а
|
||||
константа осталась на `0.13.0`, и гейт `internal/gates.TestTheAnnouncedContractVersionIsTheOneTheCanonRatified`
|
||||
краснел с момента ратификации — базовая линия зоны была КРАСНОЙ на входе, и не по вине пака.
|
||||
⚠ В ходе пака константа стояла на `0.14.0` (свой минор: `mode` у `/stop` плюс `Run.stop_mode`, по
|
||||
ратифицированному порядку «код первым»); с отменой пака этого минора нет, поэтому при откате она
|
||||
приведена к **`0.13.1`** — к тому, чему билд действительно служит.
|
||||
|
||||
### ⛔ Что зона узнала о СЕБЕ — это и есть результат смены
|
||||
|
||||
Ниже разделено на «замерено исполнением», «прочитано в коде» и «латентно» намеренно: смешивать их
|
||||
дороже обычного, потому что по ним заводятся строки. Адреса — по HEAD `ac9a24d`.
|
||||
|
||||
**1. `Runner.Stop` блокируется на весь грейс — ЗАМЕРЕНО.** `internal/runner/runner.go:195` зовёт
|
||||
`systemctl --user stop` без `--no-block`, `runCommand` (`:106-112`) своего тайм-аута не имеет.
|
||||
Пробник на транзиентном юните с `TimeoutStopSec=15`, процесс ловит SIGTERM и не выходит (systemd 259):
|
||||
блокирующая форма вернулась через **15,058 с** (по SIGKILL), `stop --no-block` — через **0,014 с**,
|
||||
юнит в `deactivating`. Сегодня движок на жёстком сигнале умирает за секунды, поэтому это невидимо.
|
||||
Носители последствия: `WriteTimeout: 30 s` (`cmd/tmplatformd/main.go:208`) и свип (п.2).
|
||||
|
||||
**2. Израсходованный бюджет прогона свип считает ПРОВАЛОМ реконсиляции — прочитано в коде.**
|
||||
`reconcileOne` (`internal/runs/reconcile.go:89-90`) заворачивает каждый прогон в `s.runBudget()`
|
||||
(дефолт 60 с), и комментарий на `:92-115` говорит буквально «SPENDING the budget counts as a failure
|
||||
even when nothing reported one». ⇒ блокирующий `Stop` внутри свипа (`:580`) даёт `ReconcileFailures++`,
|
||||
отсрочку и через `StalledAfter` ЛОЖНУЮ тревогу оператору, плюс съеденную долю прохода для всех
|
||||
остальных аккаунтов. ⚠ Прежняя формулировка оркестратора («свип встанет до двадцати минут») неверна
|
||||
по механизму и исправлена актом `D39.236` п.5 по возражению этой смены.
|
||||
|
||||
**3. Повторный `stop` на юните в `deactivating` ИНЕРТЕН — ЗАМЕРЕНО, и это несущее свойство.**
|
||||
Три стопа в t+0 / t+2 / t+4 против грейса 6 с: процесс получил **один** сигнал, юнит умер на
|
||||
**t+6,04 с** (не t+10). То есть пере-выдача не шлёт второго сигнала и не перезапускает
|
||||
`TimeoutStopSec`. ⭐ Ценность не в хорошей половине, а в том, что комментарий свипа
|
||||
(`reconcile.go:576-579`) УЖЕ утверждает «asking again is free and idempotent», и это утверждение
|
||||
держится на недокументированном поведении systemd, которое до этой смены никто не проверял.
|
||||
⚠ И вторая половина того же комментария — «it is the only thing that closes the first case» —
|
||||
**не выполняется**, если юнит уже начал останавливаться: пропущенный первый SIGTERM свип не чинит,
|
||||
юнит стоит в `deactivating` до SIGKILL по грейсу (сегодня — до десяти минут прогона, который ничего
|
||||
не делает, с зарезервированным холдом).
|
||||
|
||||
**4. Два SIGTERM подряд Go-процесс видит как ОДИН — ЗАМЕРЕНО, ратифицировано `D39.238`.**
|
||||
gap 0 мс → оба сигнала увидены в **0 из 20** прогонов (в длинной серии слиплись 40 из 40);
|
||||
gap 1 мс → **20 из 20**; буфер принимающего канала **8** — коалесценция живёт НИЖЕ канала, в
|
||||
рантаймовом БИТЕ на сигнал. ⚠ Первый мой прибор давал «2 из 2» и подтверждал удобное: `kill` шёл из
|
||||
шелла, и запуск двух процессов сам создавал зазор. Прибор чинен, число перевернулось.
|
||||
Норма, выведенная из этого оркестратором: **замер, чей результат подтверждает удобное, проверяется на
|
||||
ПРИБОР прежде, чем на предмет.**
|
||||
|
||||
**5. ⛔ `finish` рассчитывает деньги из ДО-ДРЕНАЖНОЙ выписки — ЛАТЕНТНО, но класс настоящий.**
|
||||
`reconcile.go:955-958`: расчёт внутри `finish` объявлен оппортунистическим и получает тот самый `l`,
|
||||
который проход прочитал ДО дренажа журнала. `finish` вручную освежает `l.Status`/`l.PausedReason`
|
||||
перед вызовом (`:~918`) — то есть автор знал про класс и закрыл ровно два поля. **Любое число, которое
|
||||
дренаж ЭТОГО ЖЕ прохода записал в строку попытки, для этого расчёта невидимо.**
|
||||
⚠ Граница честности: на HEAD ничего не сломано — `settle` не читает из выписки ни одного поля, которое
|
||||
пишет дренаж. Класс предъявлен исполнением только потому, что пак добавил такое поле: строка попытки
|
||||
читала 9 оценочных строк, выписка — 0, и расчёт взял число, которое случайно знал запасной канал.
|
||||
Поле ушло с паком, класс остался. Родственник в этом же файле уже стоил зоне дефекта: `freshRunState`
|
||||
(`reconcile.go:~660`) написан ровно потому, что выписка не может знать, что записал её собственный дренаж.
|
||||
|
||||
**6. Обработчик `/stop` шлёт сигнал по имени юнита из СВОЕЙ ЖЕ выписки — прочитано в коде.**
|
||||
`Service.Stop` (`reconcile.go:1541`) берёт `unit` из `RequestStop` и зовёт `Runner.Stop(ctx, unit)`.
|
||||
Успел ли свип между этими двумя рестартовать попытку — сигнал уйдёт СТАРОМУ юниту. Самолечение есть
|
||||
(следующий проход увидит `alive && StopRequestedAt != nil` и пере-выдаст стоп текущему, `:575-583`),
|
||||
но приходит оно от СВИПА, а не от обработчика: окно равно одному тику, и всё это время пользователь
|
||||
держит `202`, а прогон работает.
|
||||
|
||||
**7. Двойной клик по `/stop` — гонка по построению, сегодня безвредная.** `RequestStop`
|
||||
(`internal/pgstore/runs.go:1298-1309`) — один UPDATE; под READ COMMITTED два одновременных нажатия
|
||||
не различимы самим оператором, потому что `RETURNING` отвечает НОВОЙ строкой и не может отличить
|
||||
собственную запись от чужой. Схлопывание в первый таймстамп делает оба нажатия одинаковыми, поэтому
|
||||
вреда нет — ровно до той секунды, когда ответ или действие начнут зависеть от того, кто нажал первым.
|
||||
Форма лечения, проверенная в отменённом паке: `select … for update` в одной транзакции с UPDATE.
|
||||
|
||||
**8. `stoppedOnRequest` сравнивает ДВОЕ РАЗНЫХ ЧАСОВ, и код это признаёт.** `reconcile.go:1137-1153`,
|
||||
довод на `:~1000`: «the two clocks being compared are not even the same one — the marker carries the
|
||||
host's, the intent the platform's». Сегодня цена ошибки — ЯРЛЫК. В любой конструкции, где от этого
|
||||
ярлыка зависит ДЕЙСТВИЕ (перезапуск, возврат холда, возобновление), это гонка по построению.
|
||||
|
||||
**9. Каталог движка: потолка ожидания нет у пяти провайдеров из восьми — ЗАМЕРЕНО прибором.**
|
||||
Программа на `config.LoadModels` + `Timeouts.Profile().DeadlineFor(B)` над снапшотом `git archive HEAD
|
||||
backend` (ни байта записи в чужую зону): `attempt_max_s` задан у **3** (`deepseek` 1240 · `kimi` 1200 ·
|
||||
`gemini` 1200), инертен у **5** (`zai` · `xai` · `openai` · `mistral` · `local`), и у них `DeadlineFor`
|
||||
растёт с бюджетом линейно и сверху не ограничена. Максимум по каталогу при наибольшем бюджете
|
||||
сегодняшней конфигурации (**35 200** ток. = редакторские `max(8000×2.2, 16000) = 17 600` с одним
|
||||
удвоением по `regenerate_before_escalate: 1`) — **1240 с**, deepseek. Беcключевой `zai` перерастает его
|
||||
при **43 400** ток., то есть при `edit_ceiling_out` ≈ 9 900 против сегодняшних 8 000 — одна строка
|
||||
yaml, которая читается как ручка качества. Носитель — строка бэклога **383**.
|
||||
|
||||
**10. Dev и прод расходятся по грейсу в 20 раз, и это не объявлено нигде.**
|
||||
`internal/ingest/supervisor.go:18` — `stopGrace = 30 * time.Second` (уезжает в `cmd.WaitDelay`, `:75`)
|
||||
против `600 с` в проде (`internal/runner/runner.go:59`). Один и тот же движок против тех же платных
|
||||
провайдеров получает на свёртку полминуты на dev-пути и десять минут на боевом.
|
||||
|
||||
**11. Пять открытых рядов реестра — про холды и остановку, и это один предмет, а не пять.**
|
||||
Их печатает сам гейт зоны (`make check`, `internal/gates`, строка `ALARM PD-count: 15 open rows …
|
||||
(baseline 15)`): **PD-418** (у settling-строки живого прогона нет ручки, а рантбук обещает обратное) ·
|
||||
**PD-244** (единственный выход `settle`, оставляющий холд открытым МОЛЧА) · **PD-162** (книга с
|
||||
пропавшим каталогом заклинивает прогон навсегда с открытым холдом) · **PD-465** (заявка на спавн
|
||||
коммитится ДО подъёма юнита) · **PD-217** (незакрытый холд блокирует апгрейд движка бессрочно).
|
||||
Под новым инвариантом это готовый предметный список, уже приоритизированный реестром.
|
||||
|
||||
**12. Что в дереве оказалось ЛУЧШЕ ожидаемого — и это тоже находка.** Гонка «кто пишет исход мёртвого
|
||||
прогона» ЗАКРЫТА, и закрыта правильной формой: `FinishRun` возвращает `closed bool`, и `finish`
|
||||
(`reconcile.go:903`, ветка `!closed` ~`:937`) разбирает случай «прогон уехал под этим проходом» явно;
|
||||
второй расчёт получает `ErrNoReservation`, потому что резервация закрывается под `state = 'open'`.
|
||||
Столкновение реконсайлера с `/stop` названо в трёх местах и в каждом закрыто: `finishStopped`
|
||||
(`:678-685`), отказ рестарта по `ErrStopRequested` (`:1324`), пере-проверка заявки под замком в
|
||||
`FinishUnspawnedStop` (`:~710`). ⇒ **модель «записать условно и прочитать вердикт» уже живёт в зоне**,
|
||||
и новому инварианту стоит брать её образцом, а не изобретать другую.
|
||||
|
||||
**13. И одна развилка, которую новый инвариант обязан пересмотреть ЯВНО.**
|
||||
`interruptedBySomeoneElse` (`reconcile.go:539`, функция `:1129`) ПЕРЕЗАПУСКАЕТ прогон по коду 5 без
|
||||
записанного намерения. Это осознанное решение с записанным доводом (ребут против ручного
|
||||
`systemctl stop`, асимметрия цен), а не дефект — но это главная развилка «правильного возобновления»,
|
||||
и наследовать её молча нельзя.
|
||||
|
||||
### Числа смены
|
||||
|
||||
```
|
||||
$ make check (все четыре гейта стенда закрыты: Postgres 55433 · движковый бинарь и шаблон из
|
||||
`git archive HEAD backend` · достижимый пользовательский systemd · MemoryMax)
|
||||
вход (до первой правки): MAKE-EXIT=2 · 19 `ok` · 1 FAIL · линтер «0 issues» · скипов 5
|
||||
единственный красный — `internal/gates.TestTheAnnouncedContractVersionIsTheOneTheCanonRatified`
|
||||
(«this build announces contract 0.13.0 and the ratified canon is 0.13.1»), НЕ по вине смены
|
||||
скипы 5, условие у всех одно и названное: нет деплой-артефакта `configs/mining-contrast.zh.txt`
|
||||
|
||||
ПОСЛЕ ОТКАТА (последний прогон, снят после последней правки):
|
||||
MAKE-EXIT=0 · 20 `ok` · 0 FAIL · линтер «0 issues» · скипов 5
|
||||
`gofmt`/`go vet`/`sqlc diff` чисты · `ALARM PD-count: 15 open rows … (baseline 15)` — не сдвинут
|
||||
красный входа ПОГАШЕН правкой константы: канон `0.13.1`, билд `0.13.1`
|
||||
```
|
||||
⚠ Гейты стенда закрыты все четыре намеренно: без `TM_PLATFORM_TEST_DSN` та же батарея печатает те же
|
||||
`ok` и прячет ~370 тестов (эррата `08.09-д`). Счёт скипов печатается рядом со счётом `ok`.
|
||||
|
||||
### Что НЕ удалось
|
||||
|
||||
**Ничего из заказанного не сдано — пак отменён на середине, и это не оценка работы, а факт.**
|
||||
Построенное и зелёное на момент отмены (грейс суммой с названными слагаемыми, `--no-block`, путь
|
||||
второго сигнала через наблюдение `SubState`, режим в записи намерения с атомарной заявкой на второй
|
||||
сигнал, чтение минора `1.4` из обоих каналов, пометка оценки на строке расчёта, живой systemd-стенд)
|
||||
откачено целиком. Из адверсариального прохода §5.5 успели отработать три направления из шести —
|
||||
(а) гонка двух нажатий, (б) остановка мёртвого юнита, (д) пере-выдача не должна эскалировать; (в)
|
||||
чтение мягко остановленного прогона реконсиляцией, (г) идемпотентность `/stop` и (е) удлинённое окно
|
||||
`deactivating` до отмены проверены не были. Мутационная проверка СВОИХ новых пинов — та, которую
|
||||
норма зоны требует отдельным заходом, — не выполнена ни для одного из них: до неё смена не дошла.
|
||||
Поэтому ни один пин выше в дереве не остаётся, и ни на один нельзя ссылаться как на проверенный.
|
||||
|
||||
## Состояние зоны на 08.09.2026
|
||||
|
||||
| Вопрос | Ответ |
|
||||
|
|
|
|||
|
|
@ -36,7 +36,12 @@ import "net/http"
|
|||
// the bar described in chapters while the wire sent units, `ordered_chapters: 0` promised where the
|
||||
// wire sent 2, three values of a vocabulary that had four — because the NUMBER matched each time. The
|
||||
// bump keeps the number from lying; it does not close the blind spot, which is unified backlog row 309.
|
||||
const ContractVersion = "0.13.0"
|
||||
//
|
||||
// ⚠ 0.13.1 is the canon's CORRECTING minor (D39.235 §6) and this constant did not follow it for a day:
|
||||
// the canon moved first and the gate stood red the whole time, which is the reverse of the order the
|
||||
// paragraph above ratifies. The number is brought to the canon here rather than the other way round,
|
||||
// because the canon is the one that is right — the wire this build serves is the 0.13.1 shape.
|
||||
const ContractVersion = "0.13.1"
|
||||
|
||||
// Capabilities is what this deployment can do: one flat document, the same for every account.
|
||||
type Capabilities struct {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue