From 340de859168565fadb27e17adb4721fafe869b1e Mon Sep 17 00:00:00 2001 From: "Claude (backend session)" Date: Sat, 25 Jul 2026 17:55:51 +0300 Subject: [PATCH] Land pack-17 delta: third content-policy action allow asserts capability without routing, making the ratified label pair expressible in data (D39.28) --- CLAUDE.md | 6 +- backend/configs/models.yaml | 17 +- backend/configs/pipeline-c1.yaml | 16 +- .../internal/config/content_labels_test.go | 174 +++++++++++++++++- backend/internal/config/pipeline.go | 41 ++++- .../internal/pipeline/content_routing_test.go | 77 ++++++++ backend/internal/pipeline/runner_test.go | 24 ++- ...BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md | 6 +- docs/PROGRESS.md | 10 +- docs/README.md | 6 +- docs/architecture/05-decisions-log.md | 18 +- docs/architecture/09-target-architecture.md | 2 +- .../reports/PACK17_DELTA_2026-07-25.md | 64 +++++++ 13 files changed, 416 insertions(+), 45 deletions(-) create mode 100644 docs/archive/reports/PACK17_DELTA_2026-07-25.md diff --git a/CLAUDE.md b/CLAUDE.md index 457620c0..6e98934e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,7 @@ Go-бэкенд издательского художественного пер ## Источники истины (по убыванию) -1. **`docs/architecture/05-decisions-log.md` (D1–D39.27)** — ратифицированный контракт; при конфликте с любым другим доком побеждает он; сверху файла — карта актуальности (что чем superseded). **Правило чтения (онбординг-диета 25.07): карта + живая голова (эра D39.x); корпус D1–D38 = справочник — по ссылкам/grep по D-номеру, целиком НЕ читать.** +1. **`docs/architecture/05-decisions-log.md` (D1–D39.28)** — ратифицированный контракт; при конфликте с любым другим доком побеждает он; сверху файла — карта актуальности (что чем superseded). **Правило чтения (онбординг-диета 25.07): карта + живая голова (эра D39.x); корпус D1–D38 = справочник — по ссылкам/grep по D-номеру, целиком НЕ читать.** 2. `docs/experiments/00-provider-quirks.md` — wire-квирки провайдеров (читать ПЕРЕД правкой адаптеров/вызовами провайдеров). 3. `docs/architecture/09-target-architecture.md` (7 слоёв; статус стройки — шапка-таблица) · `12-go-style-notes.md` (норматив общности §0) · `10-prompt-architecture.md` (промпт-тема). 4. `docs/architecture/01-decisions.md` (Р1–Р10), `02-mvp-plan.md`, `04-unhappy-paths.md`, `06-memory-risk-registry.md` — тела исторические, читать через ⚠-баннеры 25.07. @@ -39,11 +39,11 @@ Go-бэкенд издательского художественного пер 2. `docs/architecture/05-decisions-log.md`: карта актуальности (шапка) + живая голова (D39.x). Целиком НЕ читать — корпус D1–D38 по ссылкам/grep по мере надобности. 3. По роли: Бэкенд — `backend/README.md` + `03-implementation-notes.md` (через баннер) + `12-go-style-notes.md`; Полигон — `eval/README.md` + `experiments/00` + `09-pilot-protocol.md`; всем — шапка-таблица `09-target-architecture.md` (статус слоёв). -## Текущее состояние (2026-07-25, пост-пак-17 — механизм content-labels построен и залендён, D39.27 — эра «механизация выпускного качества») +## Текущее состояние (2026-07-25, пак-17 ЗАКРЫТ ЦЕЛИКОМ — механизм content-labels живой, D39.26–D39.28 — эра «механизация выпускного качества») - **Фазы:** Ф0 ✅ · Ф1-инфра ✅ (D20–D28; golden = инвариант детерминизма №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ**: 7 слоёв построены паками 11–16 (D39.13–D39.24; статус слоёв — шапка-таблица `09-target-architecture.md`; слой-3 diff-редактор — за `enabled:false`, включение = решение владельца по замеру остатка). - **Пере-прогон rerun2 прочитан (D39.20):** операционка живьём ($0-резюм ×3 арма, $6.63<$15, echo 0, майнинг-стоп→подпись); планка ≤2 НЕ пройдена; анти-корреляция гладкость↔верность ×3 → mistral-редактор вон. **Эксп ЗАКРЫТ (D39.22), итерация №2 отложена; редактор = deepseek-v4-pro ИНТЕРИМ** (glm-5 резерв). Стайл-каноны D39.21 (时辰=2ч · generic-«гу» ср. род · стихи смыслом · 资质=«талант»). -- **Курс — разработка бэкенда:** **пак-17 «канал B вживую» ЗАЛЕНДЕН ЦЕЛИКОМ (D39.26 дизайн + D39.27 стройка); открыт хвост-дельта «assert-only action» под ратифицированный ПАРНЫЙ вокабуляр `violence`/`sexually-explicit`** (**D39.25: в движке НЕТ понятия «18+»** — generic content-labels × provider-capabilities, refusal причино-агностичен; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга; на владельце — вокабуляр лейблов и редактор под лейблом) → следующий платный прогон (разом: итерация №2 dspro-vs-glm · замер остатка ремонта · живой канал B) → сид-дельта (元 · 赤城 · 学堂家老 · 春秋蝉) → Ф2-механизмы (голос/состояние D21 · native-Gemini судья) · ja→ru §B5 → пилот Ф2.5. +- **Курс — разработка бэкенда:** **пак-17 «канал B вживую» ЗАКРЫТ ЦЕЛИКОМ (D39.26 дизайн + D39.27 стройка + D39.28 дельта `allow`); вокабуляр = ПАРА `violence`/`sexually-explicit`, `accepts_labels` ждут ФАКТОВ ToS по первоисточникам (fail-closed — верное состояние)** (**D39.25: в движке НЕТ понятия «18+»** — generic content-labels × provider-capabilities, refusal причино-агностичен; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга; на владельце — вокабуляр лейблов и редактор под лейблом) → следующий платный прогон (разом: итерация №2 dspro-vs-glm · замер остатка ремонта · живой канал B) → сид-дельта (元 · 赤城 · 学堂家老 · 春秋蝉) → Ф2-механизмы (голос/состояние D21 · native-Gemini судья) · ja→ru §B5 → пилот Ф2.5. - **Стек:** draft deepseek-v4-flash thinking-ON → editor deepseek-v4-pro → судья gemini-preview + grok-фолбэк 18+ (~$0.85/ранобэ D30.4). Ключи: DEEPSEEK, ZAI, KIMI, OPENAI, GEMINI, XAI, MISTRAL (xAI: единый, data-sharing, off перед продом D27). - **Висит на владельце + открытые петли:** трекер в CURRENT-STATE `docs/PROGRESS.md` (единственное место). - **Стенд:** WSL2, GTX 1070 8GB (localhost из-под прокси = 403 — no-proxy транспорт для local). Книга `/home/ubuntu/books/gu-zhenren/` (GB18030; текст и производные ВНЕ git). diff --git a/backend/configs/models.yaml b/backend/configs/models.yaml index 6ab3a3d7..d111ffd6 100644 --- a/backend/configs/models.yaml +++ b/backend/configs/models.yaml @@ -72,13 +72,16 @@ providers: # СПОСОБНОСТЬ ПО КОНТЕНТУ (D39.25/D39.26). Флаг `permissive: true` ОТСТАВЛЕН (падает громко): # вместо булева «можно 18+» endpoint объявляет, КАКИЕ лейблы ему разрешено ПОЛУЧАТЬ, и книга # маршрутизируется только в модели, чей набор её лейблы покрывает. Значения — ДАННЫЕ; движок не - # знает ни одного. Пока строка ПУСТА СОЗНАТЕЛЬНО (fail-closed): вокабуляр лейблов — открытое - # решение владельца (D39.26 добор A), потому что единый «adult» схлопывает ратифицированную - # развязку D14 п.1 (ToS DeepSeek запрещает sexually explicit, но violence оставляет допустимым — - # то есть violence-тяжёлая книга под единым лейблом теряет dspro-редактора без нужды). - # Форма ответа = одна строка данных, например: - # accepts_labels: [adult] # единый лейбл - # accepts_labels: [violence, sexually-explicit] # раздельный вокабуляр (сохраняет D14 п.1) + # знает ни одного. ВОКАБУЛЯР РЕШЁН владельцем (D39.27): ПАРА `violence` / `sexually-explicit` — + # она сохраняет ратифицированную развязку D14 п.1 (ToS DeepSeek запрещает sexually explicit, но + # violence оставляет допустимым), то есть violence-книга не теряет dspro-редактора. + # Строка ПУСТА СОЗНАТЕЛЬНО и это ВЕРНОЕ состояние (fail-closed): значения — ФАКТЫ ToS/AUP, а они + # (а) добываются только по ПЕРВОИСТОЧНИКАМ (правило двух направлений, `experiments/00-provider-quirks.md`), + # (б) даются ПЕР-ЛЕЙБЛУ, а не пер-провайдеру целиком: violence и sexually-explicit — РАЗНЫЕ ответы у + # одного и того же вендора, и (в) фиксируются полигоном, не бэкенд-сессией. По памяти не заполнять. + # Форма, когда факты будут (одна строка на провайдера, только подтверждённые лейблы): + # accepts_labels: [violence] # эндпойнт вправе получать только violence + # accepts_labels: [violence, sexually-explicit] # подтверждены оба # Эмпирика этого endpoint: главный пермиссив-тир канала B + reasoning-ON эскалация (D3/D19.1), # первичный судья эротики (D22.6). timeouts: { attempt_s: 240, max_attempts: 3, backoff_cap_s: 60 } diff --git a/backend/configs/pipeline-c1.yaml b/backend/configs/pipeline-c1.yaml index 5b1c77d1..da92942e 100644 --- a/backend/configs/pipeline-c1.yaml +++ b/backend/configs/pipeline-c1.yaml @@ -120,16 +120,22 @@ escalation: # движок не знает ни одного значения, а вокабуляр 18+ ждёт решения владельца, D39.26 добор A): # # content_policy: -# - { label: <лейбл-книги>, action: route, chain: <имя-цепочки> } # маршрут: label_models + один хоп +# - { label: <лейбл-1>, action: allow } # только АССЕРТ: маршрут не меняется +# - { label: <лейбл-2>, action: route, chain: <имя-цепочки> } # маршрут: label_models + один хоп # # action: terminal — книга вообще не обрабатывается (отказ ВХОДА по объявленным данным; это НЕ скрин # # контента: ратифицированный пер-чанковый L3-скрин D22.7 остаётся отдельным предусловием прода). # stages: # - name: draft -# label_models: { <лейбл-книги>: <модель-с-accepts_labels> } +# label_models: { <лейбл-2>: <модель-с-accepts_labels> } # только для route-лейбла # -# Требования загрузки для route-лейбла: цепочка существует и содержит РОВНО один член · -# escalation.budget_usd > 0 · каждая достижимая модель несёт все лейблы книги (иначе загрузка translate -# падает, называя модель, провайдера и недостающий лейбл; read-only проекции остаются доступны). +# ВСЕ ТРИ действия несут ОДИН инвариант: каждая достижимая модель обязана принимать все лейблы книги +# (иначе загрузка translate падает, называя модель, провайдера и недостающий лейбл; read-only проекции +# остаются доступны). Различаются они остальным: +# • allow — эндпойнты уже верные, ничего не двигаем; chain/label_models/бюджет НЕ нужны и ЗАПРЕЩЕНЫ; +# • route — цепочка существует и содержит РОВНО один член · escalation.budget_usd > 0 · +# хотя бы одна стадия объявляет label_models для этого лейбла; +# • terminal — ни одного вызова. +# Пара «один лейбл ассертит, второй пере-маршрутизирует» выражается ДАННЫМИ — Go не трогается. fanout: candidates: 1 # C1: один черновик diff --git a/backend/internal/config/content_labels_test.go b/backend/internal/config/content_labels_test.go index 3bad5229..d8813914 100644 --- a/backend/internal/config/content_labels_test.go +++ b/backend/internal/config/content_labels_test.go @@ -188,10 +188,15 @@ func TestContentRoutingRefusals(t *testing.T) { want: "no hop can ever fire", }, { + // A terminal policy takes no per-stage routes either (they would be meaningless), so the fixture + // drops them together with the chain — that pairing is itself checked by the shape test below. name: "terminal policy refuses the book", - pipeline: strings.Replace(basePipeline, + pipeline: strings.NewReplacer( " - { label: restricted-synth, action: route, chain: restricted }", - " - { label: restricted-synth, action: terminal }", 1), + " - { label: restricted-synth, action: terminal }", + " label_models: { restricted-synth: capable-model }\n", "", + " label_models: { restricted-synth: second-model }\n", "", + ).Replace(basePipeline), labels: []string{"restricted-synth"}, want: "action=terminal", }, @@ -252,7 +257,7 @@ func TestContentPolicyShapeIsFatal(t *testing.T) { want string }{ {"multi-member chain", strings.Replace(basePipeline, "restricted: [second-model]", "restricted: [second-model, capable-hop]", 1), "MULTI-HOP IS NOT BUILT"}, - {"unknown action", strings.Replace(basePipeline, "action: route, chain: restricted", "action: quarantine, chain: restricted", 1), "action must be route|terminal"}, + {"unknown action", strings.Replace(basePipeline, "action: route, chain: restricted", "action: quarantine, chain: restricted", 1), "action must be route|allow|terminal"}, {"route without chain", strings.Replace(basePipeline, "action: route, chain: restricted", "action: route", 1), "requires `chain:`"}, {"chain not defined", strings.Replace(basePipeline, "action: route, chain: restricted", "action: route, chain: nosuch", 1), "not defined in escalation.chains"}, {"duplicate registry label", strings.Replace(basePipeline, @@ -687,3 +692,166 @@ models: }) } } + +// TestAllowActionAssertsWithoutRouting covers the third policy shape: `allow` applies the capability +// invariant and changes NO route. It is the shape a label needs when the providers are already correct — +// with only route/terminal such a label was inexpressible (route demands a chain, a budget and a +// per-stage model it has no use for; terminal refuses the book), which is why a two-label vocabulary +// where one label re-routes and the other only asserts could not be written as DATA at all. +func TestAllowActionAssertsWithoutRouting(t *testing.T) { + // An allow-only registry: no chain, no budget, no label_models anywhere. + allowOnly := `core: C1 +version: 1 +defaults: { max_output_ratio: 2, min_max_tokens: 512 } +content_policy: + - { label: restricted-synth, action: allow } +stages: + - name: draft + role: translator + model: capable-model + prompt_override: @PROMPT@ + prompt_version: v + escalate_to: capable-hop + - name: edit + role: editor + model: second-model + prompt_override: @PROMPT@ + prompt_version: v +` + _, load := labelFixture(t, allowOnly) + p, err := load([]string{"restricted-synth"}) + if err != nil { + t.Fatalf("an allow-only policy must need no chain and no budget: %v", err) + } + if err := p.ContentRoutingError(); err != nil { + t.Fatalf("every reachable model accepts the label, so this must be runnable: %v", err) + } + // The route is untouched — that is the whole semantic. + for _, st := range p.Stages { + if st.ResolvedModel != st.Model || st.ResolvedHop != st.EscalateTo { + t.Errorf("stage %q must keep its configured route under action=allow: (%q,%q) vs (%q,%q)", + st.Name, st.ResolvedModel, st.ResolvedHop, st.Model, st.EscalateTo) + } + } + // …and the INVARIANT still bites: an endpoint that may not receive the label is refused, which is the + // only thing an allow policy does. + incapable := strings.Replace(allowOnly, "model: capable-model", "model: plain-model", 1) + _, loadBad := labelFixture(t, incapable) + pb, err := loadBad([]string{"restricted-synth"}) + if err != nil { + t.Fatalf("load: %v", err) + } + rerr := pb.ContentRoutingError() + if rerr == nil || !strings.Contains(rerr.Error(), "may not receive content label") { + t.Fatalf("action=allow must still refuse an incapable endpoint — the invariant IS the policy, got %v", rerr) + } +} + +// TestAllowActionRejectsRoutingKnobs pins that an allow policy refuses the knobs it cannot honour, rather +// than ignoring them: an author who wrote a chain or a per-stage model meant something the engine will +// not do, and silently dropping it is how a config comes to lie about its own routing. +func TestAllowActionRejectsRoutingKnobs(t *testing.T) { + base := `core: C1 +version: 1 +defaults: { max_output_ratio: 2, min_max_tokens: 512 } +content_policy: + - { label: restricted-synth, action: allow%s } +stages: + - name: draft + role: translator + model: capable-model + prompt_override: @PROMPT@ + prompt_version: v + escalate_to: capable-hop%s +escalation: + chains: { restricted: [second-model] } + budget_usd: 1.0 +` + cases := []struct{ name, policyTail, stageTail, want string }{ + {"chain on an allow policy", ", chain: restricted", "", "takes no chain"}, + {"label_models for an allow label", "", "\n label_models: { restricted-synth: second-model }", "is meaningless"}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + _, load := labelFixture(t, fmt.Sprintf(base, c.policyTail, c.stageTail)) + if _, err := load(nil); err == nil || !strings.Contains(err.Error(), c.want) { + t.Fatalf("want a load error mentioning %q, got %v", c.want, err) + } + }) + } +} + +// TestRatifiedLabelPairIsDataOnly is the delta's acceptance shape, written as the owner ratified it: TWO +// labels, one that only ASSERTS (its providers are already correct, so no model moves) and one that +// RE-ROUTES (its endpoints differ). Both values here are synthetic — the point is that the SHAPE needs no +// Go change, so whichever vocabulary the data ends up carrying, the mechanism already holds it. +func TestRatifiedLabelPairIsDataOnly(t *testing.T) { + pair := `core: C1 +version: 1 +defaults: { max_output_ratio: 2, min_max_tokens: 512 } +content_policy: + - { label: restricted-synth, action: allow } + - { label: second-synth, action: route, chain: rerouted } +stages: + - name: draft + role: translator + model: capable-model + prompt_override: @PROMPT@ + prompt_version: v + escalate_to: capable-hop + label_models: { second-synth: narrowed-model } + - name: edit + role: editor + model: capable-model + prompt_override: @PROMPT@ + prompt_version: v +escalation: + chains: { rerouted: [second-model] } + budget_usd: 1.0 +` + _, load := labelFixture(t, pair) + + // (a) the asserting label alone: nothing moves, no chain/budget needed by it. + only, err := load([]string{"restricted-synth"}) + if err != nil { + t.Fatalf("load: %v", err) + } + if err := only.ContentRoutingError(); err != nil { + t.Fatalf("restricted-synth must be runnable on capable endpoints: %v", err) + } + if only.Stages[0].ResolvedModel != "capable-model" || only.Stages[0].ResolvedHop != "capable-hop" { + t.Errorf("the asserting label must not move the route, got (%q,%q)", only.Stages[0].ResolvedModel, only.Stages[0].ResolvedHop) + } + + // (b) BOTH labels: the re-routing one moves the draft leg, and the invariant is applied to the union — + // narrowed-model accepts only one of the two synthetic labels, so it must be refused here. + both, err := load([]string{"restricted-synth", "second-synth"}) + if err != nil { + t.Fatalf("load: %v", err) + } + rerr := both.ContentRoutingError() + if rerr == nil || !strings.Contains(rerr.Error(), "narrowed-model") { + t.Fatalf("the invariant must be applied to the UNION of the book's labels (narrowed-model accepts only one), got %v", rerr) + } + if both.Stages[0].ResolvedModel != "narrowed-model" { + t.Errorf("the re-routing label must still resolve its stage, got %q", both.Stages[0].ResolvedModel) + } + + // (c) the pair on endpoints that accept BOTH: runnable, one leg re-routed, the other untouched. + ok := strings.Replace(pair, "label_models: { second-synth: narrowed-model }", "label_models: { second-synth: second-model }", 1) + ok = strings.Replace(ok, "chains: { rerouted: [second-model] }", "chains: { rerouted: [capable-hop] }", 1) + _, loadOK := labelFixture(t, ok) + p, err := loadOK([]string{"restricted-synth", "second-synth"}) + if err != nil { + t.Fatalf("load: %v", err) + } + if err := p.ContentRoutingError(); err != nil { + t.Fatalf("the ratified PAIR shape must be runnable on capable endpoints with zero Go changes: %v", err) + } + if p.Stages[0].ResolvedModel != "second-model" || p.Stages[0].ResolvedHop != "capable-hop" { + t.Errorf("draft resolved (%q,%q), want (second-model,capable-hop)", p.Stages[0].ResolvedModel, p.Stages[0].ResolvedHop) + } + if p.Stages[1].ResolvedModel != "capable-model" { + t.Errorf("the edit stage carries no route for either label, so it stays configured, got %q", p.Stages[1].ResolvedModel) + } +} diff --git a/backend/internal/config/pipeline.go b/backend/internal/config/pipeline.go index ea9e0e62..8a77b3c9 100644 --- a/backend/internal/config/pipeline.go +++ b/backend/internal/config/pipeline.go @@ -67,9 +67,21 @@ type LabelPolicy struct { } const ( - // LabelActionRoute / LabelActionTerminal are the two policy SHAPES the engine implements. They are - // actions, not content classes — no label value appears here. + // The three policy SHAPES the engine implements. They are actions, not content classes — no label + // value appears here, and ALL THREE carry the capability invariant (every model a labelled run can + // call must accept every label of the book); they differ only in what else they do. + // + // route — re-route the claimed stages to their label_models and take the single hop from `chain`. + // allow — process on capable endpoints ONLY, changing no route: the invariant is the whole policy. + // terminal — do not process the book at all (an entry refusal by declared data, not a screen). + // + // `allow` exists because the invariant is the point of a label that needs no different model: with only + // route/terminal a label whose providers are already correct was inexpressible — route demanded a + // chain, a budget and a per-stage model it has no use for, and terminal refuses the book outright. A + // pair of labels where one re-routes and the other only asserts is exactly the ratified vocabulary + // shape, so without this value the mechanism failed its own test ("a second label is added by DATA"). LabelActionRoute = "route" + LabelActionAllow = "allow" LabelActionTerminal = "terminal" ) @@ -186,11 +198,13 @@ type Stage struct { PromptVersion string `yaml:"prompt_version"` Temperature float64 `yaml:"temperature"` Reasoning string `yaml:"reasoning"` // "", off, low, medium, high - // ReasoningMaxTokens is the explicit reasoning-token BUFFER reserved for this stage - // when it thinks (reasoning low|medium|high) on an ADDITIVE-billing provider (xAI — - // reasoning bills on top of completion, D13.6). Required (>0) in exactly that case - // (fail-fast in LoadPipeline); ignored for subset providers / reasoning-off. It only - // sizes the reservation (EstimateUSD), so it is NOT part of the snapshot/wire. + // ReasoningMaxTokens is the explicit reasoning-token BUFFER reserved for this stage on an + // ADDITIVE-billing provider (xAI — reasoning bills on top of completion, D13.6). Required (>0) + // whenever the stage's RESOLVED model sits on such a provider — INCLUDING at reasoning:"off" + // (D39.26 добор B, form 2): "off" suppresses thinking only where the capability carries an + // off-switch, so exempting it was a silent exit that left the ceiling blind. Ignored for subset + // providers. A call that does suppress thinking merely over-reserves — the ceiling tightens, it never + // goes blind. It only sizes the reservation (EstimateUSD), so it is NOT part of the snapshot/wire. ReasoningMaxTokens int `yaml:"reasoning_max_tokens"` // EscalateTo is the SINGLE-HOP fallback model tried ONCE when this stage's // output is a deterministic content-failure another model might fix (echo / @@ -448,12 +462,18 @@ func (p *Pipeline) resolveContentRouting(models *Models, labels []string, bad fu case len(chain) > 1: bad("content_policy[%d] (%s): chain %q has %d members — MULTI-HOP IS NOT BUILT (one hop = chain[0], D39.26 point 3). Shorten the chain to its head, or keep the extra members in a chain no policy references", i, pol.Label, pol.Chain, len(chain)) } + case LabelActionAllow: + // Nothing is re-routed, so a chain would be a fallback nobody can reach: refused rather than + // ignored, because an author who wrote one meant something the engine will not do. + if pol.Chain != "" { + bad("content_policy[%d] (%s): action=%s takes no chain — it changes no route, so there is no hop to take. Use action=%s if this label must re-route, or drop the chain", i, pol.Label, LabelActionAllow, LabelActionRoute) + } case LabelActionTerminal: if pol.Chain != "" { bad("content_policy[%d] (%s): action=terminal takes no chain (nothing is ever called for such a book)", i, pol.Label) } default: - bad("content_policy[%d] (%s): action must be %s|%s, got %q", i, pol.Label, LabelActionRoute, LabelActionTerminal, pol.Action) + bad("content_policy[%d] (%s): action must be %s|%s|%s, got %q", i, pol.Label, LabelActionRoute, LabelActionAllow, LabelActionTerminal, pol.Action) } } // label_models may only name models that exist, whether or not this book activates them: a typo in @@ -469,8 +489,11 @@ func (p *Pipeline) resolveContentRouting(models *Models, labels []string, bad fu if _, ok := models.Models[model]; !ok { bad("stage %q: label_models[%q] model %q is not defined in models.yaml", st.Name, label, model) } - if p.policyFor(label) == nil { + switch pol := p.policyFor(label); { + case pol == nil: bad("stage %q: label_models[%q] has no content_policy entry — a route the registry never mentions cannot be taken", st.Name, label) + case pol.Action != LabelActionRoute: + bad("stage %q: label_models[%q] is meaningless — that label's policy is action=%s, which changes no route (only action=%s re-routes stages). Drop the entry, or change the policy", st.Name, label, pol.Action, LabelActionRoute) } } } diff --git a/backend/internal/pipeline/content_routing_test.go b/backend/internal/pipeline/content_routing_test.go index f57242eb..0ba469c8 100644 --- a/backend/internal/pipeline/content_routing_test.go +++ b/backend/internal/pipeline/content_routing_test.go @@ -572,3 +572,80 @@ func TestEditorOnlyLabelLeavesTheDraftWaveUntouched(t *testing.T) { t.Errorf("the EDIT-wave snapshot must move (%.12s) — the editor is called on another model, so its checkpoints are not the same calls", labelledEdit) } } + +// TestAssertOnlyLabelRunsWithoutMovingAnything drives the third policy shape through the REAL driver: a +// book whose label only ASSERTS must run exactly like the unlabelled one — same wire, same snapshots, +// same money — while the capability invariant still guards every call. It needs no escalation budget and +// no chain, which is the whole reason the shape exists. +func TestAssertOnlyLabelRunsWithoutMovingAnything(t *testing.T) { + rec := &reqRec{} + srv := newJSONProvider(rec, draftEdit) + defer srv.Close() + + plain := newRunner(t, setupEscalationProject(t, srv.URL, 0, nil)) + plainDraft, _, err := plain.snapshotIDForWave(waveDraft) + if err != nil { + t.Fatal(err) + } + plainEdit, _, err := plain.snapshotIDForWave(waveEdit) + if err != nil { + t.Fatal(err) + } + plainRes, err := plain.TranslateBook(context.Background()) + if err != nil { + t.Fatalf("unlabelled translate: %v", err) + } + plainBodies := append([]string(nil), rec.all()...) + plain.Close() + + // Same project, plus a label whose policy only asserts (no chain, no label_models, budget 0). + rec2 := &reqRec{} + srv2 := newJSONProvider(rec2, draftEdit) + defer srv2.Close() + r := newRunner(t, setupEscalationProject(t, srv2.URL, 0, &labelRouting{ + acceptsLabels: []string{synthLabel}, + bookLabels: []string{synthLabel}, + policy: synthLabel, + action: "allow", + })) + defer r.Close() + res, err := r.TranslateBook(context.Background()) + if err != nil { + t.Fatalf("an asserting label must not disturb the run: %v", err) + } + if res.Chunks[0].Disposition != DispOK || res.TotalUSD != plainRes.TotalUSD { + t.Errorf("run diverged: disposition=%s cost=%v want ok/%v", res.Chunks[0].Disposition, res.TotalUSD, plainRes.TotalUSD) + } + // The wire must be identical body for body — an assertion is not a wire change. + bodies := rec2.all() + if len(bodies) != len(plainBodies) { + t.Fatalf("call count changed: %d vs %d", len(bodies), len(plainBodies)) + } + for i := range bodies { + if bodies[i] != plainBodies[i] { + t.Errorf("call %d body differs under an asserting label:\n got %s\nwant %s", i, bodies[i], plainBodies[i]) + } + } + // …and so must both snapshots: an asserting label moves NO route, so it can re-bill nothing. + draft, _, err := r.snapshotIDForWave(waveDraft) + if err != nil { + t.Fatal(err) + } + edit, _, err := r.snapshotIDForWave(waveEdit) + if err != nil { + t.Fatal(err) + } + if draft != plainDraft || edit != plainEdit { + t.Errorf("an asserting label moved a snapshot (draft %.12s→%.12s, edit %.12s→%.12s) — it changes no model, so it must re-bill nothing", + plainDraft, draft, plainEdit, edit) + } + // The invariant is still the point: strip the capability and the SAME book must refuse to run. + incapable := setupEscalationProject(t, srv2.URL, 0, &labelRouting{ + bookLabels: []string{synthLabel}, + policy: synthLabel, + action: "allow", + }) + if _, err := NewRunner(incapable, obs.NewLogger()); err == nil || !strings.Contains(err.Error(), "may not receive content label") { + t.Fatalf("an asserting label must still refuse incapable endpoints, got %v", err) + } +} diff --git a/backend/internal/pipeline/runner_test.go b/backend/internal/pipeline/runner_test.go index e7fec1f9..3b014cea 100644 --- a/backend/internal/pipeline/runner_test.go +++ b/backend/internal/pipeline/runner_test.go @@ -1080,7 +1080,8 @@ func TestRunnerRejectsCoverageGateWithoutThresholds(t *testing.T) { type labelRouting struct { acceptsLabels []string // provider accepts_labels bookLabels []string // book content_labels - policy string // the label the registry routes (empty = no registry entry written) + policy string // the label the registry declares (empty = no registry entry written) + action string // the policy action ("" = route; "allow" asserts without re-routing) labelModel string // the draft stage's label_models target for `policy` editModel string // the EDIT stage's label_models target (the editor-only routing case) chainModel string // the head of the label's chain (its single hop) @@ -1125,16 +1126,25 @@ models: labelModelsLine, editLabelModelsLine, policyBlock := "", "", "" if lbl != nil && lbl.policy != "" { - if lbl.labelModel != "" { - labelModelsLine = fmt.Sprintf(", label_models: { %s: %s }", lbl.policy, lbl.labelModel) + action := lbl.action + if action == "" { + action = "route" } - if lbl.editModel != "" { - editLabelModelsLine = fmt.Sprintf(", label_models: { %s: %s }", lbl.policy, lbl.editModel) + if action == "route" { + if lbl.labelModel != "" { + labelModelsLine = fmt.Sprintf(", label_models: { %s: %s }", lbl.policy, lbl.labelModel) + } + if lbl.editModel != "" { + editLabelModelsLine = fmt.Sprintf(", label_models: { %s: %s }", lbl.policy, lbl.editModel) + } + policyBlock = fmt.Sprintf("content_policy:\n - { label: %s, action: route, chain: labelled }\n", lbl.policy) + } else { + // An asserting policy takes no chain and no per-stage route — writing either is a load error. + policyBlock = fmt.Sprintf("content_policy:\n - { label: %s, action: %s }\n", lbl.policy, action) } - policyBlock = fmt.Sprintf("content_policy:\n - { label: %s, action: route, chain: labelled }\n", lbl.policy) } chainBlock := "" - if lbl != nil && lbl.chainModel != "" { + if lbl != nil && lbl.chainModel != "" && (lbl.action == "" || lbl.action == "route") { chainBlock = fmt.Sprintf(" chains: { labelled: [%s] }\n", lbl.chainModel) } writeFile(t, filepath.Join(dir, "pipeline.yaml"), fmt.Sprintf(` diff --git a/docs/BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md b/docs/BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md index a0da18dc..b662fda9 100644 --- a/docs/BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md +++ b/docs/BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md @@ -1,6 +1,6 @@ # Промт бэкенд-сессии: ПАК-17 «канал B (18+) вживую» (ДВУХФАЗНЫЙ: дизайн → ратификация → стройка) -**Статус: ФАЗА 1 ИСПОЛНЕНА И РАТИФИЦИРОВАНА (`D39.26`, 25.07) → ФАЗА 2 РАЗМЕЧЕНА И ОТКРЫТА.** Дизайн-отчёт `docs/archive/reports/PACK17_DESIGN_2026-07-25.md` принят **В РЕДАКЦИИ §14** (тело §0–13 читать ТОЛЬКО через §14; при расхождении побеждает §14) + ревью-шапка оркестратора. **Фаза 2 стартует по §«ФАЗА 2» ниже** — там ратифицированный «ровно так»-список; §«ФАЗА 1» оставлена как история задания. +**Статус: ✅ ПАК ОТРАБОТАН ЦЕЛИКОМ И ЗАКРЫТ (дизайн `D39.26` · стройка `D39.27` · дельта `D39.28`, 25.07). ПРОМТ АРХИВНЫЙ — исполнять из него нечего.** Отчёты с ревью-шапками: `docs/archive/reports/PACK17_{DESIGN,CHANNEL_B_REPORT,DELTA}_2026-07-25.md` (дизайн принят **В РЕДАКЦИИ §14**). Остаток пака — не код: ФАКТЫ ToS под `accepts_labels` (зона полигона, `experiments/00-provider-quirks.md`) · Q2 редактор и D22.7 перед первой explicit-книгой. Всё ниже — история задания. **Статус фазы 1: ВЫДАН 25.07.2026, оркестратор №7.** Цель: 18+ контент (уровни 1–2) идёт через волновой стек по adult-каналу — permissive-провайдеры, refusal-обработка, механические гарантии маршрутизации. Ратифицированная база: D14 (состав канала B) · D19 (Mistral = переводчик канала B; register-оговорка эха D19.1; судьи 18+ п.6: Gemini fail-closed на эротике, Grok первичный) · D22 (развязки 18+/эха; L3-спека D22.7) · D4 (adult-цепочка = permissive-провайдеры; уже в конфигах: `escalation.chains.adult: [grok-4.3]`). Эмпирика полигона: `experiments/10-explicit-benchmark` (violence) · `11-erotica-benchmark`. @@ -33,7 +33,9 @@ ## ✅ ФАЗА 2 ИСПОЛНЕНА, ПРИНЯТА И ЗАЛЕНДЕНА (`D39.27`, 25.07) — ниже история задания; АКТИВНА ТОЛЬКО «ДЕЛЬТА» в конце файла -## ДЕЛЬТА ПОСЛЕ ПРИЁМКИ (ответ владельца по вокабуляру — единственная открытая работа пака-17) +## ✅ ДЕЛЬТА ИСПОЛНЕНА И ПРИНЯТА (`D39.28`, 25.07) — ПАК-17 ЗАКРЫТ ЦЕЛИКОМ; промт архивный, исполнять из него нечего + +## ДЕЛЬТА ПОСЛЕ ПРИЁМКИ (история задания) **Владелец выбрал ПАРУ лейблов `violence` / `sexually-explicit`** (сохраняет ратифицированную развязку D14 п.1: DeepSeek запрещён только на sexually explicit, на violence допустим ⇒ violence-книга не теряет dspro-редактора). Из этого — одна обязательная правка механизма и одна операционная: diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 8ddfc68f..301fc9ec 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,11 +1,15 @@ # Журнал прогресса -> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-07-25, пост-пак-17 фазы 2 — механизм content-labels ЖИВОЙ, D39.27). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1–D39.27); этот файл — ЖУРНАЛ.** -> - **Фазы/курс:** Ф0 ✅ · Ф1-инфра ✅ (D20–D28; golden = инвариант №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ** (7 слоёв → программа D39.1–D39.10 → паки 11–16, D39.13–D39.24; статус слоёв — шапка-таблица `architecture/09-target-architecture.md`) · пере-прогон rerun2 прочитан (D39.20: операционка живьём, $0-резюм ×3 арма, $6.63<$15; планка ≤2 НЕ пройдена; анти-корреляция гладкость↔верность ×3 → mistral-редактор вон) · эксп ЗАКРЫТ (D39.22), итерация №2 отложена · **курс = разработка бэкенда: пак-17 «канал B вживую» ЗАЛЕНДЕН ЦЕЛИКОМ (фаза 1 — D39.26, фаза 2 — D39.27, 25.07); открыт хвост-дельта «assert-only action» под ратифицированный ПАРНЫЙ вокабуляр `violence`/`sexually-explicit`** (D39.25: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга) → следующий платный прогон (разом: итерация №2 dspro-vs-glm · замер остатка ремонта → решение о петле D39.24 · живой канал B) → сид-дельта (元 · 赤城 · 学堂家老 · 春秋蝉) → Ф2-механизмы (голос/состояние D21 · native-Gemini судья) · ja→ru §B5 → пилот Ф2.5. Хроника треков A/B, exp15/16 и стройки паков — архивы ниже + D-лог. +> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-07-25, пак-17 ЗАКРЫТ ЦЕЛИКОМ — механизм content-labels живой, D39.26–D39.28). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1–D39.28); этот файл — ЖУРНАЛ.** +> - **Фазы/курс:** Ф0 ✅ · Ф1-инфра ✅ (D20–D28; golden = инвариант №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ** (7 слоёв → программа D39.1–D39.10 → паки 11–16, D39.13–D39.24; статус слоёв — шапка-таблица `architecture/09-target-architecture.md`) · пере-прогон rerun2 прочитан (D39.20: операционка живьём, $0-резюм ×3 арма, $6.63<$15; планка ≤2 НЕ пройдена; анти-корреляция гладкость↔верность ×3 → mistral-редактор вон) · эксп ЗАКРЫТ (D39.22), итерация №2 отложена · **курс = разработка бэкенда: пак-17 «канал B вживую» ЗАКРЫТ ЦЕЛИКОМ (дизайн D39.26 · стройка D39.27 · дельта `allow` D39.28, 25.07); вокабуляр = ПАРА `violence`/`sexually-explicit`, остаток — ФАКТЫ ToS в зоне полигона (fail-closed до них)** (D39.25: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга) → следующий платный прогон (разом: итерация №2 dspro-vs-glm · замер остатка ремонта → решение о петле D39.24 · живой канал B) → сид-дельта (元 · 赤城 · 学堂家老 · 春秋蝉) → Ф2-механизмы (голос/состояние D21 · native-Gemini судья) · ja→ru §B5 → пилот Ф2.5. Хроника треков A/B, exp15/16 и стройки паков — архивы ниже + D-лог. > - **Стек:** draft deepseek-v4-flash (thinking ON) → **editor deepseek-v4-pro БИЛИНГВ ИНТЕРИМ (D39.22; glm-5 резерв, mistral вон D39.20)**, промпт v3-discourse (P1a+чэнъюй+few_shot-тумблер) → судья gemini-3.1-pro-preview; канал B Mistral+grok; 7 ключей; ~$0.85/ранобэ (D30.4). -> - **ОТКРЫТЫЕ ПЕТЛИ (норма 25.07, перепись 180 инцидентов: класс NEVER_CLOSED больше не копим — каждая петля обязана получить диспозицию решено/отложено-с-записью/отклонено; ведёт оркестратор).** *Владелец:* ~~вокабуляр content-лейблов~~ **РЕШЕНО 25.07: ПАРА `violence`/`sexually-explicit`** (D39.27 п.5 — сохраняет D14 п.1 и dspro-редактора на violence) ⇒ следствие в работе: assert-only `action` (дельта пака-17) · ~~Q2 редактор под лейблом~~ **ОТЛОЖЕН С ЗАПИСЬЮ** (D39.27 п.7: при паре связывает только `sexually-explicit`-книги, которых нет; возвращается вместе с предусловием D22.7 перед первой explicit-книгой; кандидаты — grok-4.3 интерим, glm-5 только после сверки ToS Z.AI по первоисточнику, mistral отвергнут D39.20) · **ToS-факты под `accepts_labels` — пер-провайдер И пер-лейбл, по первоисточникам (НОВОЕ, зона фиксации — `experiments/00-provider-quirks.md`, полигон); до заполнения механизм fail-closed — верное состояние** · **промпт-слой под лейблом (НОВОЕ, вскрыто вопросом владельца): лейбл сегодня НЕ доезжает до промпта** (чистые данные маршрутизации + load-time гарантия); нужно ли, чтобы переводчик/редактор ЗНАЛИ о свойстве контента — отдельное решение промпт-слоя · мини-каноны сид-дельты — 元-корень (предложение «изначальн-») и 春秋蝉 (подписано «Весенне-осенняя цикада», ждёт подтверждения) · **включение платной петли ремонта — по замеру остатка на пост-профилактическом прогоне (D39.24; заменил mini-live гейт D6)** · ja→ru-реплика §B5 (тайминг) · старые Ф2.5-блокеры (билингв-якорь — D25 п.9 Q1 · контаминация корпуса — D27 п.4 · судья-дублёр D22.6 · планка запуска · publishable/waiver — D25 п.1 · FN-bound L3 — D25 п.4 · юр-пакет · провенанс 12-*-доков · xAI-ключ off перед продом D27 · residual-тачпойнты exp16, не влитые в сид-дельту: мини-голд алиасов · precision@30, `books/gu-zhenren/exp16/`). *Оркестратор:* ~~ратификация дизайна пака-16~~ РЕШЕНО D39.24 (25.07) · фантом-гард D-ссылок в чек-листе лендинга (действует) · ~~doc-sync pass~~ **ИСПОЛНЕН 25.07** (запись ниже: оба puml перерисованы под пост-пак-16 · баннеры на 01/02/03/04/06/07/08/09 · ресёрч-свип 18/18c/19/20/21/22 · README/POLYGON_PACKAGE4 освежены; residual — пинги полигону в его зоне) · ~~приёмка пака-17 фаза 1~~ **РЕШЕНО D39.26 (25.07: принято в редакции §14, три добора приёмки, фаза 2 размечена)** · **`escalation_model` = ОТВЕТИВШАЯ модель (НОВОЕ, вынесено из пака-17): вердикт-изменение** — golden пинит `esc_model` на ОТКАЗАВШЕМ хопе, маскировка не скрывает ⇒ отдельная ратификация с санкционированным пере-капчером · **`--accept-rebill[=usd]` с порогом `min($0.50, 5%×ProjectedBookUSD)` РАТИФИЦИРОВАН D20.2-Q2, но в коде/CLI ОТСУТСТВУЕТ (НОВОЕ)** — живёт только в спеке `backend/docs/D15.2-*.md`; `--resnapshot` пере-пиннит без суммы · **D22.7 (пер-чанковый L3-скрин) — предусловие первой erotica-книги в проде НЕ снято паком-17 (НОВОЕ, держать отдельным гейтом)** · фантом-гард D-ссылок в чек-листе лендинга (действует). *Бэкенд:* ~~пак-17 фаза 2~~ **ПРИНЯТА И ЗАЛЕНДЕНА 25.07 (D39.27)**; открыт хвост — **дельта «assert-only action»** (§«ДЕЛЬТА» промта пака-17) + фикс-лист (стейл-коммент `Stage.ReasoningMaxTokens` `pipeline.go:189-194` после формы 2) · ~~пак-16 ф.2~~ ЗАЛЕНДЕНА 25.07 · per-class исходы ремонта — схемное решение (класс не durable), принимается ВМЕСТЕ с включением петли · ниты общности (владелец 25.07) — **едут хвостом пака-17 фазы 2 УСЛОВНО**: только в байт-нейтральной форме с доказательством исполнением (иначе `CheapGateVersion`/`pack.Version()` двигают снапшот стендовых книг и убивают $0-резюм драфта под замер остатка пака-16): Detail-строки чекеров → шаблоны/цитаты из данных (статический 时辰-текст в generic-файле соврёт второй паре) · множитель ×2 `lintTimeUnits` (`checkers.go:174`) → ключ данных. *Полигон (residual):* **6 пингов doc-sync аудита 25.07 (зона `docs/experiments/` — оркестратор не правит):** (1) `00-provider-quirks.md:13,73` — катовер deepseek-chat 24.07 ПРОШЁЛ, пост-катовер факт (жив ли алиас, цены) не внесён — live-проба `/models` + вендор-дока; (2) `00-provider-quirks.md:69` — оговорка «mistral-editor-арм до guard» superseded D39.20 (mistral вон из редакторов; rate-limit-цифры оставить — нужны переводчику канала B пака-17); (3) `09-pilot-protocol.md:3` — ⚠-указатель застрял на D31/glm-5: продлить до пост-D39.22 (dspro-интерим · планка ≤2 не пройдена rerun2 · авто-QA слепыми метками D39.20 · стайл-каноны D39.21), тело не трогать; (4) `15-segmentation-empirics.md:6-7` — шапка до сих пор «НИ ОДНОГО платного вызова», а эксп залендён REV.2 (D39.7/D39.8, $12.79) — перевернуть статус-блок; (5) `16-bank-mining.md:3` — дописать финал «ИСПОЛНЕН И ЗАЛЕНДЁН (D39.10); gender-тачпойнт §7 закрыт (D39.19); продолжение — Go-майнер парити EXACT»; (6) `experiments/README.md` — индекс без строк exp15/16 (сверка застряла на D38.2); (7) `eval/README.md` — не тронут с 19.07 (пре-rerun2): освежить статус под пост-D39.22 (эксп закрыт · судейский риг-стандарт D39.7 · экстракция только `tmctl export`). +> - **ОТКРЫТЫЕ ПЕТЛИ (норма 25.07, перепись 180 инцидентов: класс NEVER_CLOSED больше не копим — каждая петля обязана получить диспозицию решено/отложено-с-записью/отклонено; ведёт оркестратор).** *Владелец:* ~~вокабуляр content-лейблов~~ **РЕШЕНО 25.07: ПАРА `violence`/`sexually-explicit`** (D39.27 п.5 — сохраняет D14 п.1 и dspro-редактора на violence) ⇒ следствие в работе: assert-only `action` (дельта пака-17) · ~~Q2 редактор под лейблом~~ **ОТЛОЖЕН С ЗАПИСЬЮ** (D39.27 п.7: при паре связывает только `sexually-explicit`-книги, которых нет; возвращается вместе с предусловием D22.7 перед первой explicit-книгой; кандидаты — grok-4.3 интерим, glm-5 только после сверки ToS Z.AI по первоисточнику, mistral отвергнут D39.20) · **ToS-факты под `accepts_labels` — пер-провайдер И ПЕР-ЛЕЙБЛ, по первоисточникам (зона фиксации — `experiments/00-provider-quirks.md`, полигон): ЕДИНСТВЕННОЕ, что осталось между механизмом и живой лейблованной книгой; на владельце — маршрут работы (сверку прогоняет оркестратор с цитатами → полигон фиксирует, либо полигон целиком). До фактов механизм fail-closed — верное состояние** · **промпт-слой под лейблом (НОВОЕ, вскрыто вопросом владельца): лейбл сегодня НЕ доезжает до промпта** (чистые данные маршрутизации + load-time гарантия); нужно ли, чтобы переводчик/редактор ЗНАЛИ о свойстве контента — отдельное решение промпт-слоя · мини-каноны сид-дельты — 元-корень (предложение «изначальн-») и 春秋蝉 (подписано «Весенне-осенняя цикада», ждёт подтверждения) · **включение платной петли ремонта — по замеру остатка на пост-профилактическом прогоне (D39.24; заменил mini-live гейт D6)** · ja→ru-реплика §B5 (тайминг) · старые Ф2.5-блокеры (билингв-якорь — D25 п.9 Q1 · контаминация корпуса — D27 п.4 · судья-дублёр D22.6 · планка запуска · publishable/waiver — D25 п.1 · FN-bound L3 — D25 п.4 · юр-пакет · провенанс 12-*-доков · xAI-ключ off перед продом D27 · residual-тачпойнты exp16, не влитые в сид-дельту: мини-голд алиасов · precision@30, `books/gu-zhenren/exp16/`). *Оркестратор:* ~~ратификация дизайна пака-16~~ РЕШЕНО D39.24 (25.07) · фантом-гард D-ссылок в чек-листе лендинга (действует) · ~~doc-sync pass~~ **ИСПОЛНЕН 25.07** (запись ниже: оба puml перерисованы под пост-пак-16 · баннеры на 01/02/03/04/06/07/08/09 · ресёрч-свип 18/18c/19/20/21/22 · README/POLYGON_PACKAGE4 освежены; residual — пинги полигону в его зоне) · ~~приёмка пака-17 фаза 1~~ **РЕШЕНО D39.26 (25.07: принято в редакции §14, три добора приёмки, фаза 2 размечена)** · **`escalation_model` = ОТВЕТИВШАЯ модель (НОВОЕ, вынесено из пака-17): вердикт-изменение** — golden пинит `esc_model` на ОТКАЗАВШЕМ хопе, маскировка не скрывает ⇒ отдельная ратификация с санкционированным пере-капчером · **`--accept-rebill[=usd]` с порогом `min($0.50, 5%×ProjectedBookUSD)` РАТИФИЦИРОВАН D20.2-Q2, но в коде/CLI ОТСУТСТВУЕТ (НОВОЕ)** — живёт только в спеке `backend/docs/D15.2-*.md`; `--resnapshot` пере-пиннит без суммы · **D22.7 (пер-чанковый L3-скрин) — предусловие первой erotica-книги в проде НЕ снято паком-17 (НОВОЕ, держать отдельным гейтом)** · фантом-гард D-ссылок в чек-листе лендинга (действует). *Бэкенд:* ~~пак-17 (фазы 1–2 + дельта)~~ **ЗАКРЫТ ЦЕЛИКОМ 25.07 (D39.26/27/28); фикс-лист закрыт; бэкенд-очереди по паку-17 НЕТ** · ~~пак-16 ф.2~~ ЗАЛЕНДЕНА 25.07 · per-class исходы ремонта — схемное решение (класс не durable), принимается ВМЕСТЕ с включением петли · ниты общности (владелец 25.07) — **едут хвостом пака-17 фазы 2 УСЛОВНО**: только в байт-нейтральной форме с доказательством исполнением (иначе `CheapGateVersion`/`pack.Version()` двигают снапшот стендовых книг и убивают $0-резюм драфта под замер остатка пака-16): Detail-строки чекеров → шаблоны/цитаты из данных (статический 时辰-текст в generic-файле соврёт второй паре) · множитель ×2 `lintTimeUnits` (`checkers.go:174`) → ключ данных. *Полигон (residual):* **6 пингов doc-sync аудита 25.07 (зона `docs/experiments/` — оркестратор не правит):** (1) `00-provider-quirks.md:13,73` — катовер deepseek-chat 24.07 ПРОШЁЛ, пост-катовер факт (жив ли алиас, цены) не внесён — live-проба `/models` + вендор-дока; (2) `00-provider-quirks.md:69` — оговорка «mistral-editor-арм до guard» superseded D39.20 (mistral вон из редакторов; rate-limit-цифры оставить — нужны переводчику канала B пака-17); (3) `09-pilot-protocol.md:3` — ⚠-указатель застрял на D31/glm-5: продлить до пост-D39.22 (dspro-интерим · планка ≤2 не пройдена rerun2 · авто-QA слепыми метками D39.20 · стайл-каноны D39.21), тело не трогать; (4) `15-segmentation-empirics.md:6-7` — шапка до сих пор «НИ ОДНОГО платного вызова», а эксп залендён REV.2 (D39.7/D39.8, $12.79) — перевернуть статус-блок; (5) `16-bank-mining.md:3` — дописать финал «ИСПОЛНЕН И ЗАЛЕНДЁН (D39.10); gender-тачпойнт §7 закрыт (D39.19); продолжение — Go-майнер парити EXACT»; (6) `experiments/README.md` — индекс без строк exp15/16 (сверка застряла на D38.2); (7) `eval/README.md` — не тронут с 19.07 (пре-rerun2): освежить статус под пост-D39.22 (эксп закрыт · судейский риг-стандарт D39.7 · экстракция только `tmctl export`). > - Архивы хроники: `archive/PROGRESS-2026-07-04-10.md` (D31) · `archive/PROGRESS-2026-07-10-13.md` (D39.6-гигиена) · `archive/PROGRESS-2026-07-13-25.md` (слайс 25.07: стройка паков 11–16, rerun2). Записи ниже — живой хвост. +## Оркестратор №8 — ДЕЛЬТА ПАКА-17 ПРИНЯТА и залендена: действие `allow` (assert-only) — ратифицированная ПАРА лейблов выразима ДАННЫМИ; ПАК-17 ЗАКРЫТ ЦЕЛИКОМ (D39.28), 25.07 + +Дельта закрыла работу, которую открыл ответ владельца по вокабуляру. Итог: тройка `route | allow | terminal`, где ВСЕ ТРИ несут ОДИН инвариант (каждая достижимая модель принимает все лейблы книги) и различаются остальным; `allow` не двигает маршрут и ЗАПРЕЩАЕТ `chain`/`label_models`/бюджет как бессмысленные (дисциплина «громко, а не игнор»). `checkContentRouting` править не пришлось — предсказание D39.27 п.6 подтвердилось. **Моя приёмка исполнением:** build/vet/gofmt чисты (единственная жалоба `internal/llm/llm.go` воспроизводится на HEAD — сессия файл не трогала, оговорка честная) · `-race` 12/12 · **golden `f7641993…0020f8` = HEAD байт-в-байт**, `testdata/` чист · парити EXACT `n=13618 … 0.9655` · **ценз диффом ИМЁН 451→455, удалено 0**. **Моя независимая мутация:** инвариант проверяет только ПЕРВЫЙ лейбл вместо объединения → красный `TestRatifiedLabelPairIsDataOnly` — союзная семантика, на которой стоит ПАРА, запинена тестом, кодирующим форму ответа владельца. Сквозняк `TestAssertOnlyLabelRunsWithoutMovingAnything` прочитан и НЕ вакуумен: число вызовов + каждое тело запроса + стоимость + оба волновых снапшота, всё при `budget_usd = 0`. **Данные:** `accepts_labels` осознанно пусты — ратифицировал как ВЕРНОЕ fail-closed состояние; в диффах данных ни одного ToS-утверждения от сессии (значения = факты по первоисточникам, ПЕР-ЛЕЙБЛУ, фиксирует полигон). Фикс-лист D39.27 закрыт. **Пак-17 отработан целиком (D39.26 · D39.27 · D39.28); промт переведён в архивный статус.** + ## Оркестратор №8 — ПАК-17 ФАЗА 2 ПРИНЯТА (execute-first) и ЗАЛЕНДЕНА: механизм лейблов живой, вокабуляр владельца = ПАРА, дельта = assert-only action (D39.27), 25.07 Приёмка гоняла, а не читала. **Мой ре-ран манифеста:** build/vet чисты · `-race` 12 пакетов ok, 0 FAIL · **golden `f7641993…4f0020f8` байт-в-байт равен HEAD-версии фикстуры** (`git show HEAD:… | sha256sum`), `testdata/` чист ⇒ пере-капчера не было · **парити EXACT `n=13618 catastrophe{方源:0 蛊:1 蛊师:2 古月:22} recall 0.9655 (56/58)`** тождественно пакам 15/16 · **ценз проверен ДИФФОМ ИМЁН (не счётчиком): 433→451**, удалены ровно 8 объявленных (6 failover + `TestCheckAdultChannel` + `TestRunnerChannelBRequiresPermissive`) · `-shuffle=on` зелёный · шов на провод один. **Рубеж-2 — три МОИХ мутации в копии дерева (не из списка сессии), все пойманы именованными тестами:** лейблы обратно в канон `BriefHash` → `TestEditorOnlyLabelLeavesTheDraftWaveUntouched` красный (приятная находка: инвариант D39.26 п.1 запинён СЛЕДСТВИЕМ — снапшотом драфт-волны — что сильнее ассерта равенства хешей, моя гипотеза «инвариант не запинён» опровергнута исполнением) · отключение лейбл-проверки в `clientFor` → два красных · безусловный отказ маршрутизации → `TestLabelledReadPathStaysAlive` красный. **По коду подтверждено:** достижимость лейбл-зависима, `r.client(` больше нет, порядок `GetCheckpoint→clientFor→Reserve`, добор C закрыт именованным тестом рейт-гардов, отчётность вся `omitempty`, **шиппинг-конфиги без единой поведенческой правки**, **стенд чист** (0 файлов с `permissive:`/`channel:` вне git; все 5 книг ссылаются на репозиторный `models.yaml`) ⇒ отставки операционку не ломают. Сессия сама нашла и починила 8 дефектов ДО сдачи (CRIT: лейбл-фикстура была тождественным отображением ⇒ несущая половина пака не покрывалась) — ровно та самопроверка исполнением, которой мандат 12.07 и добивался. **Диспозиции:** форма добора B = **форма 2** (гейт независимо от `off`; форма 1 валит `models.yaml` через `echoMineViolation`×`control: effort` — проверено) + generic-предупреждение `sourceEchoExposure` · семантика хопа = следствие D39.26 п.1, не девиация · `labelsFor()` подписью — сильнее рантайм-ассерта · хвосты общности — отложено-с-записью. **Ответ владельца: вокабуляр = ПАРА `violence`/`sexually-explicit`** ⇒ **дельта: третье значение `action` (assert-only)** — без него пара невыразима данными (`route` требует chain+бюджет+`label_models`, `terminal` не обрабатывает). **Q2 редактор отложен с записью** (связывает только explicit-книги, которых нет; на violence остаётся dspro). **Новая петля, вскрытая вопросом владельца:** лейбл НЕ доезжает до промпта — это чистые данные маршрутизации; «промпт знает о свойстве контента» = отдельное решение промпт-слоя. diff --git a/docs/README.md b/docs/README.md index 03bc19ad..f15894d8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,15 +11,15 @@ ## Структура -- `architecture/` — синтез. **Источник истины по решениям — [`05-decisions-log.md`](architecture/05-decisions-log.md) (D1–D39.27); при конфликте с любым доком он выше.** +- `architecture/` — синтез. **Источник истины по решениям — [`05-decisions-log.md`](architecture/05-decisions-log.md) (D1–D39.28); при конфликте с любым доком он выше.** - `01-decisions.md` — принципы Р1–Р10; `02-mvp-plan.md` — фазы и приёмка (v3, 09.07); `03-implementation-notes.md` — контракты Фазы 0; `04-unhappy-paths.md` — ~70 режимов отказа → механизм; `06-memory-risk-registry.md` — реестр рисков банка памяти; **[`09-target-architecture.md`](architecture/09-target-architecture.md) — целевая 7-слойная архитектура (D39; статус стройки — шапка-таблица; инвариант общности §0.1)** · [`10-prompt-architecture.md`](architecture/10-prompt-architecture.md) — консолидированная промпт-заметка (концерн 4) · [`12-go-style-notes.md`](architecture/12-go-style-notes.md) — норматив общности §0 + Go-ответы. **Исполненные (архив 25.07, санкция владельца, → `archive/architecture/`):** [`07-strategic-review.md`](archive/architecture/07-strategic-review.md) (стратаудит 09.07 — курс исполнен) · [`08-sync-audit-ledger.md`](archive/architecture/08-sync-audit-ledger.md) (ледджер синк-аудита — отработан) · [`11-implementation-plan.md`](archive/architecture/11-implementation-plan.md) (план пака-11 — исполнен целиком); `components.puml`/`pipeline.puml` — диаграммы (перерисованы 25.07 под пост-пак-16 реальность: волновой исполнитель, пакетный сплит пака-15, repair-петля за `enabled:false`, generic content-labels D39.25; владелец смотрит PlantUML-расширением VS Code; вручную НЕ рендерить). - `experiments/` — эмпирика «Полигона»: `00-provider-quirks` (читать перед любым вызовом провайдера), `01-token-calibration`, `02-refusal-benchmark`, `03-local-stand`, `04-editor-quality`, `06-local-extraction`, `07-coverage-precision`, `08-cost-model-v2` (актуальная денежная модель), `09-pilot-protocol` (пилот Ф2.5 + поправки D13), `10-explicit-benchmark` (18+ violence-рука канала B), `11-erotica-benchmark` (erotica по трём парам/регистрам — закрытие D14.4, D22), `12-quality-diagnosis`/`13-translator-bakeoff`/`14-quality-empirics`/`14b-meaning-battery` (дуга качества «мерить→строить», закрыты → D30/D32/D37/D38), `15-segmentation-empirics` (exp15: когезия под floor-шумом, фертильность — закрыт → D39.7/D39.8), `16-bank-mining` (exp16: WHICH/WHAT банк-майнинга — закрыт → D39.10). - `research/` — фактура исследований 04–05.07: `01–10` базовые, `11-gap-*` добор критиком, `12-*` режимы отказа/отзывы/таксономии (+ два внешних материала с провенанс-шапками), `13` валидация памяти, `14` адаптивная память, `15` голос и состояние (принят, D21), **`16` ридер-IDE (принят с ревью-шапкой, D29)**, **`17` внешняя критика GPT-5.6 (принят с ревью-шапкой, D25)** — у 16/17 читать шапку прежде тела. **`18` рычаги качества (два отчёта, D36)** · **`19` нарезка+когезия+контракт t/e (D39.1)** · **`20` банк-майнинг W1.5 (D39.6)** · **`21` обзор LLM-транспорта чужих харнессов (23.07: наш транспорт опережает/вровень со всеми 11)** · **`22` доменные харнессы перевода (24.07: калибрующий — ядро подтверждено, впереди COGS/18+/общность/измеренность, позади Q4-выпуск и gate+repair-петля; сиквел `02`/`05`)** — у всех ревью-шапки. ⚠ Часть под superseded-баннерами (01/02/03/04/05/09 и gap-1/2/5) — **читай баннер прежде содержимого**. - `PROGRESS.md` — **журнал** (CURRENT-STATE сверху, ниже хронология; НЕ источник решений). -- **Активные хендофф-промты (пост-пак-16, 25.07):** [`BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md`](BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md) (**пак-17 канал B (18+) вживую — ЗАЛЕНДЕН ЦЕЛИКОМ 25.07: фаза 1 дизайн D39.26, фаза 2 стройка D39.27; АКТИВНА только §«ДЕЛЬТА» — assert-only `action` под ратифицированный ПАРНЫЙ вокабуляр `violence`/`sexually-explicit`**; отчёты с ревью-шапками — [`archive/reports/PACK17_DESIGN_2026-07-25.md`](archive/reports/PACK17_DESIGN_2026-07-25.md), [`archive/reports/PACK17_CHANNEL_B_REPORT_2026-07-25.md`](archive/reports/PACK17_CHANNEL_B_REPORT_2026-07-25.md); L3 — жёсткая линия, live только по санкции владельца) · [`ORCHESTRATOR_SESSION_PROMPT.md`](ORCHESTRATOR_SESSION_PROMPT.md) (хендофф №6, 25.07 — только живое: роль/методология/уроки/нормы §1–11; состояние НЕ дублирует) · [`POLYGON_PACKAGE4_SESSION_PROMPT.md`](POLYGON_PACKAGE4_SESSION_PROMPT.md) (residual пилот/18+/echo). **Пак-16 ЗАЛЕНДЁН 25.07** (слой-3 diff-редактор: двухфазный дизайн D39.24 → стройка за `enabled:false`; отчёты с ревью-шапками — [`archive/reports/PACK16_DESIGN_2026-07-25.md`](archive/reports/PACK16_DESIGN_2026-07-25.md), [`archive/reports/PACK16_DIFF_EDITOR_REPORT_2026-07-25.md`](archive/reports/PACK16_DIFF_EDITOR_REPORT_2026-07-25.md)). **Паки 12/13/14/15 ЗАЛЕНДЕНЫ 24–25.07** (приёмка воркфлоу-линзами; отчёты с ревью-шапками — [`archive/reports/PACK14_GENERALITY_REPORT_2026-07-24.md`](archive/reports/PACK14_GENERALITY_REPORT_2026-07-24.md), [`archive/reports/PACK15_STRUCTURE_REPORT_2026-07-25.md`](archive/reports/PACK15_STRUCTURE_REPORT_2026-07-25.md); норматив — [`architecture/12-go-style-notes.md`](architecture/12-go-style-notes.md)). Дальше: **пак-17 = канал B вживую** (батч со следующим платным прогоном: вопросы итерации №2 + замер остатка ремонта → решение о включении петли) → голос/состояние D21 + C2/C3 → пилот Ф2.5. Закрытые — в `archive/prompts/`. +- **Активные хендофф-промты (пост-пак-16, 25.07):** [`BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md`](BACKEND_PACK17_CHANNEL_B_SESSION_PROMPT.md) (**пак-17 канал B (18+) вживую — ОТРАБОТАН ЦЕЛИКОМ 25.07: дизайн D39.26 · стройка D39.27 · дельта `allow` D39.28; вокабуляр = ПАРА `violence`/`sexually-explicit`, остаток — ФАКТЫ ToS в зоне полигона**; отчёты с ревью-шапками — [`archive/reports/PACK17_DESIGN_2026-07-25.md`](archive/reports/PACK17_DESIGN_2026-07-25.md), [`archive/reports/PACK17_CHANNEL_B_REPORT_2026-07-25.md`](archive/reports/PACK17_CHANNEL_B_REPORT_2026-07-25.md), [`archive/reports/PACK17_DELTA_2026-07-25.md`](archive/reports/PACK17_DELTA_2026-07-25.md); L3 — жёсткая линия, live только по санкции владельца) · [`ORCHESTRATOR_SESSION_PROMPT.md`](ORCHESTRATOR_SESSION_PROMPT.md) (хендофф №6, 25.07 — только живое: роль/методология/уроки/нормы §1–11; состояние НЕ дублирует) · [`POLYGON_PACKAGE4_SESSION_PROMPT.md`](POLYGON_PACKAGE4_SESSION_PROMPT.md) (residual пилот/18+/echo). **Пак-16 ЗАЛЕНДЁН 25.07** (слой-3 diff-редактор: двухфазный дизайн D39.24 → стройка за `enabled:false`; отчёты с ревью-шапками — [`archive/reports/PACK16_DESIGN_2026-07-25.md`](archive/reports/PACK16_DESIGN_2026-07-25.md), [`archive/reports/PACK16_DIFF_EDITOR_REPORT_2026-07-25.md`](archive/reports/PACK16_DIFF_EDITOR_REPORT_2026-07-25.md)). **Паки 12/13/14/15 ЗАЛЕНДЕНЫ 24–25.07** (приёмка воркфлоу-линзами; отчёты с ревью-шапками — [`archive/reports/PACK14_GENERALITY_REPORT_2026-07-24.md`](archive/reports/PACK14_GENERALITY_REPORT_2026-07-24.md), [`archive/reports/PACK15_STRUCTURE_REPORT_2026-07-25.md`](archive/reports/PACK15_STRUCTURE_REPORT_2026-07-25.md); норматив — [`architecture/12-go-style-notes.md`](architecture/12-go-style-notes.md)). Дальше: **пак-17 = канал B вживую** (батч со следующим платным прогоном: вопросы итерации №2 + замер остатка ремонта → решение о включении петли) → голос/состояние D21 + C2/C3 → пилот Ф2.5. Закрытые — в `archive/prompts/`. - `archive/` — `prompts/`: закрытые сессионные промты (только история, инструкции оттуда не исполнять) · `reports/`: отчёты паков/ресёрч-сессий с ревью-шапками (живая фактура приёмок — на них ссылаются D-лог и активные промты) · `architecture/`: исполненные арх-доки (07 стратревью · 08 синк-ледджер · 11 план пака-11) · три слайса хроники `PROGRESS-2026-07-{04-10,10-13,13-25}.md`. -## Статус (2026-07-25, пост-пак-17 — механизм content-labels построен и залендён, D39.27 — механизация выпускного качества) +## Статус (2026-07-25, пак-17 ЗАКРЫТ ЦЕЛИКОМ — механизм content-labels живой, D39.26–D39.28 — механизация выпускного качества) Фаза 0 ✅; Ф1-инфра ✅ (D20–D28). **АРХ-РЕСЕТ D39 ИСПОЛНЕН ЦЕЛИКОМ:** 7-слойная архитектура (`09-*`) → исследовательская программа (D39.1–D39.10) → план (D39.12, `11-*`) → стройка пака-11 (D39.13–D39.19: волновой исполнитель · чанкер output-бюджет · src→dst-редактор · Go-майнер паритет-EXACT · банкнота · `internal/lang`+langpacks · reject-set · echo-сплит). **ПЕРЕ-ПРОГОН rerun2 ИСПОЛНЕН И ПРОЧИТАН (D39.20, 23.07):** операционка вся подтверждена живьём (ОДИН resnapshot · **$0-резюм драфта ×3 арма** · echo 0 · $6.63<$15 · майнинг-стоп→подпись→reject-set); **планка ≤2 НЕ пройдена** — но дефект-классы полностью механизируемы. Три сигнала (судья dspro 0.679>glm 0.589>>mistral 0.232, floor-шум 0 · два слепых чтения): **анти-корреляция гладкость↔верность доказана трижды**; **mistral исключён как несущий редактор** (голос = north-star промпта); dspro-vs-glm → итерация №2 после механизации (драфт $0). research/21: транспорт наш опережает/вровень со всеми 11, рефакторинг отвергнут. **Эксп ЗАКРЫТ (D39.22), итерация №2 отложена; редактор = deepseek-v4-pro ИНТЕРИМ (glm-резерв); стайл-каноны D39.21 ратифицированы.** **Очередь (курс — разработка бэкенда):** паки 12–16 залендены (транспорт-гигиена · выпускной QA · генеральность · структура: движок разложен `internal/{text,seed,chunk,miner,checks,membank}`+композит-корень, конфиг-слой пары, строгий декод · слой-3 diff-редактор ПОСТРОЕН за `enabled:false` по двухфазному дизайну D39.24 — позиционная детекция + read-only скан остатка + машинерия ремонта §15.2; включение платного пути = решение владельца по замеру остатка на пост-профилактическом прогоне) → пак-17 = канал B вживую (**ЗАЛЕНДЕН ЦЕЛИКОМ: D39.26 дизайн + D39.27 стройка; вокабуляр = ПАРА `violence`/`sexually-explicit`, хвост — assert-only `action`**; D39.25: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга; на владельце — вокабуляр лейблов и редактор под лейблом; батч со следующим платным прогоном: вопросы итерации №2 + замер остатка ремонта) → сид-дельта (元/赤城/学堂家老/春秋蝉) → Ф2-механизмы (D21 голос/состояние, native-Gemini судья) + ja→ru §B5 → пилот Ф2.5 (блокеры прежние: билингв-якорь D25.9-Q1, корпус, судья-дублёр D22.6). Судья 18+ = grok-фолбэк (квирк D22.6 подтверждён живьём). Ключ xAI: единый, data-sharing, off перед продом (D27). diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index 63f0d34c..528eef09 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -1,9 +1,9 @@ -# Журнал решений оркестратора — контракт D1–D39.27 (развязки 04.07 · пакеты 09–10.07 · приёмка/качество-первым/пивот/эмпирика 11–12.07 · арх-ресет+стройка пере-прогонного стека 13–19.07) +# Журнал решений оркестратора — контракт D1–D39.28 (развязки 04.07 · пакеты 09–10.07 · приёмка/качество-первым/пивот/эмпирика 11–12.07 · арх-ресет+стройка пере-прогонного стека 13–19.07) > **⟶ КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Читая контракт целиком, держи под рукой, что чем перекрыто: > - **Полностью superseded:** **D1 (моно-редактор) → D17 → D30.1 (редактор БИЛИНГВ)** · D9 (ja-приёмка) → D18 (蛊真人 zh→ru; ja — второй прогон) · D17 → D30.1 · скобка D19.4 «ключ без data-sharing» и D20.3 «чистый ключ = блокер пилота» → **D27** (единый ключ С data-sharing, отключение перед продом) · exp04-дефолт «editor grok-4.3» (D3) → D30.1 (grok reasoning-off из редакторских ролей СНЯТ — no-op; кандидат glm-5-билингв; gemini — премиум-эскалация только за санитайзером D30.3). > - **Частично амендировано:** D3 (канал B → D14.1/D19.1 + оговорки D22.5/D22.6; апекс-слаг `-preview` — D22.4; draft под вопросом exp13 — D30.6) · D6 («off/minimal для draft/edit» эродирован: draft thinking-ON принудительно, editor-off снят D30.1; живы per-роль принцип и D6.2-буфер) · D10 (+D24.2 alias-слепое пятно, истинная консистентность ≈99.5%) · D11-числа → exp08 → **D30.4** (флип D1 = +15–25%, ~$0.85/ранобэ; «$1.5–2» = неподписанная опция Б) · D12 (+D24.3 флоры max_tokens; +D29.1в rollup — амендмент вердикт-правила) · D13.1-арм из решающего → ПОДТВЕРЖДАЮЩИЙ (D30.1); +D25.3 prefix-анализ судьи · D14.2 закрыт D19.1/D22.4; D14.4 закрыт D22.1 · D15.3 исполнен; спека D15.2: v2→v3→v3.1 (D22.2), реализация = текущий пакет (D30.9) · D24.4 +D28.1 (precision/recall-трейдофф; hard-gate требует пере-замера) · порядок D24.5 отложен D26.1 (этап B — после читаемых 25 глав). -> - **Живое ядро без изменений:** D2 (+новый гейт-класс D30.3), D4, D5, D7, D8, D10-механизм, D12-таксономия отказов, D16, D18, D19.1–19.2, D21 (Ф2-механизмы voice/address/reveal), D22.5–22.7, D23 (golden = инвариант №8), D24–D39.27 — действующая голова контракта (**D39.27 = пак-17 фаза 2 ПРИНЯТ и залендён [механизм построен по всем 15 «ровно так»; golden байт-в-байт, парити EXACT, `-race`, ценз 433→451 диффом имён; мои три мутации пойманы именованными тестами]; форма добора B = гейт над резолвнутой моделью НЕЗАВИСИМО от `off` + generic-предупреждение `sourceEchoExposure`; **вокабуляр владельца = ПАРА `violence`/`sexually-explicit`** (сохраняет D14 п.1 и dspro-редактора) ⇒ обязательная дельта: третье значение `action` assert-only; Q2 редактор ОТЛОЖЕН с записью (связывает только explicit-книги); новая петля: лейбл НЕ доезжает до промпта — промпт-слой решается отдельно**; **D39.26 = дизайн пака-17 «generic content-labels × provider-capabilities» ратифицирован В РЕДАКЦИИ §14 отчёта [лейблы НЕ входят ни в один хеш — назначенный слой D20.2-Q3 = резолвнутая модель стадии пер-стадийно/пер-волново · резолв ДО валидации, «достижимое» лейбл-зависимо · один хоп `chain[0]`, `len>1` громко, `budget_usd>0` обязателен · Q1 = книга по аргументу ГАРАНТИИ · вокабуляр `content_labels`(книга)/`accepts_labels`(модель, `*[]string` только сужает) · `permissive`+`Stage.Channel`+enum+блок D4.1 отставляются ОДНИМ диффом · `failover.go` удаляется · отказы за `forWrite`, резолв всегда]; добор оркестратора: единый лейбл «adult» схлопывает violence-развязку D14 п.1 → вокабуляр владельцу · `reasoning:"off"` — тихий выход из аддитивного И эхо-гейта на xai (обобщение D19.1 п.2 в данные не доехало) · рейт-гард деградирует молча; НА ВЛАДЕЛЬЦЕ: вокабуляр лейблов + Q2 редактор; D22.7 паком НЕ снимается**; **D39.25 = в движке НЕТ понятия 18+ [generic content-labels × provider-capabilities, refusal причино-агностичен, «adult» = значение данных; §0 п.6]**; **D39.24 = дизайн пака-16 diff-редактора ратифицирован с перестановкой: $0-части (позиционная детекция · read-only скан · фиксы ru_hours_re/半个时辰) строим, платная машинерия §D3–D5+§15.2 под enabled:false, включение = решение владельца по замеру остатка пост-профилактики; поправки §15.2 (позитивный ре-гейт+анти-удаление · счётчики из role='repair' без миграции · отбрасывание пересечений · reserve-деградация · classify-гард); ru-target-долг = ограничение**; **D39.23 = пак-15 «структура+форма» ратифицирован [хойст text/value-базы → сплит miner/checks/membank/chunk по ИЗМЕРЕННОЙ связности (прил. A пака-14 с поправкой ревью-шапки) · форм-фиксы Export/RequestHash/resolveChunkState · конфиг-слой пары (пар-калибровки из ран-конфигов, промпт-резолв конвенцией) · overlay-хвосты, unionStringMap-коллизия = громкая ошибка; §1 12-go-style амендирован; байт-точность+парити EXACT; crush-масштаб НЕ эмулируем]**; **D39.22 = эксп пере-прогона ЗАКРЫТ решением владельца; итерация №2 ОТЛОЖЕНА [её вопросы — довесок следующего платного прогона]; редактор = deepseek-v4-pro ИНТЕРИМ [судья №1 · проза/глоссарий · дефекты чинибельны · ×2 дешевле], glm-резерв, дефолт-конфиг при лендинге пака-13; курс = разработка бэкенда [масштаб книги · канал B · Ф2-механизмы · ja→ru §B5]**; **D39.21 = стайл-решения владельца: меры → единицы языка читателя [时辰=2ч] · generic-«гу» СРЕДНИЙ род, именные — по-термово из банка · стихи СМЫСЛОМ · 资质=«талант»; + директива аудита полноты банк-инъекции**; **D39.20 = ПЕРЕ-ПРОГОН исполнен+прочитан [операционка вся живьём: $0-резюм драфта ×3 арма · echo 0 · $6.63<$15; судья dspro 0.679>glm 0.589>>mistral 0.232 при floor-шуме 0; слепые чтения ×2: сводные выборы разошлись, чисто-художественные ИДЕНТИЧНЫ = mistral 4/5 → анти-корреляция гладкость↔верность доказана трижды]; планка ≤2 НЕ пройдена → пилот не открыт; mistral ИСКЛЮЧЁН как несущий редактор [критические смысловые вне гейтов; голос = north-star промпта]; dspro-vs-glm НЕ ратифицирован [зазор ~шум] → итерация №2 после механизации [драфт $0]; дефект-классы→механика: 时辰-юнит-чекер · число-масштабы · broken-word/latin · заголовок-шаблон · банк-дельта 元/资质/赤城 · mined_delta-крэш-фикс [пак-13]; решения владельца: род «гу»/меры/стихи/春秋蝉; полигон: авто-QA ключевать слепыми метками**; **D39.19 = пере-прогон-преп ЗАЛЕНДЕН [6 фиксов: R1-FL-A CLI-exit-3 · R1-FL-B reject-set-livelock-закрыт · R1-FL-C x/text-в-Ш-2 · echo-сплит · read-model-naming · ниты]; golden version-only [masked=0]; парити EXACT; §8-манифест полон; gender-signoff закрыт; бэкенд готов к пере-прогону**; **D39.18 = blast-radius edit-единицы: c-lite (редактура чистого остатка вместо бланка всей единицы; вариант владельца (c)+fallback (b)) залендён `488c8d4`, no-flag путь байт-идентичен; + 4 read-модель-фикса пост-лендинг-ревью [quality-echo/status-first-flagged/export-pairs/detail], открытое решение владельцу = echo-метрика семантика; R1-FL-A/B/C deferred в пере-прогон-преп**; **D39.17-errata = runWave parent-cancellation баг [мой рубеж-2 пропустил, 2-я сессия поймала], фикс `5fc4860`**; **D39.17 = R1 драйвер-свитч ЗАЛЕНДЕН [волновой исполнитель черновик∥→банк-майнинг-стоп→редактура∥; редактор не пере-рендерит draft; Ш-1/Ш-2; golden-рерайт; read-модели per-unit]; полный рубеж-2 [5 линз вкл. синк-с-ресёрчем] = clean bill по несущим осям, 3 MINOR/NOTE-фикса (R1-FL-A CLI-exit-код · R1-FL-B mining-стоп-reject · R1-FL-C x/text-версия) в стенд-only mining-пути → пере-прогон-преп; ПАК-11 ЗАВЕРШЁН**; **D39.16 = арх-проход ЗАЛЕНДЕН: майнер-данные (百家姓/Палладий/частицы/суффиксы) вынесены в `internal/lang`+`configs/langpacks/` [граница компиль-enforced, байт-точно — парити EXACT, golden байт-идентичен]; FL-1/2/3+тест-путь; РАТИФИЦИРОВАНЫ 3 дивергенции: language.Tag ОТВЕРГНУТ [CLDR-инстабильность в вердиктах], DC-чекеры+снапшот-фолд DEFER [майнер offline/нейтральность]; общность структурно доказана; промт отработан→архив**; **D39.15 = директива владельца: изоляция языковых данных из `pipeline/` [движок общий · `internal/lang/`+`configs/langpacks/` данными-файлами · `x/text/language.Tag` вместо `isRuTarget` · снапшот-folded как промпты · алгоритмы не трогать]; архитектурный проход ПЕРЕД R1 [ре-секвенс]; поведенчески нейтрально zh→ru [golden байт-стабилен]; не минор-хант/не rewrite-ради-rewrite; промт `BACKEND_ARCH_CLEANUP_SESSION_PROMPT.md` 5-фазный [ресёрч→синтез→фикс-лист→исполнение→ревью]; ja→ru = тест общности §B5 позже**; **D39.14 = пак-11 continuation ПРИНЯТ и залендён [R2 майнер паритет-EXACT · R3 банкнота 12-точек · R4 DC-чекеры+export--pairs · R5 арм-конфиги · R1-аддитив; рубеж-2 D39.13-долга погашен: 3 MINOR/PARTIAL-находки [FL-1 bankTokenBudget-фолд · FL-2 banknote-телеметрия-резюм · FL-3 emission-top-N-кап], все латентны → R1-продолжение; R1 драйвер-свитч резидуал; clean bill по деньгам/wire/порту]**; **D39.13 = пак-11 Block A ПРИНЯТ и залендён [WS2 целиком (вкл. src→dst-блок редактора = закрытие D30.1) + WS1a/b/c-инфра + WS4-ядро; 2 девиации ратифицированы; резидуал R1–R5 в PACK11_REPORT; промт остаётся активным; рубеж-2 приёмки — долг]**; **D39.12 = план `architecture/11-implementation-plan.md` РАТИФИЦИРОВАН [три рубежа: ревью-2 F1–F11 → 7-агентный адверсариал → подтверждающий пас; пер-волновой снапшот + инвариант секвенирования = «переоплата ОДНА»; EditCeilingOut=3200; лемматизатор дефолт B; 4 гейтнутых арма]; бэкенд-пак ЕДИНОЙ сессией — `BACKEND_PLAN11_SESSION_PROMPT.md`**; **D39.11 = директива владельца: стройка ТОЛЬКО после дизайн-синтез сессии с верифицированными алгоритмами; хендофф №5**; **D39.10 = exp16: WHICH подтверждена [V-C recall 0.97, $0] / WHAT→банкнота+Палладий [canon-recovery <70%], 9B-споттер не нужен, Z1→облако, ре-аудит exp14b чист [errata не требуется]; исследовательская программа ЗАВЕРШЕНА → бэкенд-пак слоёв 1+4**; **D39.9 = Q4a: верность НЕ на translate-стадии [flash уже верен, pro ×3.67 не ратифицирован], смысл ломает glm-РЕДАКТОР [свап-арм приоритетен], DET-пол 0.261; exp14b-правила дефектны → $0-ре-аудит в exp16**; **D39.8 = слепое чтение: планка не пройдена никем, дефекты распределены [читатель подтвердил floor-null], битва качества → дефект-классы [时辰-юниты, числа, gender-enforce, стих, регистр]**; **D39.7 = exp15 залендён REV.2: «граница вредит» отозван [артефакт окна судьи], все когезия-эффекты под floor 0.126, «sequential нигде не лучше» LOO-stable [не ратифицированный зелёный свет], фертильность 1.20/0.39, $12.79/$15; гейт = слепое чтение владельца**; **D39.6 = research/20 банк-майнинг залендён [детектор-лестница V-A/B/C, алиас-ярусы, banknote-v1 с 12 гейт-точками, §B5 плагины P1–P6, §D-пре-рег]; полигон-стадия после exp15**; **D39.5 = пак-1.5+F1–F9 залендены [tmctl export с manifest/drift-гардами · fold-first санитайзер · bounded глосс · пример zh-ru]; экстракция полигона впредь через tmctl export**; **D39.4 = адверсариал-долг ПОГАШЕН [8 осей: 0 CRIT/1 HIGH/6 MED], пак-1.5 придержан под фикс-лист F1–F9 в §T4**; **D39.3 = пакет банк-майнинга W1.5 [диспозиция H15/V4-п4] ратифицирован, research/20-промт с HOLD до фриза exp15**; **D39.2 = трек-A пак-1 залендён `d3f6b34`: trust-gated suppressor [терм-дрейф закрыт в КОДЕ] + export-contract нормализация + pair-сеам + реестры + quality-report; адверсариал-долг за 529**; **D39.1 = research/19 залендён: конфликт exp14↔research/18 разрешён [эмиссия≠окно≠связность], Go-спека чанкера+когезии, волновая архитектура W0–W3 [вводная владельца: параллелизм], пре-рег эмпирики Q0–Q5; оркестратор-ревью снято владельцем**; **D39 = АРХ-РЕСЕТ: синк-аудит сломанного телефона [65 находок, 0 refuted, `08-sync-audit-ledger.md`] + целевая 7-слойная архитектура [`09-target-architecture.md`] + фазовый план [трек A build-now ∥ трек B ресёрч, пере-прогон после]; формализует фантомный «D35.7» как цель слоя 1; терм-дрейф код-корень жив [сид-воркэраунд]; редактор 4-мандата-full-regen = причина инверсий**; D35 = пивот качество-первым; D36 = приёмка research/18; D37 = приёмка exp14: претензия-1=промпт-рычаг; D38 = приёмка exp14b: «только gpt-5.4» ОПРОВЕРГНУТО [mistral/deepseek-pro чинят], фронтир в дефолт НЕ нужен, корень терм-дрейфа = статус-draft сида; **D38.1 = слепой h2h P1a↔F-disc залендён [ChatGPT «фронтир сильнее» / Claude «near-parity» расходятся, СХОДЯТСЯ: дефекты = инфра] → РАЗВОРОТ «мерить→строить», выдан BACKEND_INFRA_PACK; D38.2 = консолидация/док-гигиена: спент exp-скрипты в `eval/exp12|13|14|14b/`, статус-доки+.puml актуализированы; D38.3 = инфра-пак читаемости залендён (санитайзер-классы strip+export + число/omission-гард), верифицирован исполнением [build/vet/test зелёные, 3 span-дефекта запинены]; D38.4 = аудит полноты exp14/14b: чэнъюй-рычаг забыт near-term→дописан в дискурс-промпт; inversion-гард D37 §2г не строился (число/omission ≠ полярность)→editor-swap = защита от инверсий (приоритет↑); нарезка = re-run-арм; гард-тумблер вкл. при resnapshot; D38.5 = компоненты resnapshot залендены+верифицированы [reseed-сид eb409f2 · дискурс+чэнъюй editor v3 0ac5f7a с few_shot-тумблером] → следующий шаг единый resnapshot + пере-прогон**). +> - **Живое ядро без изменений:** D2 (+новый гейт-класс D30.3), D4, D5, D7, D8, D10-механизм, D12-таксономия отказов, D16, D18, D19.1–19.2, D21 (Ф2-механизмы voice/address/reveal), D22.5–22.7, D23 (golden = инвариант №8), D24–D39.28 — действующая голова контракта (**D39.28 = ПАК-17 ЗАКРЫТ ЦЕЛИКОМ: третье действие `allow` (assert-only) построено ⇒ ратифицированная ПАРА `violence`/`sexually-explicit` выразима ДАННЫМИ; тройка `route|allow|terminal` несёт ОДИН инвариант и различается остальным; `accepts_labels` пусты = верное fail-closed до ФАКТОВ ToS по первоисточникам (пер-лейблу, зона полигона)**; **D39.27 = пак-17 фаза 2 ПРИНЯТ и залендён [механизм построен по всем 15 «ровно так»; golden байт-в-байт, парити EXACT, `-race`, ценз 433→451 диффом имён; мои три мутации пойманы именованными тестами]; форма добора B = гейт над резолвнутой моделью НЕЗАВИСИМО от `off` + generic-предупреждение `sourceEchoExposure`; **вокабуляр владельца = ПАРА `violence`/`sexually-explicit`** (сохраняет D14 п.1 и dspro-редактора) ⇒ обязательная дельта: третье значение `action` assert-only; Q2 редактор ОТЛОЖЕН с записью (связывает только explicit-книги); новая петля: лейбл НЕ доезжает до промпта — промпт-слой решается отдельно**; **D39.26 = дизайн пака-17 «generic content-labels × provider-capabilities» ратифицирован В РЕДАКЦИИ §14 отчёта [лейблы НЕ входят ни в один хеш — назначенный слой D20.2-Q3 = резолвнутая модель стадии пер-стадийно/пер-волново · резолв ДО валидации, «достижимое» лейбл-зависимо · один хоп `chain[0]`, `len>1` громко, `budget_usd>0` обязателен · Q1 = книга по аргументу ГАРАНТИИ · вокабуляр `content_labels`(книга)/`accepts_labels`(модель, `*[]string` только сужает) · `permissive`+`Stage.Channel`+enum+блок D4.1 отставляются ОДНИМ диффом · `failover.go` удаляется · отказы за `forWrite`, резолв всегда]; добор оркестратора: единый лейбл «adult» схлопывает violence-развязку D14 п.1 → вокабуляр владельцу · `reasoning:"off"` — тихий выход из аддитивного И эхо-гейта на xai (обобщение D19.1 п.2 в данные не доехало) · рейт-гард деградирует молча; НА ВЛАДЕЛЬЦЕ: вокабуляр лейблов + Q2 редактор; D22.7 паком НЕ снимается**; **D39.25 = в движке НЕТ понятия 18+ [generic content-labels × provider-capabilities, refusal причино-агностичен, «adult» = значение данных; §0 п.6]**; **D39.24 = дизайн пака-16 diff-редактора ратифицирован с перестановкой: $0-части (позиционная детекция · read-only скан · фиксы ru_hours_re/半个时辰) строим, платная машинерия §D3–D5+§15.2 под enabled:false, включение = решение владельца по замеру остатка пост-профилактики; поправки §15.2 (позитивный ре-гейт+анти-удаление · счётчики из role='repair' без миграции · отбрасывание пересечений · reserve-деградация · classify-гард); ru-target-долг = ограничение**; **D39.23 = пак-15 «структура+форма» ратифицирован [хойст text/value-базы → сплит miner/checks/membank/chunk по ИЗМЕРЕННОЙ связности (прил. A пака-14 с поправкой ревью-шапки) · форм-фиксы Export/RequestHash/resolveChunkState · конфиг-слой пары (пар-калибровки из ран-конфигов, промпт-резолв конвенцией) · overlay-хвосты, unionStringMap-коллизия = громкая ошибка; §1 12-go-style амендирован; байт-точность+парити EXACT; crush-масштаб НЕ эмулируем]**; **D39.22 = эксп пере-прогона ЗАКРЫТ решением владельца; итерация №2 ОТЛОЖЕНА [её вопросы — довесок следующего платного прогона]; редактор = deepseek-v4-pro ИНТЕРИМ [судья №1 · проза/глоссарий · дефекты чинибельны · ×2 дешевле], glm-резерв, дефолт-конфиг при лендинге пака-13; курс = разработка бэкенда [масштаб книги · канал B · Ф2-механизмы · ja→ru §B5]**; **D39.21 = стайл-решения владельца: меры → единицы языка читателя [时辰=2ч] · generic-«гу» СРЕДНИЙ род, именные — по-термово из банка · стихи СМЫСЛОМ · 资质=«талант»; + директива аудита полноты банк-инъекции**; **D39.20 = ПЕРЕ-ПРОГОН исполнен+прочитан [операционка вся живьём: $0-резюм драфта ×3 арма · echo 0 · $6.63<$15; судья dspro 0.679>glm 0.589>>mistral 0.232 при floor-шуме 0; слепые чтения ×2: сводные выборы разошлись, чисто-художественные ИДЕНТИЧНЫ = mistral 4/5 → анти-корреляция гладкость↔верность доказана трижды]; планка ≤2 НЕ пройдена → пилот не открыт; mistral ИСКЛЮЧЁН как несущий редактор [критические смысловые вне гейтов; голос = north-star промпта]; dspro-vs-glm НЕ ратифицирован [зазор ~шум] → итерация №2 после механизации [драфт $0]; дефект-классы→механика: 时辰-юнит-чекер · число-масштабы · broken-word/latin · заголовок-шаблон · банк-дельта 元/资质/赤城 · mined_delta-крэш-фикс [пак-13]; решения владельца: род «гу»/меры/стихи/春秋蝉; полигон: авто-QA ключевать слепыми метками**; **D39.19 = пере-прогон-преп ЗАЛЕНДЕН [6 фиксов: R1-FL-A CLI-exit-3 · R1-FL-B reject-set-livelock-закрыт · R1-FL-C x/text-в-Ш-2 · echo-сплит · read-model-naming · ниты]; golden version-only [masked=0]; парити EXACT; §8-манифест полон; gender-signoff закрыт; бэкенд готов к пере-прогону**; **D39.18 = blast-radius edit-единицы: c-lite (редактура чистого остатка вместо бланка всей единицы; вариант владельца (c)+fallback (b)) залендён `488c8d4`, no-flag путь байт-идентичен; + 4 read-модель-фикса пост-лендинг-ревью [quality-echo/status-first-flagged/export-pairs/detail], открытое решение владельцу = echo-метрика семантика; R1-FL-A/B/C deferred в пере-прогон-преп**; **D39.17-errata = runWave parent-cancellation баг [мой рубеж-2 пропустил, 2-я сессия поймала], фикс `5fc4860`**; **D39.17 = R1 драйвер-свитч ЗАЛЕНДЕН [волновой исполнитель черновик∥→банк-майнинг-стоп→редактура∥; редактор не пере-рендерит draft; Ш-1/Ш-2; golden-рерайт; read-модели per-unit]; полный рубеж-2 [5 линз вкл. синк-с-ресёрчем] = clean bill по несущим осям, 3 MINOR/NOTE-фикса (R1-FL-A CLI-exit-код · R1-FL-B mining-стоп-reject · R1-FL-C x/text-версия) в стенд-only mining-пути → пере-прогон-преп; ПАК-11 ЗАВЕРШЁН**; **D39.16 = арх-проход ЗАЛЕНДЕН: майнер-данные (百家姓/Палладий/частицы/суффиксы) вынесены в `internal/lang`+`configs/langpacks/` [граница компиль-enforced, байт-точно — парити EXACT, golden байт-идентичен]; FL-1/2/3+тест-путь; РАТИФИЦИРОВАНЫ 3 дивергенции: language.Tag ОТВЕРГНУТ [CLDR-инстабильность в вердиктах], DC-чекеры+снапшот-фолд DEFER [майнер offline/нейтральность]; общность структурно доказана; промт отработан→архив**; **D39.15 = директива владельца: изоляция языковых данных из `pipeline/` [движок общий · `internal/lang/`+`configs/langpacks/` данными-файлами · `x/text/language.Tag` вместо `isRuTarget` · снапшот-folded как промпты · алгоритмы не трогать]; архитектурный проход ПЕРЕД R1 [ре-секвенс]; поведенчески нейтрально zh→ru [golden байт-стабилен]; не минор-хант/не rewrite-ради-rewrite; промт `BACKEND_ARCH_CLEANUP_SESSION_PROMPT.md` 5-фазный [ресёрч→синтез→фикс-лист→исполнение→ревью]; ja→ru = тест общности §B5 позже**; **D39.14 = пак-11 continuation ПРИНЯТ и залендён [R2 майнер паритет-EXACT · R3 банкнота 12-точек · R4 DC-чекеры+export--pairs · R5 арм-конфиги · R1-аддитив; рубеж-2 D39.13-долга погашен: 3 MINOR/PARTIAL-находки [FL-1 bankTokenBudget-фолд · FL-2 banknote-телеметрия-резюм · FL-3 emission-top-N-кап], все латентны → R1-продолжение; R1 драйвер-свитч резидуал; clean bill по деньгам/wire/порту]**; **D39.13 = пак-11 Block A ПРИНЯТ и залендён [WS2 целиком (вкл. src→dst-блок редактора = закрытие D30.1) + WS1a/b/c-инфра + WS4-ядро; 2 девиации ратифицированы; резидуал R1–R5 в PACK11_REPORT; промт остаётся активным; рубеж-2 приёмки — долг]**; **D39.12 = план `architecture/11-implementation-plan.md` РАТИФИЦИРОВАН [три рубежа: ревью-2 F1–F11 → 7-агентный адверсариал → подтверждающий пас; пер-волновой снапшот + инвариант секвенирования = «переоплата ОДНА»; EditCeilingOut=3200; лемматизатор дефолт B; 4 гейтнутых арма]; бэкенд-пак ЕДИНОЙ сессией — `BACKEND_PLAN11_SESSION_PROMPT.md`**; **D39.11 = директива владельца: стройка ТОЛЬКО после дизайн-синтез сессии с верифицированными алгоритмами; хендофф №5**; **D39.10 = exp16: WHICH подтверждена [V-C recall 0.97, $0] / WHAT→банкнота+Палладий [canon-recovery <70%], 9B-споттер не нужен, Z1→облако, ре-аудит exp14b чист [errata не требуется]; исследовательская программа ЗАВЕРШЕНА → бэкенд-пак слоёв 1+4**; **D39.9 = Q4a: верность НЕ на translate-стадии [flash уже верен, pro ×3.67 не ратифицирован], смысл ломает glm-РЕДАКТОР [свап-арм приоритетен], DET-пол 0.261; exp14b-правила дефектны → $0-ре-аудит в exp16**; **D39.8 = слепое чтение: планка не пройдена никем, дефекты распределены [читатель подтвердил floor-null], битва качества → дефект-классы [时辰-юниты, числа, gender-enforce, стих, регистр]**; **D39.7 = exp15 залендён REV.2: «граница вредит» отозван [артефакт окна судьи], все когезия-эффекты под floor 0.126, «sequential нигде не лучше» LOO-stable [не ратифицированный зелёный свет], фертильность 1.20/0.39, $12.79/$15; гейт = слепое чтение владельца**; **D39.6 = research/20 банк-майнинг залендён [детектор-лестница V-A/B/C, алиас-ярусы, banknote-v1 с 12 гейт-точками, §B5 плагины P1–P6, §D-пре-рег]; полигон-стадия после exp15**; **D39.5 = пак-1.5+F1–F9 залендены [tmctl export с manifest/drift-гардами · fold-first санитайзер · bounded глосс · пример zh-ru]; экстракция полигона впредь через tmctl export**; **D39.4 = адверсариал-долг ПОГАШЕН [8 осей: 0 CRIT/1 HIGH/6 MED], пак-1.5 придержан под фикс-лист F1–F9 в §T4**; **D39.3 = пакет банк-майнинга W1.5 [диспозиция H15/V4-п4] ратифицирован, research/20-промт с HOLD до фриза exp15**; **D39.2 = трек-A пак-1 залендён `d3f6b34`: trust-gated suppressor [терм-дрейф закрыт в КОДЕ] + export-contract нормализация + pair-сеам + реестры + quality-report; адверсариал-долг за 529**; **D39.1 = research/19 залендён: конфликт exp14↔research/18 разрешён [эмиссия≠окно≠связность], Go-спека чанкера+когезии, волновая архитектура W0–W3 [вводная владельца: параллелизм], пре-рег эмпирики Q0–Q5; оркестратор-ревью снято владельцем**; **D39 = АРХ-РЕСЕТ: синк-аудит сломанного телефона [65 находок, 0 refuted, `08-sync-audit-ledger.md`] + целевая 7-слойная архитектура [`09-target-architecture.md`] + фазовый план [трек A build-now ∥ трек B ресёрч, пере-прогон после]; формализует фантомный «D35.7» как цель слоя 1; терм-дрейф код-корень жив [сид-воркэраунд]; редактор 4-мандата-full-regen = причина инверсий**; D35 = пивот качество-первым; D36 = приёмка research/18; D37 = приёмка exp14: претензия-1=промпт-рычаг; D38 = приёмка exp14b: «только gpt-5.4» ОПРОВЕРГНУТО [mistral/deepseek-pro чинят], фронтир в дефолт НЕ нужен, корень терм-дрейфа = статус-draft сида; **D38.1 = слепой h2h P1a↔F-disc залендён [ChatGPT «фронтир сильнее» / Claude «near-parity» расходятся, СХОДЯТСЯ: дефекты = инфра] → РАЗВОРОТ «мерить→строить», выдан BACKEND_INFRA_PACK; D38.2 = консолидация/док-гигиена: спент exp-скрипты в `eval/exp12|13|14|14b/`, статус-доки+.puml актуализированы; D38.3 = инфра-пак читаемости залендён (санитайзер-классы strip+export + число/omission-гард), верифицирован исполнением [build/vet/test зелёные, 3 span-дефекта запинены]; D38.4 = аудит полноты exp14/14b: чэнъюй-рычаг забыт near-term→дописан в дискурс-промпт; inversion-гард D37 §2г не строился (число/omission ≠ полярность)→editor-swap = защита от инверсий (приоритет↑); нарезка = re-run-арм; гард-тумблер вкл. при resnapshot; D38.5 = компоненты resnapshot залендены+верифицированы [reseed-сид eb409f2 · дискурс+чэнъюй editor v3 0ac5f7a с few_shot-тумблером] → следующий шаг единый resnapshot + пере-прогон**). Ответ на вопросы бэкенд-сессии (PROGRESS §«Вопросы от бэкенда», §«[НУЖНО РЕШЕНИЕ] перед Фазой 1» — с ревизии D31 в `archive/PROGRESS-2026-07-04-10.md`) и полигона (think-режим). Каждое решение прошло адверсариальную панель из 3 критиков (research / экономика / исполнимость в коде Фазы 0); ни одно не отклонено, все уточнены. Это **контракт Фазы 1** — бэкенд исполняет отсюда; при конфликте с буквой 01-decisions/02-mvp-plan — источник истины здесь (потом сольём в основные доки). @@ -868,3 +868,17 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу 10. **Фикс-лист (не блокирует лендинг):** коммент поля `Stage.ReasoningMaxTokens` (`internal/config/pipeline.go:189-194`) всё ещё говорит «ignored for subset providers / reasoning-off» — после формы 2 вторая половина неверна; поправить ближайшим касанием файла. **Инварианты пака подтверждены моим исполнением:** golden байт-идентичен БЕЗ пере-капчера · парити EXACT · `-race` зелёный · ценз диффом имён · сессия не коммитила (лендинг мой). **Скоуп-выносы D39.26 чисты:** `escalation_model`-семантика не тронута, `--accept-rebill` не строился, D22.7 не снят, L3 не строился, live-вызовов с 18+ не было. + +## D39.28 — Дельта пака-17 ПРИНЯТА и залендена: третье действие `allow` (assert-only) — ратифицированная ПАРА лейблов стала выразима ДАННЫМИ; пак-17 закрыт целиком (25.07, оркестратор №8). ✅ + +**Отчёт:** `docs/archive/reports/PACK17_DELTA_2026-07-25.md` (с ревью-шапкой). Закрывает единственную работу, которую открыл ответ владельца по вокабуляру (D39.27 п.5–6). + +**Ратифицировано:** тройка `route | allow | terminal`, где **ВСЕ ТРИ несут ОДИН инвариант** (каждая достижимая модель обязана принимать все лейблы книги) и различаются остальным: `allow` не меняет маршрут и ЗАПРЕЩАЕТ `chain`/`label_models`/бюджет как бессмысленные (автор, написавший их, имел в виду то, чего движок не сделает — дисциплина «громко, а не игнор»); `route` требует цепочку ровно из одного члена + `budget_usd > 0` + хотя бы одну стадию с `label_models`; `terminal` — ни одного вызова. Имя `allow` принимаю (приор промта): читается тройкой, `assert` оператору ничего не говорит. `checkContentRouting` править не пришлось — он уже был action-агностичен, что и предсказывал D39.27 п.6. + +**Моя приёмка — execute-first:** build/vet чисты · `gofmt` чист (единственная жалоба `internal/llm/llm.go` воспроизводится на HEAD — проверил `git show HEAD:… | gofmt -l`, сессия файл не трогала: честная оговорка) · **`-race` 12 пакетов ok, 0 FAIL** · **golden `f7641993…4f0020f8` = HEAD-версия байт-в-байт**, `testdata/` чист · **парити EXACT** `n=13618 … recall 0.9655 (56/58)` · **ценз диффом имён: 451→455, удалено 0**, добавлены ровно четыре названных. **Моя независимая мутация (не из списка сессии):** инвариант проверяет только ПЕРВЫЙ лейбл вместо объединения (`p.ContentLabels[:1]`) → красный `TestRatifiedLabelPairIsDataOnly` — то есть союзная семантика, на которой стоит ратифицированная ПАРА, запинена именно тем тестом, который кодирует форму ответа владельца. **Прочитал сквозняк `TestAssertOnlyLabelRunsWithoutMovingAnything`: не вакуумный** — сверяет число вызовов, КАЖДОЕ тело запроса построчно, суммарную стоимость и ОБА волновых снапшота; и гоняется при `budget_usd = 0`, то есть заодно доказывает, что assert-only не требует бюджета. + +**Данные:** `accepts_labels` осознанно ПУСТЫ (fail-closed) — ратифицирую это как верное состояние. Значения = ФАКТЫ ToS/AUP: только по первоисточникам, **ПЕР-ЛЕЙБЛУ** (`violence` ≠ `sexually-explicit` у одного вендора — на этом стоит D14 п.1), фиксация — полигон (`experiments/00-provider-quirks.md`). Проверил диффы `models.yaml`/`pipeline-c1.yaml`: ни одного ToS-утверждения от сессии, вокабуляр в шиппинг не прибит, примеры синтетические. **Фикс-лист D39.27 п.10 закрыт** (коммент `Stage.ReasoningMaxTokens` приведён в соответствие с формой 2). + +**Две объявленные правки фикстур принимаю** как отражение новой корректной семантики: кейс `terminal` больше не несёт `label_models` (они стали громкой ошибкой при не-route действии — само по себе покрыто тестом), и подстрока сообщения о неизвестном действии стала `route|allow|terminal`. + +**ПАК-17 ЗАКРЫТ ЦЕЛИКОМ** (D39.26 дизайн · D39.27 стройка · D39.28 дельта). Остаток — не код: ToS-факты под `accepts_labels` (петля полигона) · Q2 редактор и D22.7 (перед первой explicit-книгой) · хвосты общности (отложено-с-записью) · `--accept-rebill` (D20.2-Q2, вне пака). **До заполнения фактов ни одна лейблованная книга не запускается — это не дефект, а спроектированное fail-closed поведение.** diff --git a/docs/architecture/09-target-architecture.md b/docs/architecture/09-target-architecture.md index 0f305a5e..7c6327cc 100644 --- a/docs/architecture/09-target-architecture.md +++ b/docs/architecture/09-target-architecture.md @@ -18,7 +18,7 @@ > | 6 Export | ✅ ПОСТРОЕН: `checks.ExportNormalize` на каждом финале; санитайзер-классы strip+export | D39.2 · D38.3 | > | 7 Гигиена | ЧАСТИЧНО: реестр role→инъекция · вынос `foldModelWire` · снос сирот/`gatesEnabled` — сделаны; **target-aware гейты — ОТКРЫТО** (ru-target-долг = ограничение, D39.24 §15.3; едет с канал-B/таргет-работой) | D39.2 · паки 11–16 | > -> §1 «как есть» читать как снимок ДО стройки (const-чанкер · glm-редактор · мёртвые `STMDepth`/`OverlapTokens` — всего этого больше нет). §4 отработан целиком: трек B исполнен (exp15/exp16 — программа ЗАВЕРШЕНА, D39.7/D39.10), гейт пере-прогона пройден фактом rerun2 (D39.20; планка ≤2 НЕ пройдена, эксп ЗАКРЫТ D39.22, редактор = deepseek-v4-pro интерим). Канал B — пак-17 **ЗАЛЕНДЕН ЦЕЛИКОМ (дизайн D39.26 + стройка D39.27: механизм лейблов живой, golden байт-идентичен, `permissive`/`Stage.Channel`/D4.1-блок отставлены, `failover.go` удалён; вокабуляр = ПАРА `violence`/`sexually-explicit`, хвост — assert-only `action`)**; дизайн РАТИФИЦИРОВАН D39.26 (лейблы вне хешей — назначенный слой D20.2-Q3 = резолвнутая модель стадии пер-стадийно/пер-волново · резолв ДО валидации, «достижимое» лейбл-зависимо · один хоп `chain[0]` · гранулярность = книга · `content_labels`(книга) ⊆ `accepts_labels`(модель) · `Stage.Channel`/`permissive`/D4.1-блок отставляются одним диффом), фаза 2 = стройка; инженерная форма переопределена **D39.25**: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities, refusal причино-агностичен; «18+ линия» в §6 читать как продукт-линию, не механизм движка. +> §1 «как есть» читать как снимок ДО стройки (const-чанкер · glm-редактор · мёртвые `STMDepth`/`OverlapTokens` — всего этого больше нет). §4 отработан целиком: трек B исполнен (exp15/exp16 — программа ЗАВЕРШЕНА, D39.7/D39.10), гейт пере-прогона пройден фактом rerun2 (D39.20; планка ≤2 НЕ пройдена, эксп ЗАКРЫТ D39.22, редактор = deepseek-v4-pro интерим). Канал B — пак-17 **ЗАКРЫТ ЦЕЛИКОМ (дизайн D39.26 + стройка D39.27 + дельта D39.28: механизм лейблов живой, действия `route|allow|terminal` с одним инвариантом, golden байт-идентичен, `permissive`/`Stage.Channel`/D4.1-блок отставлены, `failover.go` удалён; вокабуляр = ПАРА `violence`/`sexually-explicit`; `accepts_labels` пусты до ФАКТОВ ToS — fail-closed)**; дизайн РАТИФИЦИРОВАН D39.26 (лейблы вне хешей — назначенный слой D20.2-Q3 = резолвнутая модель стадии пер-стадийно/пер-волново · резолв ДО валидации, «достижимое» лейбл-зависимо · один хоп `chain[0]` · гранулярность = книга · `content_labels`(книга) ⊆ `accepts_labels`(модель) · `Stage.Channel`/`permissive`/D4.1-блок отставляются одним диффом), фаза 2 = стройка; инженерная форма переопределена **D39.25**: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities, refusal причино-агностичен; «18+ линия» в §6 читать как продукт-линию, не механизм движка. --- diff --git a/docs/archive/reports/PACK17_DELTA_2026-07-25.md b/docs/archive/reports/PACK17_DELTA_2026-07-25.md new file mode 100644 index 00000000..9b6cac7a --- /dev/null +++ b/docs/archive/reports/PACK17_DELTA_2026-07-25.md @@ -0,0 +1,64 @@ +# ПАК-17, ДЕЛЬТА ПОСЛЕ ПРИЁМКИ: третье значение `action` под ратифицированную ПАРУ лейблов + +> **Ревью-шапка оркестратора №8 (25.07, приёмка execute-first, инлайн; ПРИНЯТА и залендена — `D39.28`; пак-17 закрыт целиком). 0 блокеров.** Ре-ранил сам: build/vet чисты · `gofmt` чист (единственная жалоба `internal/llm/llm.go` воспроизводится на HEAD — сессия файл не трогала, оговорка честная) · **`-race` 12/12 ok** · **golden `f7641993…4f0020f8` = HEAD байт-в-байт**, `testdata/` чист · **парити EXACT `n=13618 … 0.9655 (56/58)`** · **ценз диффом ИМЁН: 451→455, удалено 0** — ровно четыре названных теста. +> **Моя независимая мутация:** инвариант проверяет только ПЕРВЫЙ лейбл вместо объединения → красный `TestRatifiedLabelPairIsDataOnly`, то есть союзная семантика ратифицированной ПАРЫ запинена именно тем тестом, который кодирует форму ответа владельца. **Сквозняк `TestAssertOnlyLabelRunsWithoutMovingAnything` прочитан — не вакуумный:** число вызовов + КАЖДОЕ тело запроса + стоимость + ОБА волновых снапшота, и всё при `budget_usd = 0`. +> **Ратифицировано:** имя `allow` · «все три действия несут один инвариант, различаются остальным» · запрет `chain`/`label_models` при `allow` как громкая ошибка · две объявленные правки фикстур (новая корректная семантика) · пустые `accepts_labels` как ВЕРНОЕ fail-closed состояние (проверил диффы данных: ни одного ToS-утверждения от сессии, вокабуляр в шиппинг не прибит). Фикс-лист D39.27 закрыт. + +> **Статус: ИСПОЛНЕНО, СДАНО НА ПРИЁМКУ. Сессия НЕ коммитила.** База: `D39.27` (приёмка фазы 2) + §«ДЕЛЬТА ПОСЛЕ ПРИЁМКИ» промта пака-17. Дельта закрывает единственную открытую работу пака: владелец выбрал ПАРУ `violence` / `sexually-explicit`, а чистого capability-ассерта в enum действий не было — то есть ратифицированный вокабуляр был НЕВЫРАЗИМ данными. `git mv` не применялся, удалений нет. + +## §1. Что сделано (три пункта дельты) + +**1. Третье значение `action` — `allow` (`internal/config/pipeline.go`).** Имя выбрал приорное: `route | allow | terminal` читается как естественная тройка, а альтернативы отверг — `assert` ничего не говорит оператору, `require-capability` многословен для ключа данных. Семантика вынесена в док-комментарий enum'а: **все три действия несут ОДИН инвариант** (каждая достижимая модель обязана принимать все лейблы книги) и различаются только остальным: + +| action | инвариант | маршрут | требует | +|---|---|---|---| +| `route` | да | пере-маршрутизирует заявленные стадии + один хоп `chain[0]` | `chain` (ровно 1 член) · `budget_usd > 0` · ≥1 стадия с `label_models` | +| **`allow`** | **да — это ВЕСЬ смысл политики** | **не меняет ничего** | **ничего; `chain`/`label_models` ЗАПРЕЩЕНЫ как бессмысленные** | +| `terminal` | н/д (ни одного вызова) | — | ничего | + +Дельта в коде вышла ровно такой, как предсказал промт: значение enum + ветка валидации (`chain` при `allow` — громко) + ветка в проверке `label_models` (запись для не-route лейбла — громко, с указанием, что менять) + примеры. `checkContentRouting` править не пришлось — он уже был action-агностичен (кроме `terminal`), и это подтверждено мутацией (см. §3). + +**2. `accepts_labels` НЕ заполнял** — оставил fail-closed, это ратифицированное верное состояние. Комментарий в `configs/models.yaml` переписан под решённый вокабуляр и под то, ЧЕГО не хватает: значения — это ФАКТЫ ToS/AUP, они (а) берутся только по первоисточникам (правило двух направлений), (б) даются **пер-лейблу**, а не пер-провайдеру целиком — `violence` и `sexually-explicit` у одного вендора разные ответы (ровно на этом стоит D14 п.1), (в) фиксируются полигоном в `experiments/00-provider-quirks.md`, не бэкенд-сессией. Ни одного ToS-утверждения от себя в репо не внёс — по памяти нельзя, а чужую зону занимать не стал. + +**3. Фикс-лист:** коммент `Stage.ReasoningMaxTokens` — вторая половина («ignored … reasoning-off») была неверна после ратифицированной формы 2. Переписан: буфер обязателен для ЛЮБОЙ стадии, чья резолвнутая модель на аддитивном провайдере, **включая `reasoning:"off"`** (off гасит thinking только там, где у модели есть выключатель, поэтому исключение было тихим выходом); подавленный вызов лишь пере-резервирует — потолок ужимается, слепым не становится. + +**Данные (шиппинг):** `configs/pipeline-c1.yaml` — закомментированный пример реестра дополнен формой `allow` и таблицей «что требует каждое действие»; значения в примере СИНТЕТИЧЕСКИЕ, вокабуляр в шиппинг не прибит. + +## §2. Тесты (+4, все обязательные из дельты и сверх) + +- `TestAllowActionAssertsWithoutRouting` — **позитивный** (книга с assert-only лейблом грузится без цепочки и без бюджета, каждая стадия сохраняет свой конфиговый маршрут) **и негативный** (неспособный эндпойнт → громко; инвариант — единственное, что делает эта политика). +- `TestAllowActionRejectsRoutingKnobs` — `chain` при `allow` и `label_models` для allow-лейбла отвергаются, а не игнорируются: автор, написавший их, имел в виду то, чего движок не сделает. +- `TestRatifiedLabelPairIsDataOnly` — форма ратифицированного ответа владельца: ДВА лейбла, один только ассертит, второй пере-маршрутизирует; инвариант применяется к ОБЪЕДИНЕНИЮ (модель, принимающая лишь один из двух, отвергается по имени). Значения синтетические — доказывается ФОРМА, поэтому любой вокабуляр ляжет данными. +- `TestAssertOnlyLabelRunsWithoutMovingAnything` — сквозняк через настоящий драйвер: allow-лейблованный прогон **байт-в-байт совпадает** с нелейблованным (тела запросов сравнены поштучно, стоимость равна, оба волновых снапшота НЕ двинулись), и та же книга без capability отказывается запускаться. Это прямое доказательство, что ассерт не имеет wire-эффекта и не может ничего пере-оплатить. + +## §3. Ревью исполнением (мутации — каждая новая ветка обязана краснеть) + +| мутация | красные тесты | +|---|---| +| инвариант перестаёт применяться к не-route действию (`allow` становится декоративным) | `TestAllowActionAssertsWithoutRouting` · `TestRatifiedLabelPairIsDataOnly` · `TestAssertOnlyLabelRunsWithoutMovingAnything` | +| `chain` при `allow` молча игнорируется | `TestAllowActionRejectsRoutingKnobs` | +| `label_models` для не-route лейбла молча игнорируется | `TestAllowActionRejectsRoutingKnobs` | + +Две правки фикстур были неизбежны и отражают НОВУЮ (корректную) семантику, объявляю их явно: (а) кейс `terminal` теперь снимает и `label_models` (они стали громкой ошибкой при не-route действии — что само проверяется тестом выше); (б) ожидаемая подстрока сообщения об неизвестном действии стала `route|allow|terminal`. + +## §4. Манифест «заявление = команда» + +| # | Заявление | Команда | Результат | +|---|---|---|---| +| D1 | сборка/vet чисты, формат чист | `go build ./... && go vet ./...` · `gofmt -l internal/ cmd/` | без вывода (кроме `internal/llm/llm.go` — состояние HEAD, не моё) | +| D2 | вся сьюта зелёная под `-race` | `go test -race -count=1 ./...` | 12 пакетов `ok`, 0 FAIL | +| D3 | **golden байт-идентичен, пере-капчера нет** | `sha256sum internal/pipeline/testdata/golden/capture.golden` · `git status --porcelain backend/internal/pipeline/testdata/` | `f7641993e37f7ec619dfc5d2914a342d796a7cecd92080e33704f3974f0020f8` · пусто | +| D4 | **парити EXACT** | `TM_MINER_PARITY=1 go test ./internal/miner/ -run Parity -count=1 -v` | `PARITY: n=13618 catastrophe{方源:0 蛊:1 蛊师:2 古月:22} recall@proposed=0.9655 (56/58 GT)` | +| D5 | ценз ИСПОЛНЕНИЕМ: 451 → **455** (+4, −0) | `grep -rn "^func Test" backend \| wc -l` · `git grep -c "^func Test" HEAD -- 'backend/**/*_test.go'` (сумма) · `diff` списков имён | 455 / 451; добавлены ровно 4 названных выше | +| D6 | каждая новая ветка краснеет от мутации | копия дерева в `/tmp`, 3 мутации, `go test ./internal/config/ ./internal/pipeline/` | таблица §3 | +| D7 | вокабуляр в шиппинг НЕ прибит, ToS-факты не выдуманы | `grep -rnE "^\s*(accepts_labels\|content_policy)" backend/configs/` | ни одной раскомментированной строки | +| D8 | движок не знает ни одного значения лейбла | `go test ./internal/config/ -run TestNoPrivilegedLabelValue` | `ok` (книга с «adult» против реестра синтетических лейблов отвергается как любая другая) | +| D9 | сессия не коммитила | `git status --porcelain \| wc -l` | 6 записей, HEAD не двигался | + +## §5. Открытые петли (после дельты) + +1. **`accepts_labels` — ждут ФАКТОВ по первоисточникам, пер-провайдер И ПЕР-ЛЕЙБЛ** (`violence` ≠ `sexually-explicit` у одного вендора). Зона полигона (`experiments/00-provider-quirks.md`). До фактов любая лейблованная книга падает громко, называя модель, провайдера и недостающий лейбл — верное состояние. +2. **Q2 (редактор под лейблом)** — отложен с записью при приёмке; связывает только explicit-книги, которых нет. +3. **D22.7** паком не снят: пер-чанковый L3-скрин остаётся предусловием первой erotica-книги в проде; `action: terminal` — book-level отказ входа по объявленным данным, механизм слабее, значение под него не заведено. +4. Хвосты общности (Detail-строки чекеров · множитель ×2) — «отложено-с-записью», причина в отчёте фазы 2 §3.3 (двинули бы снапшот стендовых книг). +5. `--accept-rebill` (D20.2-Q2) — по-прежнему отсутствует в коде; петля не пака-17.