Return the refused retry to the standard stop instead of inventing an outcome: the position gets a mark, the frame already carries the shortfall, and a top-up resume finishes it
This commit is contained in:
parent
baa06cef7b
commit
b0cb7a40f2
1 changed files with 57 additions and 87 deletions
|
|
@ -56,110 +56,80 @@
|
|||
|
||||
## 4. Состав
|
||||
|
||||
### 4.1 Деградация — **делай РОВНО так в ЧАСТИ РЕШЕНИЯ, сам решай КАК**
|
||||
### 4.1 ⛔ КОНСТРУКЦИЯ ПЕРЕ-РЕШЕНА ВЛАДЕЛЬЦЕМ 11.09 — читай это ПЕРЕД кодом
|
||||
|
||||
**Ровно так (решено владельцем, отступление = пинг):**
|
||||
- отказ резервации ⇒ **флаг**, а не ошибка наверх — ⛔ **но предикат НЕ «индекс попытки ≥1»: эта
|
||||
формулировка была моей, и она теряла бы юниты НАВСЕГДА** (поймала исполняющая сессия, я пере-проверил
|
||||
кодом). Индекс попытки ПЕРЕ-ПРИСВАИВАЕТСЯ прогулкой по сожжённым ключам
|
||||
(`backend/internal/pipeline/stagerun.go:201`=`attempt = att.attempt`; правило —
|
||||
`stagerun.go:519`=`BURNED KEYS ARE WALKED OVER HERE`), поэтому после остановленного прогона ПЕРВАЯ
|
||||
свежая покупка позиции уже идёт под индексом ≥1, а в руках нет ни текста, ни классификации. Флаг там
|
||||
написал бы вердикт юниту, которого ни разу не перевели, — и поскольку `resolvedForResume` истинна для
|
||||
любого флага, кроме `cancelled` (`cutcall.go:293`=`FlagReason(cs.FlagReason) != FlagCancelled`), юнит
|
||||
стал бы терминальным вместе со сожжёнными деньгами. ⇒ **предикат: «в руках есть ОПЛАЧЕННАЯ
|
||||
КЛАССИФИЦИРОВАННАЯ попытка»** (есть `judged`/`last`), а не «индекс ≥1»;
|
||||
- у флага **СВОЯ причина**, отличная от исчерпания регенераций: читатель и оператор обязаны отличать «мы
|
||||
перепробовали всё» от «денег не хватило на вторую попытку»;
|
||||
- волна **идёт дальше**.
|
||||
**Слово владельца дословно:** «Надо о таком просто сообщать в виде метаданты, которая сейчас и пишется
|
||||
предупреждением, и иметь возможность поднять просто потолок и возобновить прогон, так как **это ситуация
|
||||
всё же СТАНДАРТНАЯ СО СТОПОМ, просто он произошёл по какой-то другой причине**».
|
||||
|
||||
⛔ **ЧЕГО В ЭТОМ СПИСКЕ БОЛЬШЕ НЕТ И ПОЧЕМУ — читай, это главное место пака.** Первая редакция промта
|
||||
требовала, вслед за `D39.204` п.4, чтобы «текст попытки 0 **отгружался**». **Это невыполнимо как написано,
|
||||
и поймал это опровергатель, а не я.** В цикл ретрая попадают ТОЛЬКО флагованные попытки ретраебельных
|
||||
причин — `length`/`empty` плюс эхо-реролл (`backend/internal/pipeline/disposition.go`, греп `retryable`).
|
||||
Значит «оплаченный текст попытки 0» **по построению** обрезан, пуст либо является эхом исходника. А
|
||||
флагованная диспозиция (кроме `FlagSanitizerStripped`) оставляет пустой финальный текст, следующая стадия
|
||||
скипается, и экспорт не отдаёт ничего — `backend/internal/pipeline/holes.go` называет это `withheld` прямо:
|
||||
«contaminated output never ships» (`D2`).
|
||||
⛔ **Это отменяет обе половины прежнего заказа — и моего, и `D39.204` п.4.** Ни «деградировать во флаг», ни
|
||||
«волна идёт дальше» больше НЕ строится. Прежний план заводил новый исход; новый — **возвращает ситуацию в
|
||||
УЖЕ ПОСТРОЕННЫЙ штатный путь остановки**, из которого она сегодня выпадает.
|
||||
|
||||
⇒ **настоящий выигрыш пака формулируется так, и он не меньше прежнего, а честнее:**
|
||||
**невидимая дыра `pending`** (строки нет · юнит заклинен · `exit 4`) **→ НАЗВАННАЯ дыра `withheld`**
|
||||
(строка есть · причина денежная и названа · волна идёт · прогон кончается штатно).
|
||||
⛔ **ОТВЕТ ВЛАДЕЛЬЦА ПОЛУЧЕН 11.09, и он снимает половину вопроса: «пока платит за всё ЮЗЕР, потом будем
|
||||
решать эту проблему».** ⇒ **ТВОЙ ПАК НЕ МЕНЯЕТ, КОМУ ЧТО ВЫСТАВЛЯЕТСЯ, НИ В КАКУЮ СТОРОНУ.** Никакого
|
||||
механизма «платит продукт» не строить; существующее биллинговое поведение не трогать даже там, где оно
|
||||
выглядит непоследовательным. Предмет пака — **ВИДИМОСТЬ дыры, а не её цена.**
|
||||
**Что это значит механически (моё чтение; спорь, если код скажет иначе):**
|
||||
|
||||
⚠ И назови это в отчёте отдельной строкой, чтобы следующая смена не открывала заново: объёмный леджер
|
||||
СЕГОДНЯ считает флагованный юнит НЕ доставленным, то есть грант за него не съеден — это **расходится** со
|
||||
словом «платит за всё юзер» и существует ДО твоего пака. **Не чини, не оптимизируй, просто НАЗОВИ**:
|
||||
владелец объявил вопрос отложенным.
|
||||
1. **Прогон ОСТАНАВЛИВАЕТСЯ, как при любом отказе по потолку.** Не продолжается с флагом. ⭐ И это заодно
|
||||
снимает то, чем ряд 291 пугал: «продолжить волну нельзя без ДЫРЫ в непрерывном префиксе» — при остановке
|
||||
дыры не возникает вовсе.
|
||||
2. **Отказ ретраю публикует ту же метадату, что и любая остановка по потолку** — кадр
|
||||
`Ceiling{Halted, Scope, ShortfallMicroUSD}` (`backend/internal/runevents/runevents.go`, греп
|
||||
`ShortfallMicroUSD`). ⭐ **Он уже несёт ровно то, что владелец просит:** «пополни хотя бы на столько, и
|
||||
тот же вызов будет допущен» — и это **ратифицировано им же, `D39.203`**. Строить новый носитель не надо.
|
||||
⚠ **И твоя находка №2 этим РАЗРЕШАЕТСЯ, а не отменяется:** ты была права, что `ceilingReached`
|
||||
идемпотентен по прогонному флагу и съел бы событие настоящей остановки — но теперь съедать НЕЧЕГО: эта
|
||||
остановка И ЕСТЬ настоящая. Публикуй через штатный путь.
|
||||
3. ⛔ **НАСТОЯЩИЙ ДЕФЕКТ — УЖЕ, чем я написал, и он назван в самом дереве.** Механизм «отметить
|
||||
остановленную позицию» построен ИМЕННО против невидимых дыр:
|
||||
`backend/internal/pipeline/stagerun.go:142`=`THE MARK FOR A STOPPED POSITION IS ATTACHED TO EVERY EXIT`,
|
||||
и его комментарий приводит замер прежнего инцидента — «прогон, остановленный на эскалационном хопе,
|
||||
рассчитал деньги и не оставил строки вовсе: `committed=0.001176`, `chunk_status_rows=0`». **Но метка —
|
||||
no-op для всего, что не «доставленный вызов, который остановил человек»** (`recordCancelledStage`).
|
||||
Отказ по потолку под это не подпадает ⇒ юнит остаётся без строки. **Вот дыра, и вот что ты закрываешь.**
|
||||
4. **Поднял потолок → резюм ДОДЕЛЫВАЕТ юнит.** Значит строка, которую ты пишешь, обязана быть
|
||||
НЕ-ТЕРМИНАЛЬНОЙ для резюма — по образцу `FlagCancelled`, единственной причины, у которой
|
||||
`resolvedForResume` ложен (`backend/internal/pipeline/cutcall.go:293`=`FlagReason(cs.FlagReason) != FlagCancelled`),
|
||||
и ложен именно потому, что это «оплачено, но не сделано». Твой случай той же природы.
|
||||
|
||||
⚠ Отгрузка обрезанного текста остаётся невыполнимой по `D2` независимо от того, кто платит. Сам не строй.
|
||||
**Делай РОВНО так:** остановка штатная · метадата через уже построенный кадр с недостачей · **юнит получает
|
||||
строку, а не пустоту** · строка НЕ терминальна для резюма · после пополнения резюм доделывает.
|
||||
|
||||
⛔ **И ВТОРОЕ, без чего пак сделает читателю хуже: ДЕНЬГИ ОБЯЗАНЫ ОСТАТЬСЯ ВИДНЫ.** Сегодня отказ ретрая
|
||||
публикует денежный факт: событие `ceiling` + защёлка + `exit 4`, и платформа пишет прогону `paused` и
|
||||
просит пополнить. После деградации этот путь исчезает: терминальные ветки `flagged` и `clean` несут только
|
||||
объём, а `Money` висит на ошибочных (`backend/internal/pipeline/events.go`, греп `terminal`). **Читателю
|
||||
кончились деньги — и никто ему этого не говорит.** ⛔ **НО НЕ ЧЕРЕЗ `ceilingReached` — развилку разрешила исполняющая сессия, и её довод сильнее моего
|
||||
первоначального «публикуй».** Я пере-проверил кодом: событие идемпотентно по ПРОГОННОМУ флагу
|
||||
(`backend/internal/pipeline/events.go:454`=`already := e.ceilingSaid`), поэтому отказ ретраю **СЪЕЛ БЫ
|
||||
событие настоящей остановки** позже в том же прогоне, и платформа осталась бы с остановкой без причины.
|
||||
Плюс `Halted: true` переводит идущий прогон в паузу НА ХОДУ. ⇒ **делай РОВНО так: денежный факт виден
|
||||
НОВОЙ ПРИЧИНОЙ на проводе юнита плюс операторской строкой с недостачей (`shortfall`); событие
|
||||
`ceiling`-остановки НЕ публикуется.** Довод записывается и в код, и в отчёт.
|
||||
**Решаешь сам и аргументируешь:** как назвать причину в строке юнита (или взять существующую) · где ставится
|
||||
метка · как отличить «остановлено на ретрае» от «остановлено на попытке 0» в носителе, и надо ли отличать.
|
||||
|
||||
⚠ **И зеркало, которое твой пак делает ложным В СТОРОНУ ОТСУТСТВИЯ:** `backend/internal/runevents/runevents.go:289`=`ITS PRESENCE RULE IS`
|
||||
говорит «правило присутствия — „потолок был ДОСТИГНУТ“, а не „исход = ceiling“». После пака потолок бывает
|
||||
достигнут, а `Money` нет. ⇒ сузь прозу до «ОСТАНОВИЛ прогон» **в своей зоне** и **назови мне зеркала вне
|
||||
её** (платформенный приёмник событий, канон 14) — я отнесу. Чужое не правь.
|
||||
⛔ **Попытку 0 не трогать:** её отказ и сегодня останавливает книгу штатно — ратифицировано.
|
||||
|
||||
**Решаешь сам и аргументируешь:** имя причины · где ставится деградация. ⛔ **И вот ШТАТНЫЙ носитель,
|
||||
который первая редакция промта пропустила:** у `runAttempt` уже есть параметр **`mandatory`** — «позволено
|
||||
ли отказанной резервации ЖДАТЬ освободившихся денег»; волна зовёт его с `true`, а эскалационный хоп,
|
||||
под-шаг ремонта и терминология зовут с `false` и потому деградируют (`backend/internal/pipeline/stagerun.go`,
|
||||
греп `mandatory`; правило — `reservegate.go`, греп `waitNotAllowed`). Это ровно тот класс, под который
|
||||
механизм построен. ⚠ **Но у него есть цена, и её надо назвать прежде, чем брать:** `mandatory=false`
|
||||
отнимает не только остановку, но и ПРАВО ЖДАТЬ — ретрай деградирует даже там, где деньги освободились бы
|
||||
через миллисекунду сеттлом соседа. **Остановка и ожидание — два решения на одном флаге.**
|
||||
⛔ **КТО ПЛАТИТ — НЕ ПРЕДМЕТ ЭТОГО ПАКА.** Слово владельца 11.09: «пока платит за всё ЮЗЕР, потом будем
|
||||
решать эту проблему». Механизм «платит продукт» не строить; существующее биллинговое поведение не трогать
|
||||
ни в какую сторону, даже где оно выглядит непоследовательным. ⚠ И отдельной строкой отчёта НАЗОВИ (не чини):
|
||||
объёмный леджер сегодня считает флагованный юнит НЕ доставленным, то есть грант за него не съеден — это
|
||||
расходится со словом владельца и существует ДО твоего пака. Он объявил вопрос отложенным.
|
||||
|
||||
⛔ **Попытку 0 не трогать:** её отказ по-прежнему ОСТАНАВЛИВАЕТ книгу — ратифицировано, менять нельзя.
|
||||
|
||||
⚠ **Гипотеза, которую надо ПРОВЕРИТЬ, а не принять:** деградация сама по себе закрывает и следствие (2) —
|
||||
у юнита появляется статус, значит дыры в непрерывном префиксе нет. Похоже на правду; предъяви замером.
|
||||
⚠ **Отгрузка обрезанного текста не строится** — `D2` запрещает отгружать загрязнённое, и это не зависит от
|
||||
того, кто платит.
|
||||
|
||||
### 4.2 Пин, ради которого пак существует
|
||||
|
||||
**«Купленное доставлено ⇒ не `exit 4`».** Прогон, где каждый купленный юнит доставлен, а один из них
|
||||
доставлен ФЛАГОВАННЫМ из-за нехватки денег на ретрай, **обязан кончиться `exit 2`** (флаги есть, но прогон
|
||||
завершён), **а не `exit 4`**. ⚠ Не «успехом»: `exit 0` требует, чтобы флагов не было вовсе, а флаг здесь
|
||||
есть по построению — это и есть честный исход.
|
||||
**«ОСТАНОВКА, КОТОРАЯ ЛЕЧИТСЯ ПОПОЛНЕНИЕМ».** Три утверждения, и они про разное:
|
||||
|
||||
⭐ **И заточка, которую дала исполняющая сессия, — она идёт В АКТ:** лечение клина НЕ зависит от того,
|
||||
кончился прогон кодом 2 или 4. Даже когда потолок исчерпан совсем и попытка 0 следующего юнита законно
|
||||
останавливает книгу, у деградированного юнита **всё равно появляется строка** — то есть следствие (1)
|
||||
закрыто и в окне `exit 4`. **Пин ниже стережёт более узкое окно, чем сам выигрыш**, и это надо сказать, а
|
||||
не выдать окно пина за границу лечения.
|
||||
1. **Юнит, на ретрае которого отказал потолок, ПОЛУЧАЕТ СТРОКУ.** Сегодня её нет — это и есть невидимая
|
||||
дыра. Пин обязан печатать величину, доказывающую, что деньги были потрачены, а строка появилась: пустой
|
||||
`chunk_status` рядом с ненулевым `committed` — ровно тот замер, которым дерево описывает прошлый
|
||||
инцидент.
|
||||
2. **Остановка несёт НЕДОСТАЧУ.** `ShortfallMicroUSD` ненулевой и таков, что пополнение на него допускает
|
||||
тот же вызов. ⚠ Пин на «событие вообще есть» вырожден — утверждай ЧИСЛО.
|
||||
3. **Пополнил потолок → резюм ДОДЕЛАЛ юнит.** Два прогона в одном тесте: без пополнения резюм не делает
|
||||
свежих платных вызовов и не уходит в бесконечный цикл; после пополнения — доделывает и отдаёт текст.
|
||||
⛔ Это и есть главный пин пака: сегодня второй прогон умирает вечно.
|
||||
|
||||
⛔ **И назови СУЖЕНИЕ, иначе будешь ловить недостижимое:** утверждение верно в ОКНЕ, когда остатка потолка
|
||||
хватает на попытку 0 каждого следующего юнита. Кончился потолок совсем — попытка 0 следующего юнита
|
||||
обязательна, отказ по ней штатно останавливает книгу, и `exit 4` возвращается ЗАКОННО. Фикстуру строй
|
||||
именно на этой развилке.
|
||||
|
||||
⛔ **ПИН НА РЕЗЮМ — И ЗДЕСЬ ПЕРВАЯ РЕДАКЦИЯ ПРОМТА ЗАПИНИЛА БЫ ОТМЕНУ РАТИФИЦИРОВАННОГО, не заметив.**
|
||||
Я требовал «после такого прогона резюм не делает НИ ОДНОГО свежего платного вызова». Но денежный флаг —
|
||||
единственная причина, которую ЛЕЧАТ ДЕНЬГАМИ: сегодня «поднял потолок → резюм → читатель получил хороший
|
||||
перевод» ратифицировано (`backend/internal/pipeline/escalation.go`, греп `durably pauses and resumes`,
|
||||
`D4`). Мой пин сделал бы юнит навсегда деградированным, лечимым только редрайвом.
|
||||
⇒ **делай РОВНО так: денежная причина ПЕРЕ-АТАКУЕМА на резюме** — как `FlagCancelled`, единственная
|
||||
причина, у которой `resolvedForResume` ложен именно потому, что это «оплачено, но не сделано»
|
||||
(`backend/internal/pipeline/cutcall.go`, греп `resolvedForResume`). Пинь ДВЕ вещи: **без поднятия потолка
|
||||
резюм не делает свежих платных вызовов и не умирает** · **после поднятия потолка резюм ДОДЕЛЫВАЕТ юнит**.
|
||||
⚠ **Про коды выхода.** Прежняя редакция промта требовала `exit 2` вместо `exit 4`. **Снято:** при штатной
|
||||
остановке код остаётся тем, каким он и был для остановки по потолку. Предмет пака — не смена кода выхода, а
|
||||
то, что за ним стоит: **строка вместо пустоты и лечимость пополнением.**
|
||||
|
||||
⚠ **Два гейта поймают тебя сами — назову, чтобы не терять круг:** `TestEveryFlagReasonIsRanked` потребует
|
||||
завести новую причину в `flagSeverity` (`backend/internal/pipeline/status.go`), а
|
||||
завести новую причину в `flagSeverity` (`backend/internal/pipeline/status.go`), если ты её заводишь, а
|
||||
`TestEveryOperatorMessageIsCatalogued` — внести новое сообщение в
|
||||
`backend/internal/pipeline/testdata/operator-messages.txt`.
|
||||
`backend/internal/pipeline/testdata/operator-messages.txt` (сегодня в каталоге 134 строки, из них о потолке
|
||||
уже говорят четыре — посмотри их прежде, чем писать пятую).
|
||||
|
||||
### 4.3 ⛔ ДЕФЕКТ, КОТОРЫЙ ТВОЙ ПАК НАКОРМИТ — и он НЕ латентный. Чинить ЗДЕСЬ
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue