Land bank fix-pack as D39.75: head finality in data, honest batch comment, classify fail-loud, neuter mutation tests, series fixture, tokenizer consolidation, soft-sign rollback kept with nails

This commit is contained in:
Claude (backend session) 2026-08-01 17:36:43 +03:00
parent 1e3a1cae82
commit 78b7b3480b
24 changed files with 835 additions and 133 deletions

View file

@ -365,6 +365,18 @@ func (c *Checkers) countSourceReplies(source string) int {
return n
}
// DECL-AWARE STEMMING IS DELIBERATELY NOT WIRED INTO THESE MATCHERS (fix-pack §е, D39.71). The bank's
// stemmer (lang.TargetStemmer) is the right tool for the POST-CHECK, where a miss is uniformly the safe
// direction (a false omission flag) — and where character NAMES already arrive declension-aware, because
// Forms carries the base dst plus its enumerated decl forms (the same decl.forms[] the bank uses). Stemming
// on top of that would only add stem-collision MIS-ATTRIBUTION risk to a checker whose whole doctrine is
// precision over recall. For SelfRef/NG (single surfaces) the accept-biased stemmer is not uniformly safe
// either: stemming SelfRef would suppress a false self-ref flag on a declined designation (safe), but stemming
// an NG lexeme would EXPAND the forbidden-word match (a recall boost that manufactures noise) — opposite
// directions on one checker. A declension-aware SelfRef/NG belongs in the DRIVER (enumerate forms from the
// seed, as Forms already are), not in a match-time stemmer whose safe direction flips per axis. Recorded here
// so the decision stays closed rather than silently re-opened.
// wholeWord reports whether form occurs in the normalized haystack bounded by non-word runes.
func wholeWord(hay []rune, form string) bool {
f := []rune(form)

View file

@ -1088,6 +1088,13 @@ func LoadPipeline(path string, models *Models, pair string, labels []string) (*P
}
}
}
} else if tg.ClassifyTypes {
// The classifier is a PHASE of the terminology gate — loadClassifierTemplate is reached only through
// loadTerminologyTemplate, and runClassifier only through runTerminologist, both of which return early
// when the gate is off. So classify_types under a DISABLED gate never fires and, until now, was never
// validated either: a silent no-op, the "gate that cannot fire" class this loader rejects everywhere
// else. Fail loud instead of running with a phase the operator believes is on.
bad("gates.terminology.classify_types is on but gates.terminology.enabled is false — the classifier is a phase of the terminology gate and never runs on its own; enable the gate, or drop classify_types")
}
if len(problems) > 0 {
return nil, fmt.Errorf("config %s:\n - %s", path, strings.Join(problems, "\n - "))

View file

@ -0,0 +1,117 @@
package config
import (
"path/filepath"
"strings"
"testing"
)
// terminology_classify_test.go pins the fix-pack §г config branches for the §2 classifier PHASE. The
// classifier is a phase of the terminology gate, so — like the render phase — an enabled phase must be able to
// fire (a resolvable non-additive model, a positive budget, its own prompt), and classify_types under a
// DISABLED gate is a loud error rather than the silent no-op it was (runClassifier is reached only through the
// enabled gate).
// termProject writes a zh-ru pack with a terminologist prompt and, optionally, a classifier prompt.
func termProject(t *testing.T, withClassifier bool) string {
t.Helper()
dir := promptProject(t, "zh-ru")
body := "sys\n---USER---\n{{text}}"
writeTmp(t, filepath.Join(dir, "prompts", "zh-ru", "terminologist.md"), body)
if withClassifier {
writeTmp(t, filepath.Join(dir, "prompts", "zh-ru", "classifier.md"), body)
}
return dir
}
// loadClassify assembles a config with the given terminology-gate body and loads it for zh-ru.
func loadClassify(t *testing.T, withClassifier bool, gateBody string) error {
t.Helper()
dir := termProject(t, withClassifier)
cfg := draftOnlyConfig(t, dir, gateBody)
_, err := LoadPipeline(cfg, miniModels(t), "zh-ru", nil)
return err
}
const validClassifyGate = `
gates:
terminology:
enabled: true
model: fake
budget_usd: 1
target_script: Cyrillic
classify_types: true
classify_budget_usd: 0.5
`
func TestClassifyValidConfigLoads(t *testing.T) {
if err := loadClassify(t, true, validClassifyGate); err != nil {
t.Fatalf("a well-formed classify config must load: %v", err)
}
}
func TestClassifyMissingPromptFailsLoud(t *testing.T) {
err := loadClassify(t, false, validClassifyGate) // no classifier.md written
if err == nil {
t.Fatal("classify_types on with no classifier.md must fail loud")
}
for _, want := range []string{"classify_types", filepath.Join("zh-ru", "classifier.md")} {
if !strings.Contains(err.Error(), want) {
t.Errorf("error must name %q, got: %v", want, err)
}
}
}
func TestClassifyBudgetMustBePositive(t *testing.T) {
body := `
gates:
terminology:
enabled: true
model: fake
budget_usd: 1
target_script: Cyrillic
classify_types: true
classify_budget_usd: 0
`
err := loadClassify(t, true, body)
if err == nil || !strings.Contains(err.Error(), "classify_budget_usd") {
t.Fatalf("a zero classify budget must fail loud (a phase that can never spend), got: %v", err)
}
}
func TestClassifyModelMustExist(t *testing.T) {
body := `
gates:
terminology:
enabled: true
model: fake
budget_usd: 1
target_script: Cyrillic
classify_types: true
classify_budget_usd: 0.5
classify_model: ghost
`
err := loadClassify(t, true, body)
if err == nil || !strings.Contains(err.Error(), "classify_model") {
t.Fatalf("an unknown classify_model must fail loud, got: %v", err)
}
}
// The fix-pack §г hygiene: classify_types under a disabled gate was a silent no-op — now a loud error.
func TestClassifyTypesRequiresEnabledGate(t *testing.T) {
body := `
gates:
terminology:
enabled: false
classify_types: true
`
err := loadClassify(t, true, body)
if err == nil {
t.Fatal("classify_types with the gate disabled must fail loud, not run as a silent no-op")
}
for _, want := range []string{"classify_types", "enabled"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("error must name %q, got: %v", want, err)
}
}
}

View file

@ -0,0 +1,10 @@
# Series HEAD position per writing SCRIPT (bank-quality §1, fix-pack §б, D39.71): where a dense script's
# rune-morpheme SERIES puts the shared generic HEAD — LAST (modifier-head, the CJK norm: 甲等/乙等/丙等 share
# the trailing 等) or FIRST. Read by lang.SeriesMorphology so the terminologist co-batches a grade/rank series
# head-consistently with NO Go branch; head-finality was a hardcoded Go constant before this. A dense script
# (one named in cjkScriptNames) with no row here defaults to head-FINAL, the CJK-class default — so declaring
# a new dense script head-INITIAL is one data row, not a Go edit. `head_position<TAB>scriptname<TAB>final|initial`.
head_position han final
head_position hiragana final
head_position katakana final
head_position hangul final

View file

@ -228,3 +228,10 @@ decl_suffix е
decl_suffix ы
decl_suffix и
decl_suffix о
# NB (fix-pack §а, D39.71): the bare SOFT SIGN «ь» is deliberately NOT a decl_suffix. It would close the
# Palladius soft-sign case («Фан Юаню»~«Фан Юань» via the stem «юан»), but that strip is byte-identical in
# shape to a HARMFUL collision — «Синь»/«Линь» (top zh surnames) collapse onto «син»/«лин», the stems of the
# common words «синий»/«линия», which fires a spurious Bank.SpoilerLeaks false-positive (the noise direction).
# «юань»→«юан» and «синь»→«син» are the same 4-rune-soft-sign→3-rune-stem shape, so no min-stem rule separates
# them. The soft-sign nominative gap needs seed decl-form enumeration or an anchor-gated match, not this global
# suffix — see the fix-pack report §а ping.

View file

@ -19,7 +19,7 @@ import (
// data/algorithm boundary holds; embedded so it is available without a book's langpack_root. Each file is
// SECTIONED per language/target, so adding a language = add a section, removing one = delete its section.
//go:embed data/cjk-section.txt data/refusal.txt data/injection.txt data/sentence-abbrev.txt data/target-ru.txt data/terminator.txt data/lang-script.txt
//go:embed data/cjk-section.txt data/refusal.txt data/injection.txt data/sentence-abbrev.txt data/target-ru.txt data/terminator.txt data/lang-script.txt data/script-series.txt
var embeddedFS embed.FS
// embedAlgoVersion tags the embedded-data content-hash RECIPE (like packAlgoVersion tags the pack's). Bump

View file

@ -83,16 +83,81 @@ func IsCJKScriptLang(lng string) bool {
return false
}
var (
scriptSeriesOnce sync.Once
scriptHeadFinal map[string]bool // script name → head-final (present only for scripts with a data row)
)
// loadScriptSeries parses the embedded script-series table once (script name → head-final flag).
func loadScriptSeries() {
scriptSeriesOnce.Do(func() {
m, err := parseScriptHeadFinal(mustEmbed("data/script-series.txt"))
if err != nil {
panic(fmt.Sprintf("lang: embedded script-series.txt is corrupt: %v", err))
}
scriptHeadFinal = m
})
}
// parseScriptHeadFinal reads `head_position<TAB>scriptname<TAB>final|initial` rows into script name →
// head-final. Pure, so a test can drive it with a synthetic table and prove a new script's head direction is
// DATA, not a Go edit. An unknown category or position value is a corrupt-data error, never silent.
func parseScriptHeadFinal(b []byte) (map[string]bool, error) {
out := map[string]bool{}
for i, raw := range strings.Split(string(b), "\n") {
t := strings.TrimSpace(strings.TrimRight(raw, "\r"))
if t == "" || strings.HasPrefix(t, "#") {
continue
}
f := strings.Split(t, "\t")
if len(f) != 3 || strings.TrimSpace(f[0]) != "head_position" {
return nil, fmt.Errorf("line %d: want `head_position<TAB>scriptname<TAB>final|initial`, got %q", i+1, t)
}
name := strings.TrimSpace(f[1])
switch strings.TrimSpace(f[2]) {
case "final":
out[name] = true
case "initial":
out[name] = false
default:
return nil, fmt.Errorf("line %d: position must be final|initial, got %q", i+1, f[2])
}
}
return out, nil
}
// SeriesHeadFinal reports whether a dense SCRIPT forms head-FINAL series (the shared generic head is the
// trailing rune(s)). It is DECLARED data (data/script-series.txt); a dense script with no row defaults to
// head-final, the CJK modifier-head norm — so a dense script that co-batches head-INITIALLY is added by a
// data row, with no Go edit. Exported so the data-drive is testable without routing through a source language.
func SeriesHeadFinal(scriptName string) bool {
loadScriptSeries()
if hf, ok := scriptHeadFinal[scriptName]; ok {
return hf
}
return true
}
// SeriesMorphology reports whether a SOURCE language forms rune-morpheme SERIES the terminologist should
// co-batch (甲等/乙等/丙等 — grades sharing the head 等), and whether that shared head is final. Series apply
// to DENSE (CJK) scripts, where one rune ≈ one morpheme so a single-rune difference is a real minimal pair;
// in an alphabetic source care/core differ in one letter by coincidence, so the channel stays off. CJK noun
// compounds are modifier-HEAD, so the generic word is the trailing rune(s): head-final. A linguistic constant
// like cjkScriptNames, not a pair/book branch — the one place to revisit if a non-head-final dense script is
// ever added.
// in an alphabetic source care/core differ in one letter by coincidence, so the channel stays off. Head
// finality is no longer a Go constant: it is resolved from the language's dense script(s) via SeriesHeadFinal
// (data), so a dense non-head-final script needs no Go edit — headFinal is true only when EVERY dense script
// of the language is head-final (they agree for zh/ja/ko; any head-initial dense script flips it).
func SeriesMorphology(lng string) (enabled, headFinal bool) {
if IsCJKScriptLang(lng) {
return true, true
loadLangScripts()
headFinal = true
for name := range langScriptBy[strings.ToLower(strings.TrimSpace(lng))] {
if cjkScriptNames[name] {
enabled = true
if !SeriesHeadFinal(name) {
headFinal = false
}
}
}
return false, false
if !enabled {
return false, false
}
return true, headFinal
}

View file

@ -24,3 +24,52 @@ func TestSecondPairDryRun(t *testing.T) {
t.Fatal("ko/en must resolve scripts — these are the fixed blind spots")
}
}
// TestSeriesMorphologyUnchanged pins that moving head-finality into data (fix-pack §б) reproduces the prior
// Go-constant behaviour: every dense source co-batches head-final, every alphabetic source has the channel off.
func TestSeriesMorphologyUnchanged(t *testing.T) {
for _, tc := range []struct {
lang string
enabled, headFinal bool
}{
{"zh", true, true}, {"ja", true, true}, {"ko", true, true},
{"en", false, false}, {"ru", false, false}, {"unknown", false, false},
} {
en, hf := SeriesMorphology(tc.lang)
if en != tc.enabled || hf != tc.headFinal {
t.Errorf("SeriesMorphology(%q) = (%v,%v), want (%v,%v)", tc.lang, en, hf, tc.enabled, tc.headFinal)
}
}
// The real embedded data must declare han head-final, or zh would silently flip.
if !SeriesHeadFinal("han") {
t.Fatal("data/script-series.txt must declare han head-final")
}
}
// TestSeriesHeadFinalIsDataDriven is the fix-pack §б generality proof: a dense script's head direction comes
// from DATA, so declaring a dense NON-head-final script is a data row, not a Go edit. Driven through the pure
// parser with a synthetic table so it needs no real embedded script.
func TestSeriesHeadFinalIsDataDriven(t *testing.T) {
m, err := parseScriptHeadFinal([]byte(
"# synthetic\nhead_position\ttengwar\tinitial\nhead_position\tcirth\tfinal\n"))
if err != nil {
t.Fatalf("valid table must parse: %v", err)
}
if m["tengwar"] {
t.Fatal("a script declared `initial` must resolve head-INITIAL from data with no Go edit")
}
if !m["cirth"] {
t.Fatal("a script declared `final` must resolve head-final")
}
// An unlisted script defaults to head-final (the CJK modifier-head norm) through the exported resolver.
if !SeriesHeadFinal("no-such-script-xyz") {
t.Fatal("an unlisted script must default to head-final")
}
// Corrupt data is loud, never silent.
if _, err := parseScriptHeadFinal([]byte("head_position\than\tsideways\n")); err == nil {
t.Fatal("an invalid head position must be a parse error")
}
if _, err := parseScriptHeadFinal([]byte("wrong_cat\than\tfinal\n")); err == nil {
t.Fatal("an unknown category must be a parse error")
}
}

View file

@ -3,7 +3,8 @@ package lang
import (
"sort"
"strings"
"unicode"
"textmachine/backend/internal/text"
)
// stemmer.go: the conservative TARGET stemmer (bank-quality §3, primitive C). It strips one inflectional
@ -79,23 +80,6 @@ func (s TargetStemmer) SameStem(a, b string) bool {
// TokenizeWords splits a target string into its whole words (maximal letter runs), lower-cased. Script-
// generic (any Unicode letter), so it needs no per-language branch. Used to match a declined multi-word term
// head against the output.
func TokenizeWords(s string) []string {
var out []string
var cur []rune
flush := func() {
if len(cur) > 0 {
out = append(out, string(cur))
cur = cur[:0]
}
}
for _, r := range strings.ToLower(s) {
if unicode.IsLetter(r) {
cur = append(cur, r)
continue
}
flush()
}
flush()
return out
}
// head against the output. It delegates to text.TokenizeLetters — the ONE tokenizer, shared with the target
// lexical checkers' TokenizeScript, so a stress-accented output word no longer splits mid-word here either.
func TokenizeWords(s string) []string { return text.TokenizeLetters(s) }

View file

@ -30,6 +30,21 @@ func TestTargetStemmerRuDeclension(t *testing.T) {
}
}
// TestSoftSignNamesDoNotCollideWithCommonWords is the fix-pack §а REGRESSION GUARD: a bare «ь» decl_suffix was
// weighed to close the Palladius soft-sign case («Фан Юаню»~«Фан Юань») and REJECTED, because stripping «ь»
// collapses top zh surnames onto common-adjective stems — «Синь/Линь» (辛/信/新, 林) → «син/лин» = «синий/линия».
// That fires a spurious Bank.SpoilerLeaks false-positive (the noise direction). This guard fails loudly if
// «ь» is ever re-added to the registry, so the collision cannot silently return.
func TestSoftSignNamesDoNotCollideWithCommonWords(t *testing.T) {
s := NewTargetStemmer(TargetChecksFor("ru"))
for _, pair := range [][2]string{{"Синь", "синий"}, {"Синь", "синим"}, {"Линь", "линию"}, {"Тень", "теней"}} {
if s.SameStem(pair[0], pair[1]) {
t.Errorf("«%s» (a soft-sign name) must NOT share a stem with the common word «%s» — a bare «ь» decl_suffix was re-added and re-introduced the collision (%q vs %q)",
pair[0], pair[1], s.Stem(pair[0]), s.Stem(pair[1]))
}
}
}
func TestTargetStemmerInertWithoutRegistry(t *testing.T) {
var s TargetStemmer // zero value = a target with no decl_suffix data
if s.Enabled() {

View file

@ -30,6 +30,13 @@ func TestDstFormPresentAcceptsDeclension(t *testing.T) {
if !present(t, multi, "они стояли у горы Цинмао", s) {
t.Fatal("a multi-word term with the head inflected must be accepted (горы Цинмао ~ гора Цинмао)")
}
// Fix-pack §а: the Palladius soft-sign case («Фан Юаню»~«Фан Юань») is NOT closed here — a bare «ь»
// decl_suffix that would close it also collapses «Синь»/«Линь» onto «синий»/«линия» (a SpoilerLeaks
// false-positive), so it was rejected. The soft-sign gap is left for seed decl-enumeration; this asserts
// the current honest behaviour (an oblique soft-sign name is NOT matched) so a naive re-add is visible.
if present(t, &entry{dst: "Синь"}, "небо было синим", s) {
t.Fatal("«Синь» must NOT match «синим» — a bare «ь» decl_suffix re-introduced the collision (fix-pack §а)")
}
}
// TestDstFormPresentInertWithoutStemmer guards the generality path: a target with no decl_suffix registry

View file

@ -0,0 +1,50 @@
package membank
import (
"testing"
"textmachine/backend/internal/store"
)
// memneuter_test.go pins the fix-pack §д money contract of the §3 neuter directive, on the memvoice pin's
// pattern: gender:neuter now RENDERS a directive where the same row rendered nothing before, so it MUST move
// memory_version (a loud, re-pinnable --resnapshot); and the tag respects the mined scope exactly as the
// pack-19 voice fold does, so a mined-only neuter row never re-bills the draft wave that cannot see it.
//
// (The neuter-directive-v1 marker itself is not black-box-isolable from the per-row gender field, which is
// already folded — both move the hash for a neuter row, and no single code version can hold one constant
// while toggling the other. These tests pin the OBSERVABLE contract; the marker exists to carry the RENDER
// revision across the deploy that introduced neuter rendering, which the scope test's line-ordering guards.)
func TestNeuterRowMovesTheBankVersion(t *testing.T) {
base := term("元气", "изначальная ци")
plain := ComputeVersionScopedIn(BankInput{Rows: []store.GlossaryEntry{base}}, false, false)
neut := base
neut.Gender = "neuter"
if withNeuter := ComputeVersionScopedIn(BankInput{Rows: []store.GlossaryEntry{neut}}, false, false); plain == withNeuter {
t.Fatal("a gender:neuter row left the bank version unchanged — it now renders a directive the snapshot cannot see, a silent re-payment")
}
}
// TestNeuterFoldRespectsTheMinedScope mirrors TestVoiceFoldRespectsTheMinedScopeBeforeCountingIt: the tag
// condition is evaluated only AFTER the mined-exclusion filter, so a mined-only neuter row folds into the
// enriched (edit-wave) version and NEVER the base (draft-wave) one — the draft wave never renders a mined
// row's directive, so it must not re-pay for one.
func TestNeuterFoldRespectsTheMinedScope(t *testing.T) {
minedNeuter := term("元气", "изначальная ци")
minedNeuter.Source, minedNeuter.Gender = "mined", "neuter"
minedPlain := minedNeuter
minedPlain.Gender = ""
withNeuter := BankInput{Rows: []store.GlossaryEntry{term("方源", "Фан Юань"), minedNeuter}}
plain := BankInput{Rows: []store.GlossaryEntry{term("方源", "Фан Юань"), minedPlain}}
// BASE: the mined neuter row is out of scope, so neither its gender field nor the tag folds → identical.
if got, want := ComputeVersionScopedIn(withNeuter, false, true), ComputeVersionScopedIn(plain, false, true); got != want {
t.Fatalf("a mined-only neuter row moved the BASE version — the draft wave would re-pay for a directive it never renders\n got %s\n want %s", got, want)
}
// ENRICHED: the mined neuter row IS in scope, so the version moves — a loud re-snapshot for the wire the
// edit wave sees.
if ComputeVersionScopedIn(withNeuter, false, false) == ComputeVersionScopedIn(plain, false, false) {
t.Fatal("a mined neuter row did NOT move the enriched version — the edit wave would miss the directive")
}
}

View file

@ -269,6 +269,16 @@ func (r *Runner) runTerminologist(ctx context.Context, snapID string, cands []te
seriesID := terminology.DetectSeries(cands, terminology.SeriesParams{Enabled: sEnabled, HeadFinal: headFinal})
batches := terminology.Batch(cands, batchRunes, seriesID)
res.Batches = len(batches)
// §1 keeps a series (or a lone candidate) WHOLE even past the budget — splitting a series would defeat the
// one-head co-batching it exists for — so a co-batched grade set or an evidence-heavy single term can render
// over the cap. That is deliberate but NOT silent: an over-cap unit strains the model's output ceiling (the
// cap-8000 mine), so name it while the run can still be watched.
for i, b := range batches {
if br := terminology.BatchRunes(b); br > batchRunes {
r.Log.WarnContext(ctx, "terminology: a batch renders OVER the size budget and is sent WHOLE (a series is co-batched by design, §1; a lone candidate cannot be split) — watch the model's output cap on this call",
"book", r.Book.BookID, "batch", i, "terms", len(b), "batch_runes", br, "budget_runes", batchRunes)
}
}
// The signed bank is read ONCE for the whole role, not per batch: it is the same law for every batch.
canon := r.approvedNeighbours()

View file

@ -1,8 +1,8 @@
==== run 1 (fresh) ====
snapshot_draft: dc7572ca82f1898969220aecbb5727c36b9a42486ee70237ae8f95b8fe9ebce4
snapshot_draft_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-37bf9699835f","memory_version":"bd451833d4ebb8a0ca07caf8deda76f027e1f42b78654987a22d215acf955eb7","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"draft","role":"translator","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"26d0245a56d180eafd200ebe5311aab2488142c4a98dcddee21ea457814aa21a","temperature":0.3,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000},"escalate_to":"fake-fallback","escalate_capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":6000}}]}
snapshot_edit: 6799e44bbf02e21419447613ec3915f9f47c61f2746b94c1fc898ec9f5aa70bf
snapshot_edit_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-37bf9699835f","memory_version":"0a48d6ed9409de09f514af91aebdd622637621c9dc9464e8898c238791033d91","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"edit","role":"editor","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"6a8e7f71139d6bc09a95159d1cb895e05b56d2f2f7417c5e20a7073c517c79f6","temperature":0.4,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000}}]}
snapshot_draft: 8e1f174e988d99786c177f152966aaf9a116e67b55bdf9329cefde45701bdcdf
snapshot_draft_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-3df4329e3b9e","memory_version":"bd451833d4ebb8a0ca07caf8deda76f027e1f42b78654987a22d215acf955eb7","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"draft","role":"translator","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"26d0245a56d180eafd200ebe5311aab2488142c4a98dcddee21ea457814aa21a","temperature":0.3,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000},"escalate_to":"fake-fallback","escalate_capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":6000}}]}
snapshot_edit: b968f0f3ab875824174e199e5ea9135f4a799a54d394ee1b5f5899e92d97ce3c
snapshot_edit_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-3df4329e3b9e","memory_version":"0a48d6ed9409de09f514af91aebdd622637621c9dc9464e8898c238791033d91","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"edit","role":"editor","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"6a8e7f71139d6bc09a95159d1cb895e05b56d2f2f7417c5e20a7073c517c79f6","temperature":0.4,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000}}]}
brief_hash: c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df
memory_version: 0a48d6ed9409de09f514af91aebdd622637621c9dc9464e8898c238791033d91
base_memory_version: bd451833d4ebb8a0ca07caf8deda76f027e1f42b78654987a22d215acf955eb7
@ -51,42 +51,42 @@ chunk ch8/0 disposition=flagged flag="sanitizer_stripped" final_text="Судзу
stage=edit role=editor model=fake-model resume=false disp=flagged flag="sanitizer_stripped" attempts=1 escalated=false esc_model="" finish="stop" cum_usd=0.00182 detail="CJK leak in the ru output: 特产"
stage_text="" recovered="Судзуки нашёл древний камень на каменном алтаре."
-- chunk_status --
ch1/0 draft snap_match=true content_hash=3e156226b9a74aa70c8a259c62e3d2fe68486a96d2178d31084c1e38a3256241 disp=ok flag="" attempts=1 final_hash=70bca3400d2d8d848af27845cb2b86f362cdcf7a3a73e2611c46ec05feb7c4c2 cost=0.00182 escalated=false esc_model="" detail=""
ch1/0 edit snap_match=true content_hash=5e79dd5cbd310ac01d71ca258002beca18db715dfb8b1a08907508e506595355 disp=ok flag="" attempts=1 final_hash=799b6e6419910a2e52dd4652f2bde9bee59fbdb92a849f291ed33a5e30410a15 cost=0.00182 escalated=false esc_model="" detail=""
ch1/1 draft snap_match=true content_hash=82fb7f985de6c07182e8c09a3ceaad94f2e63745b87024087e437fba762b8227 disp=ok flag="" attempts=1 final_hash=ad47812002136f356e16d4fe4d71b73fdf1f2fbbe4f6a2cecd696abb07b7502d cost=0.00182 escalated=false esc_model="" detail=""
ch2/0 draft snap_match=true content_hash=cfab1e09264a3a30aba95e7da1d6a269468c6aa0c1b334d9241e78bc266a3571 disp=ok flag="" attempts=1 final_hash=35d5f692c908d3c15b40757a167c0deb3067e90492a4490500b0a0ccf359c9e2 cost=0.00728 escalated=true esc_model="fake-fallback" detail=""
ch2/0 edit snap_match=true content_hash=55ffd839a4a26c03cf57e08000ab2c84b267e1429ab4a8e212d3a01fda757632 disp=ok flag="" attempts=1 final_hash=e3f8acb903b0dd5798636597412acadfcf03a968ff049a8604b1142dea5cded1 cost=0.00182 escalated=false esc_model="" detail=""
ch1/0 draft snap_match=true content_hash=3e156226b9a74aa70c8a259c62e3d2fe68486a96d2178d31084c1e38a3256241 disp=ok flag="" attempts=1 final_hash=58e9dbc06bfa575bbb3e1e108d565a8a48a2e07dbcc1b14633adf1b20c33bf21 cost=0.00182 escalated=false esc_model="" detail=""
ch1/0 edit snap_match=true content_hash=5e79dd5cbd310ac01d71ca258002beca18db715dfb8b1a08907508e506595355 disp=ok flag="" attempts=1 final_hash=366482169be171810b4478c7a10245779c921afdd0daf3c443a60495bf2ef737 cost=0.00182 escalated=false esc_model="" detail=""
ch1/1 draft snap_match=true content_hash=82fb7f985de6c07182e8c09a3ceaad94f2e63745b87024087e437fba762b8227 disp=ok flag="" attempts=1 final_hash=f62d390f0624ddcfbc27b048475b749d06b44ab91ade175985b162160235f6e7 cost=0.00182 escalated=false esc_model="" detail=""
ch2/0 draft snap_match=true content_hash=cfab1e09264a3a30aba95e7da1d6a269468c6aa0c1b334d9241e78bc266a3571 disp=ok flag="" attempts=1 final_hash=afc9e9ff7d6f1e4832365eefe29133bc8b8f70277fea9d8379d41a490c47f35c cost=0.00728 escalated=true esc_model="fake-fallback" detail=""
ch2/0 edit snap_match=true content_hash=55ffd839a4a26c03cf57e08000ab2c84b267e1429ab4a8e212d3a01fda757632 disp=ok flag="" attempts=1 final_hash=a57b6f62ebecf7cbdb04d8fce7679b68dedb4cb95dcac2605942aa3fdc864e05 cost=0.00182 escalated=false esc_model="" detail=""
ch3/0 draft snap_match=true content_hash=21df44eb104900664bdaaeb2f716f51dcddbc81a71d845f6ba0f91c5bc4b2cfb disp=flagged flag="hard_refusal" attempts=1 final_hash= cost=0.00728 escalated=true esc_model="" detail="provider finish_reason=refusal"
ch3/0 edit snap_match=true content_hash= disp=skipped flag="hard_refusal" attempts=0 final_hash= cost=0 escalated=false esc_model="" detail="skipped: a member draft chunk of this edit unit was flagged (hard_refusal)"
ch4/0 draft snap_match=true content_hash=7aabdcb89dea365c95363b2662144ca1a10b307d377f47a8e7cc19b5b872d287 disp=ok flag="" attempts=1 final_hash=c74a4b18be42dae5946fc9d33f1172e6be71a37bad7dc08076e5fba59316e858 cost=0.00182 escalated=false esc_model="" detail=""
ch4/0 edit snap_match=true content_hash=2854dfe1648b0cf0cbb683cb7ce94c5eedc9223b2ce20c99dc008c7add6317ef disp=ok flag="" attempts=1 final_hash=5f8f5737a4e02766f8500915341c8b8a8827f8a05b03d77a746a4a8bc48806eb cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 draft snap_match=true content_hash=2ed0a97f39cc5c57c62c3324f4646357372f26e3c5bae1773f24edf9449fc013 disp=ok flag="" attempts=1 final_hash=407f023c09e54b0399f79cf59e893817065b28129a807c57b25cc796202d75e1 cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 edit snap_match=true content_hash=b145d78e4d30a39cc4088a1ddb8d934234c65341c62c79545c2eff1e7ae0c3d3 disp=ok flag="" attempts=1 final_hash=2b722376f3691a64c38d9a8026f567dfce0dd0a5ca3d2355bd2a95e4807ee67c cost=0.00182 escalated=false esc_model="" detail=""
ch6/0 draft snap_match=true content_hash=5f396d299a7ec5b10f3c8591e54ce7d84c4d31b815e561b0dd5f56635aef5a71 disp=ok flag="" attempts=1 final_hash=afd2169fce4c9aaecdf7f3de1f1f87bc08d5b672d19bc1dc772991f8d3321d60 cost=0.00182 escalated=false esc_model="" detail=""
ch4/0 draft snap_match=true content_hash=7aabdcb89dea365c95363b2662144ca1a10b307d377f47a8e7cc19b5b872d287 disp=ok flag="" attempts=1 final_hash=95953ad9afa465ccc21cf6ae02556fcc2259593e51fb7d2a50cd24741cedf2d3 cost=0.00182 escalated=false esc_model="" detail=""
ch4/0 edit snap_match=true content_hash=2854dfe1648b0cf0cbb683cb7ce94c5eedc9223b2ce20c99dc008c7add6317ef disp=ok flag="" attempts=1 final_hash=51a33a39df9ac10b3de8111d4e6c14c97e52d694d1853d0564a49f081017ffdf cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 draft snap_match=true content_hash=2ed0a97f39cc5c57c62c3324f4646357372f26e3c5bae1773f24edf9449fc013 disp=ok flag="" attempts=1 final_hash=df9779e5705e186c4e936d4a1cbdb0bc66d603954bc7dd5ca2b7a4b06c2c9d0d cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 edit snap_match=true content_hash=b145d78e4d30a39cc4088a1ddb8d934234c65341c62c79545c2eff1e7ae0c3d3 disp=ok flag="" attempts=1 final_hash=d331964b915e5496a8c377189df897bdd5c3535e09f9ff4382e75eee7073c681 cost=0.00182 escalated=false esc_model="" detail=""
ch6/0 draft snap_match=true content_hash=5f396d299a7ec5b10f3c8591e54ce7d84c4d31b815e561b0dd5f56635aef5a71 disp=ok flag="" attempts=1 final_hash=639d1fdb559d75d1ff72f73d72820e9aac3ff58f6b867408058f6f0761184155 cost=0.00182 escalated=false esc_model="" detail=""
ch6/0 edit snap_match=true content_hash=592353321cc8000e792e0897446e4dd4f88a0280b9368b62cbd5c82b0c433193 disp=flagged flag="sanitizer_defect" attempts=1 final_hash= cost=0.00182 escalated=false esc_model="" detail="leading service preamble: Вот перевод фрагмента:"
ch7/0 draft snap_match=true content_hash=15c3ebbd56bb19880202d35b0206357655da245343376492f7ceb61808c8a5be disp=ok flag="" attempts=1 final_hash=486facff54a77c86f44cd322200caf4cf982975eeda552fa6dfdee4aa8df0904 cost=0.00182 escalated=false esc_model="" detail=""
ch7/0 edit snap_match=true content_hash=0b2674ed9829b2e6526ff41a60dd280b6670c539476526bbe835d9bf7a6be4c4 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:def877b81bcdc52e2525fdcaae04b8c64f632c7d6747b31df7f4c7f1ad176e73 cost=0.00182 escalated=false esc_model="" detail="markdown header in the output: ### Глава 7"
ch8/0 draft snap_match=true content_hash=4a420977cc34ce738b4978e6a1baf45d78d773a135070c3b7f8c723adfa57d7b disp=ok flag="" attempts=1 final_hash=070d13df73e028346b69abe69253a999f109d92b01f39f3dfada0772be50e185 cost=0.00182 escalated=false esc_model="" detail=""
ch8/0 edit snap_match=true content_hash=c851100c8d6785f77d68f8127472790ae9d0408b7fec44785d3ff9d46842c6f8 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:5095da45458381184b8798287c934e133c39668fed86c50cce921a1cb7df7893 cost=0.00182 escalated=false esc_model="" detail="CJK leak in the ru output: 特产"
ch7/0 draft snap_match=true content_hash=15c3ebbd56bb19880202d35b0206357655da245343376492f7ceb61808c8a5be disp=ok flag="" attempts=1 final_hash=64cd75a5c6410ff1d7c1880dd0ca06c871e517e9cef2ca7923a01fbcd88821ef cost=0.00182 escalated=false esc_model="" detail=""
ch7/0 edit snap_match=true content_hash=0b2674ed9829b2e6526ff41a60dd280b6670c539476526bbe835d9bf7a6be4c4 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:93ae4ce8992136692f558f136d12483aff728b1a0aa54388072335b4a0231267 cost=0.00182 escalated=false esc_model="" detail="markdown header in the output: ### Глава 7"
ch8/0 draft snap_match=true content_hash=4a420977cc34ce738b4978e6a1baf45d78d773a135070c3b7f8c723adfa57d7b disp=ok flag="" attempts=1 final_hash=e9c1dd1174091275bf8f2975f1238d53b0b557ea1fbef283729a36dfa36f5f0d cost=0.00182 escalated=false esc_model="" detail=""
ch8/0 edit snap_match=true content_hash=c851100c8d6785f77d68f8127472790ae9d0408b7fec44785d3ff9d46842c6f8 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:e6a81289fc023f94c28749e97eff499ec5222ed807b4a3c10934dbbcfe4a0ffc cost=0.00182 escalated=false esc_model="" detail="CJK leak in the ru output: 特产"
-- request_log (insertion order) --
ch1/0 draft role=translator req=fake-model actual=fake-model hash=70bca3400d2d8d848af27845cb2b86f362cdcf7a3a73e2611c46ec05feb7c4c2 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/1 draft role=translator req=fake-model actual=fake-model hash=ad47812002136f356e16d4fe4d71b73fdf1f2fbbe4f6a2cecd696abb07b7502d tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-model actual=fake-model hash=56b4390260a43c1ffd2ba17afb8c3fab16113992856bb960e62e11c4937e0028 tm_hit=0 ok=0 finish="stop" degraded="cjk_artifact" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-fallback actual=fake-fallback hash=35d5f692c908d3c15b40757a167c0deb3067e90492a4490500b0a0ccf359c9e2 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-model actual=fake-model hash=9e6325591eca8bc28e34be1da64106704d9589193c991f6545b22e8ddbae3b54 tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-fallback actual=fake-fallback hash=544c921f47e59718019cfff42782f590deba18352123ae0687ceade0130fe2bd tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch4/0 draft role=translator req=fake-model actual=fake-model hash=c74a4b18be42dae5946fc9d33f1172e6be71a37bad7dc08076e5fba59316e858 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 draft role=translator req=fake-model actual=fake-model hash=407f023c09e54b0399f79cf59e893817065b28129a807c57b25cc796202d75e1 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 draft role=translator req=fake-model actual=fake-model hash=afd2169fce4c9aaecdf7f3de1f1f87bc08d5b672d19bc1dc772991f8d3321d60 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 draft role=translator req=fake-model actual=fake-model hash=486facff54a77c86f44cd322200caf4cf982975eeda552fa6dfdee4aa8df0904 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 draft role=translator req=fake-model actual=fake-model hash=070d13df73e028346b69abe69253a999f109d92b01f39f3dfada0772be50e185 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/0 edit role=editor req=fake-model actual=fake-model hash=799b6e6419910a2e52dd4652f2bde9bee59fbdb92a849f291ed33a5e30410a15 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 edit role=editor req=fake-model actual=fake-model hash=e3f8acb903b0dd5798636597412acadfcf03a968ff049a8604b1142dea5cded1 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch4/0 edit role=editor req=fake-model actual=fake-model hash=5f8f5737a4e02766f8500915341c8b8a8827f8a05b03d77a746a4a8bc48806eb tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 edit role=editor req=fake-model actual=fake-model hash=2b722376f3691a64c38d9a8026f567dfce0dd0a5ca3d2355bd2a95e4807ee67c tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 edit role=editor req=fake-model actual=fake-model hash=806e0871ca091d6ad598412d1ed78c711c0bc19412b1c20599d25ea1a7d3dc0f tm_hit=0 ok=0 finish="stop" degraded="sanitizer_defect" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 edit role=editor req=fake-model actual=fake-model hash=c445cf04d138dd1ecfae1b805336f2c5cde333b575b64f75c10f1017e39ac498 tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 edit role=editor req=fake-model actual=fake-model hash=1a3bc5b91428e2cc31a965748ea10046bf6d9f420f8c8d3e79f9fd3da5b03f4f tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/0 draft role=translator req=fake-model actual=fake-model hash=58e9dbc06bfa575bbb3e1e108d565a8a48a2e07dbcc1b14633adf1b20c33bf21 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/1 draft role=translator req=fake-model actual=fake-model hash=f62d390f0624ddcfbc27b048475b749d06b44ab91ade175985b162160235f6e7 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-model actual=fake-model hash=4c598abeab1852ea9cf980492f60a311382dd732e9c90e6146b3612bcc987cd2 tm_hit=0 ok=0 finish="stop" degraded="cjk_artifact" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-fallback actual=fake-fallback hash=afc9e9ff7d6f1e4832365eefe29133bc8b8f70277fea9d8379d41a490c47f35c tm_hit=0 ok=1 finish="stop" degraded="" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-model actual=fake-model hash=5806ff3fbd200810f0f01581eaf39a489ffb53fbd718fd696235a4bd4cdda367 tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-fallback actual=fake-fallback hash=667725b23cd72e129132d47da0ff7db069ff4cece653b2518b7f9756600efaca tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch4/0 draft role=translator req=fake-model actual=fake-model hash=95953ad9afa465ccc21cf6ae02556fcc2259593e51fb7d2a50cd24741cedf2d3 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 draft role=translator req=fake-model actual=fake-model hash=df9779e5705e186c4e936d4a1cbdb0bc66d603954bc7dd5ca2b7a4b06c2c9d0d tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 draft role=translator req=fake-model actual=fake-model hash=639d1fdb559d75d1ff72f73d72820e9aac3ff58f6b867408058f6f0761184155 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 draft role=translator req=fake-model actual=fake-model hash=64cd75a5c6410ff1d7c1880dd0ca06c871e517e9cef2ca7923a01fbcd88821ef tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 draft role=translator req=fake-model actual=fake-model hash=e9c1dd1174091275bf8f2975f1238d53b0b557ea1fbef283729a36dfa36f5f0d tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/0 edit role=editor req=fake-model actual=fake-model hash=366482169be171810b4478c7a10245779c921afdd0daf3c443a60495bf2ef737 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 edit role=editor req=fake-model actual=fake-model hash=a57b6f62ebecf7cbdb04d8fce7679b68dedb4cb95dcac2605942aa3fdc864e05 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch4/0 edit role=editor req=fake-model actual=fake-model hash=51a33a39df9ac10b3de8111d4e6c14c97e52d694d1853d0564a49f081017ffdf tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 edit role=editor req=fake-model actual=fake-model hash=d331964b915e5496a8c377189df897bdd5c3535e09f9ff4382e75eee7073c681 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 edit role=editor req=fake-model actual=fake-model hash=1ec612b83d1bd0e657e40e189a988546406929290acbed8babcf230ac9ae592d tm_hit=0 ok=0 finish="stop" degraded="sanitizer_defect" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 edit role=editor req=fake-model actual=fake-model hash=4e4d6214cb7d85753935d8fc18a8a6fcf1f99bdef9ff4bc958a30749071af75b tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 edit role=editor req=fake-model actual=fake-model hash=fcc423a73253a774560ba8376f2c3c2db613eea3a74c3785772b56b3f36ce5ce tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
-- retrieval_state --
ch1/0 snap_match=true exact=3 sticky=0 ambiguous=0 spoiler=1 evicted=0 postcheck_miss=1 style_flags=0 trust_gated=0
injected_ids=["紋章\u001f\u001f0\u001f0","鈴木\u001f\u001f0\u001f0","魔法学院\u001f\u001f0\u001f0"] postcheck_detail=[{"src":"紋章","dst":"герб","disp":"confirmed"}] style_detail= trust_gate_detail=
@ -126,10 +126,10 @@ ch8/0 snap_match=true exact=1 sticky=0 ambiguous=0 spoiler=0 evicted=0 postcheck
[16] {"max_tokens":4000,"messages":[{"role":"system","content":"Ты — монолингвальный редактор русского текста. Правь стиль, не меняя смысла. Книга: Золотая книга."},{"role":"system","content":"КАНОНИЧЕСКИЕ ПЕРЕВОДЫ имён и терминов (в черновике термин исходника слева ДОЛЖЕН быть передан именно указанной формой справа — приводи к ней любые расхождения, склоняя по контексту; не вводи иных вариантов и не меняй ничего другого):\n- 鈴木 → «Судзуки» (муж. — мужские родовые формы)"},{"role":"user","content":"Черновик перевода для редактуры: ЧЕРНОВИК ПЕРЕВОДА 9e65df37b751. МАРКДАУН-СЦЕНА Судзуки открыл седьмую дверь."}],"model":"fake-model","stream":false,"temperature":0.4}
[17] {"max_tokens":4000,"messages":[{"role":"system","content":"Ты — монолингвальный редактор русского текста. Правь стиль, не меняя смысла. Книга: Золотая книга."},{"role":"system","content":"КАНОНИЧЕСКИЕ ПЕРЕВОДЫ имён и терминов (в черновике термин исходника слева ДОЛЖЕН быть передан именно указанной формой справа — приводи к ней любые расхождения, склоняя по контексту; не вводи иных вариантов и не меняй ничего другого):\n- 鈴木 → «Судзуки» (муж. — мужские родовые формы)"},{"role":"user","content":"Черновик перевода для редактуры: ЧЕРНОВИК ПЕРЕВОДА 2c858e3366d2. ИЕРОГЛИФ-СЦЕНА Судзуки нашёл камень."}],"model":"fake-model","stream":false,"temperature":0.4}
==== run 2 (resume) ====
snapshot_draft: dc7572ca82f1898969220aecbb5727c36b9a42486ee70237ae8f95b8fe9ebce4
snapshot_draft_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-37bf9699835f","memory_version":"bd451833d4ebb8a0ca07caf8deda76f027e1f42b78654987a22d215acf955eb7","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"draft","role":"translator","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"26d0245a56d180eafd200ebe5311aab2488142c4a98dcddee21ea457814aa21a","temperature":0.3,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000},"escalate_to":"fake-fallback","escalate_capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":6000}}]}
snapshot_edit: 6799e44bbf02e21419447613ec3915f9f47c61f2746b94c1fc898ec9f5aa70bf
snapshot_edit_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-37bf9699835f","memory_version":"0a48d6ed9409de09f514af91aebdd622637621c9dc9464e8898c238791033d91","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"edit","role":"editor","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"6a8e7f71139d6bc09a95159d1cb895e05b56d2f2f7417c5e20a7073c517c79f6","temperature":0.4,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000}}]}
snapshot_draft: 8e1f174e988d99786c177f152966aaf9a116e67b55bdf9329cefde45701bdcdf
snapshot_draft_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-3df4329e3b9e","memory_version":"bd451833d4ebb8a0ca07caf8deda76f027e1f42b78654987a22d215acf955eb7","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"draft","role":"translator","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"26d0245a56d180eafd200ebe5311aab2488142c4a98dcddee21ea457814aa21a","temperature":0.3,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000},"escalate_to":"fake-fallback","escalate_capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":6000}}]}
snapshot_edit: b968f0f3ab875824174e199e5ea9135f4a799a54d394ee1b5f5899e92d97ce3c
snapshot_edit_payload: {"brief_hash":"c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df","chunker_version":"chunker-v6-generic-heading+srcabbrev+u15.0.0","estimator_version":"estimator-v0+u15.0.0","max_tokens_policy":"maxtok-v1-double-per-attempt","classifier_version":"classify-v2-refusal+srcscript-echo015+loop+u15.0.0","pipeline_core":"C1","max_output_ratio":2,"min_max_tokens":512,"context_assembly":{"glossary_injection":"selective","glossary_token_budget":800,"cache_ttl":""},"segmentation":{"draft_budget_out":1797,"edit_ceiling_out":3200,"fertility_cjk":1.1978,"fertility_other":0.3852},"render_format_version":"renderfmt-v3-draft-gender+editor-src2dst+dc3-gender","embedded_version":"embed-v1-3df4329e3b9e","memory_version":"0a48d6ed9409de09f514af91aebdd622637621c9dc9464e8898c238791033d91","postcheck_gate":false,"coverage":{"enabled":false},"style_check_version":"cheapgate-v6-target-tokenizer-combmark+u15.0.0","sanitizer":{"enabled":true,"version":"sanitizer-v7-hangul-leak"},"stages":[{"name":"edit","role":"editor","model":"fake-model","prompt_version":"v-golden","prompt_sha256":"6a8e7f71139d6bc09a95159d1cb895e05b56d2f2f7417c5e20a7073c517c79f6","temperature":0.4,"reasoning":"off","capability":{"Budget":"max_tokens","Temp":"send","TempValue":0,"Reasoning":{"Control":"none","OffEffort":"","OffExtraBody":null,"OnExtraBody":null},"MinMaxTokens":4000}}]}
brief_hash: c2021b1b5e6c20f6084e425b9a70bdc5f682801be347a57eb1c591deb8cab0df
memory_version: 0a48d6ed9409de09f514af91aebdd622637621c9dc9464e8898c238791033d91
base_memory_version: bd451833d4ebb8a0ca07caf8deda76f027e1f42b78654987a22d215acf955eb7
@ -178,58 +178,58 @@ chunk ch8/0 disposition=flagged flag="sanitizer_stripped" final_text="Судзу
stage=edit role=editor model=fake-model resume=true disp=flagged flag="sanitizer_stripped" attempts=1 escalated=false esc_model="" finish="sanitized_export" cum_usd=0.00182 detail="CJK leak in the ru output: 特产"
stage_text="" recovered="Судзуки нашёл древний камень на каменном алтаре."
-- chunk_status --
ch1/0 draft snap_match=true content_hash=3e156226b9a74aa70c8a259c62e3d2fe68486a96d2178d31084c1e38a3256241 disp=ok flag="" attempts=1 final_hash=70bca3400d2d8d848af27845cb2b86f362cdcf7a3a73e2611c46ec05feb7c4c2 cost=0.00182 escalated=false esc_model="" detail=""
ch1/0 edit snap_match=true content_hash=5e79dd5cbd310ac01d71ca258002beca18db715dfb8b1a08907508e506595355 disp=ok flag="" attempts=1 final_hash=799b6e6419910a2e52dd4652f2bde9bee59fbdb92a849f291ed33a5e30410a15 cost=0.00182 escalated=false esc_model="" detail=""
ch1/1 draft snap_match=true content_hash=82fb7f985de6c07182e8c09a3ceaad94f2e63745b87024087e437fba762b8227 disp=ok flag="" attempts=1 final_hash=ad47812002136f356e16d4fe4d71b73fdf1f2fbbe4f6a2cecd696abb07b7502d cost=0.00182 escalated=false esc_model="" detail=""
ch2/0 draft snap_match=true content_hash=cfab1e09264a3a30aba95e7da1d6a269468c6aa0c1b334d9241e78bc266a3571 disp=ok flag="" attempts=1 final_hash=35d5f692c908d3c15b40757a167c0deb3067e90492a4490500b0a0ccf359c9e2 cost=0.00728 escalated=true esc_model="fake-fallback" detail=""
ch2/0 edit snap_match=true content_hash=55ffd839a4a26c03cf57e08000ab2c84b267e1429ab4a8e212d3a01fda757632 disp=ok flag="" attempts=1 final_hash=e3f8acb903b0dd5798636597412acadfcf03a968ff049a8604b1142dea5cded1 cost=0.00182 escalated=false esc_model="" detail=""
ch1/0 draft snap_match=true content_hash=3e156226b9a74aa70c8a259c62e3d2fe68486a96d2178d31084c1e38a3256241 disp=ok flag="" attempts=1 final_hash=58e9dbc06bfa575bbb3e1e108d565a8a48a2e07dbcc1b14633adf1b20c33bf21 cost=0.00182 escalated=false esc_model="" detail=""
ch1/0 edit snap_match=true content_hash=5e79dd5cbd310ac01d71ca258002beca18db715dfb8b1a08907508e506595355 disp=ok flag="" attempts=1 final_hash=366482169be171810b4478c7a10245779c921afdd0daf3c443a60495bf2ef737 cost=0.00182 escalated=false esc_model="" detail=""
ch1/1 draft snap_match=true content_hash=82fb7f985de6c07182e8c09a3ceaad94f2e63745b87024087e437fba762b8227 disp=ok flag="" attempts=1 final_hash=f62d390f0624ddcfbc27b048475b749d06b44ab91ade175985b162160235f6e7 cost=0.00182 escalated=false esc_model="" detail=""
ch2/0 draft snap_match=true content_hash=cfab1e09264a3a30aba95e7da1d6a269468c6aa0c1b334d9241e78bc266a3571 disp=ok flag="" attempts=1 final_hash=afc9e9ff7d6f1e4832365eefe29133bc8b8f70277fea9d8379d41a490c47f35c cost=0.00728 escalated=true esc_model="fake-fallback" detail=""
ch2/0 edit snap_match=true content_hash=55ffd839a4a26c03cf57e08000ab2c84b267e1429ab4a8e212d3a01fda757632 disp=ok flag="" attempts=1 final_hash=a57b6f62ebecf7cbdb04d8fce7679b68dedb4cb95dcac2605942aa3fdc864e05 cost=0.00182 escalated=false esc_model="" detail=""
ch3/0 draft snap_match=true content_hash=21df44eb104900664bdaaeb2f716f51dcddbc81a71d845f6ba0f91c5bc4b2cfb disp=flagged flag="hard_refusal" attempts=1 final_hash= cost=0.00728 escalated=true esc_model="" detail="provider finish_reason=refusal"
ch3/0 edit snap_match=true content_hash= disp=skipped flag="hard_refusal" attempts=0 final_hash= cost=0 escalated=false esc_model="" detail="skipped: a member draft chunk of this edit unit was flagged (hard_refusal)"
ch4/0 draft snap_match=true content_hash=7aabdcb89dea365c95363b2662144ca1a10b307d377f47a8e7cc19b5b872d287 disp=ok flag="" attempts=1 final_hash=c74a4b18be42dae5946fc9d33f1172e6be71a37bad7dc08076e5fba59316e858 cost=0.00182 escalated=false esc_model="" detail=""
ch4/0 edit snap_match=true content_hash=2854dfe1648b0cf0cbb683cb7ce94c5eedc9223b2ce20c99dc008c7add6317ef disp=ok flag="" attempts=1 final_hash=5f8f5737a4e02766f8500915341c8b8a8827f8a05b03d77a746a4a8bc48806eb cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 draft snap_match=true content_hash=2ed0a97f39cc5c57c62c3324f4646357372f26e3c5bae1773f24edf9449fc013 disp=ok flag="" attempts=1 final_hash=407f023c09e54b0399f79cf59e893817065b28129a807c57b25cc796202d75e1 cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 edit snap_match=true content_hash=b145d78e4d30a39cc4088a1ddb8d934234c65341c62c79545c2eff1e7ae0c3d3 disp=ok flag="" attempts=1 final_hash=2b722376f3691a64c38d9a8026f567dfce0dd0a5ca3d2355bd2a95e4807ee67c cost=0.00182 escalated=false esc_model="" detail=""
ch6/0 draft snap_match=true content_hash=5f396d299a7ec5b10f3c8591e54ce7d84c4d31b815e561b0dd5f56635aef5a71 disp=ok flag="" attempts=1 final_hash=afd2169fce4c9aaecdf7f3de1f1f87bc08d5b672d19bc1dc772991f8d3321d60 cost=0.00182 escalated=false esc_model="" detail=""
ch4/0 draft snap_match=true content_hash=7aabdcb89dea365c95363b2662144ca1a10b307d377f47a8e7cc19b5b872d287 disp=ok flag="" attempts=1 final_hash=95953ad9afa465ccc21cf6ae02556fcc2259593e51fb7d2a50cd24741cedf2d3 cost=0.00182 escalated=false esc_model="" detail=""
ch4/0 edit snap_match=true content_hash=2854dfe1648b0cf0cbb683cb7ce94c5eedc9223b2ce20c99dc008c7add6317ef disp=ok flag="" attempts=1 final_hash=51a33a39df9ac10b3de8111d4e6c14c97e52d694d1853d0564a49f081017ffdf cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 draft snap_match=true content_hash=2ed0a97f39cc5c57c62c3324f4646357372f26e3c5bae1773f24edf9449fc013 disp=ok flag="" attempts=1 final_hash=df9779e5705e186c4e936d4a1cbdb0bc66d603954bc7dd5ca2b7a4b06c2c9d0d cost=0.00182 escalated=false esc_model="" detail=""
ch5/0 edit snap_match=true content_hash=b145d78e4d30a39cc4088a1ddb8d934234c65341c62c79545c2eff1e7ae0c3d3 disp=ok flag="" attempts=1 final_hash=d331964b915e5496a8c377189df897bdd5c3535e09f9ff4382e75eee7073c681 cost=0.00182 escalated=false esc_model="" detail=""
ch6/0 draft snap_match=true content_hash=5f396d299a7ec5b10f3c8591e54ce7d84c4d31b815e561b0dd5f56635aef5a71 disp=ok flag="" attempts=1 final_hash=639d1fdb559d75d1ff72f73d72820e9aac3ff58f6b867408058f6f0761184155 cost=0.00182 escalated=false esc_model="" detail=""
ch6/0 edit snap_match=true content_hash=592353321cc8000e792e0897446e4dd4f88a0280b9368b62cbd5c82b0c433193 disp=flagged flag="sanitizer_defect" attempts=1 final_hash= cost=0.00182 escalated=false esc_model="" detail="leading service preamble: Вот перевод фрагмента:"
ch7/0 draft snap_match=true content_hash=15c3ebbd56bb19880202d35b0206357655da245343376492f7ceb61808c8a5be disp=ok flag="" attempts=1 final_hash=486facff54a77c86f44cd322200caf4cf982975eeda552fa6dfdee4aa8df0904 cost=0.00182 escalated=false esc_model="" detail=""
ch7/0 edit snap_match=true content_hash=0b2674ed9829b2e6526ff41a60dd280b6670c539476526bbe835d9bf7a6be4c4 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:def877b81bcdc52e2525fdcaae04b8c64f632c7d6747b31df7f4c7f1ad176e73 cost=0.00182 escalated=false esc_model="" detail="markdown header in the output: ### Глава 7"
ch8/0 draft snap_match=true content_hash=4a420977cc34ce738b4978e6a1baf45d78d773a135070c3b7f8c723adfa57d7b disp=ok flag="" attempts=1 final_hash=070d13df73e028346b69abe69253a999f109d92b01f39f3dfada0772be50e185 cost=0.00182 escalated=false esc_model="" detail=""
ch8/0 edit snap_match=true content_hash=c851100c8d6785f77d68f8127472790ae9d0408b7fec44785d3ff9d46842c6f8 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:5095da45458381184b8798287c934e133c39668fed86c50cce921a1cb7df7893 cost=0.00182 escalated=false esc_model="" detail="CJK leak in the ru output: 特产"
ch7/0 draft snap_match=true content_hash=15c3ebbd56bb19880202d35b0206357655da245343376492f7ceb61808c8a5be disp=ok flag="" attempts=1 final_hash=64cd75a5c6410ff1d7c1880dd0ca06c871e517e9cef2ca7923a01fbcd88821ef cost=0.00182 escalated=false esc_model="" detail=""
ch7/0 edit snap_match=true content_hash=0b2674ed9829b2e6526ff41a60dd280b6670c539476526bbe835d9bf7a6be4c4 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:93ae4ce8992136692f558f136d12483aff728b1a0aa54388072335b4a0231267 cost=0.00182 escalated=false esc_model="" detail="markdown header in the output: ### Глава 7"
ch8/0 draft snap_match=true content_hash=4a420977cc34ce738b4978e6a1baf45d78d773a135070c3b7f8c723adfa57d7b disp=ok flag="" attempts=1 final_hash=e9c1dd1174091275bf8f2975f1238d53b0b557ea1fbef283729a36dfa36f5f0d cost=0.00182 escalated=false esc_model="" detail=""
ch8/0 edit snap_match=true content_hash=c851100c8d6785f77d68f8127472790ae9d0408b7fec44785d3ff9d46842c6f8 disp=flagged flag="sanitizer_stripped" attempts=1 final_hash=tm-sanitized-v1:e6a81289fc023f94c28749e97eff499ec5222ed807b4a3c10934dbbcfe4a0ffc cost=0.00182 escalated=false esc_model="" detail="CJK leak in the ru output: 特产"
-- request_log (insertion order) --
ch1/0 draft role=translator req=fake-model actual=fake-model hash=70bca3400d2d8d848af27845cb2b86f362cdcf7a3a73e2611c46ec05feb7c4c2 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/1 draft role=translator req=fake-model actual=fake-model hash=ad47812002136f356e16d4fe4d71b73fdf1f2fbbe4f6a2cecd696abb07b7502d tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-model actual=fake-model hash=56b4390260a43c1ffd2ba17afb8c3fab16113992856bb960e62e11c4937e0028 tm_hit=0 ok=0 finish="stop" degraded="cjk_artifact" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-fallback actual=fake-fallback hash=35d5f692c908d3c15b40757a167c0deb3067e90492a4490500b0a0ccf359c9e2 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-model actual=fake-model hash=9e6325591eca8bc28e34be1da64106704d9589193c991f6545b22e8ddbae3b54 tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-fallback actual=fake-fallback hash=544c921f47e59718019cfff42782f590deba18352123ae0687ceade0130fe2bd tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch4/0 draft role=translator req=fake-model actual=fake-model hash=c74a4b18be42dae5946fc9d33f1172e6be71a37bad7dc08076e5fba59316e858 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 draft role=translator req=fake-model actual=fake-model hash=407f023c09e54b0399f79cf59e893817065b28129a807c57b25cc796202d75e1 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 draft role=translator req=fake-model actual=fake-model hash=afd2169fce4c9aaecdf7f3de1f1f87bc08d5b672d19bc1dc772991f8d3321d60 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 draft role=translator req=fake-model actual=fake-model hash=486facff54a77c86f44cd322200caf4cf982975eeda552fa6dfdee4aa8df0904 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 draft role=translator req=fake-model actual=fake-model hash=070d13df73e028346b69abe69253a999f109d92b01f39f3dfada0772be50e185 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/0 edit role=editor req=fake-model actual=fake-model hash=799b6e6419910a2e52dd4652f2bde9bee59fbdb92a849f291ed33a5e30410a15 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 edit role=editor req=fake-model actual=fake-model hash=e3f8acb903b0dd5798636597412acadfcf03a968ff049a8604b1142dea5cded1 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch4/0 edit role=editor req=fake-model actual=fake-model hash=5f8f5737a4e02766f8500915341c8b8a8827f8a05b03d77a746a4a8bc48806eb tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 edit role=editor req=fake-model actual=fake-model hash=2b722376f3691a64c38d9a8026f567dfce0dd0a5ca3d2355bd2a95e4807ee67c tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 edit role=editor req=fake-model actual=fake-model hash=806e0871ca091d6ad598412d1ed78c711c0bc19412b1c20599d25ea1a7d3dc0f tm_hit=0 ok=0 finish="stop" degraded="sanitizer_defect" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 edit role=editor req=fake-model actual=fake-model hash=c445cf04d138dd1ecfae1b805336f2c5cde333b575b64f75c10f1017e39ac498 tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 edit role=editor req=fake-model actual=fake-model hash=1a3bc5b91428e2cc31a965748ea10046bf6d9f420f8c8d3e79f9fd3da5b03f4f tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/0 draft role=translator req=fake-model actual=fake-model hash=70bca3400d2d8d848af27845cb2b86f362cdcf7a3a73e2611c46ec05feb7c4c2 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch1/1 draft role=translator req=fake-model actual=fake-model hash=ad47812002136f356e16d4fe4d71b73fdf1f2fbbe4f6a2cecd696abb07b7502d tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch2/0 draft role=translator req=fake-model actual=fake-fallback hash=35d5f692c908d3c15b40757a167c0deb3067e90492a4490500b0a0ccf359c9e2 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch1/0 draft role=translator req=fake-model actual=fake-model hash=58e9dbc06bfa575bbb3e1e108d565a8a48a2e07dbcc1b14633adf1b20c33bf21 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/1 draft role=translator req=fake-model actual=fake-model hash=f62d390f0624ddcfbc27b048475b749d06b44ab91ade175985b162160235f6e7 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-model actual=fake-model hash=4c598abeab1852ea9cf980492f60a311382dd732e9c90e6146b3612bcc987cd2 tm_hit=0 ok=0 finish="stop" degraded="cjk_artifact" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 draft role=translator req=fake-fallback actual=fake-fallback hash=afc9e9ff7d6f1e4832365eefe29133bc8b8f70277fea9d8379d41a490c47f35c tm_hit=0 ok=1 finish="stop" degraded="" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-model actual=fake-model hash=5806ff3fbd200810f0f01581eaf39a489ffb53fbd718fd696235a4bd4cdda367 tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch3/0 draft role=translator req=fake-fallback actual=fake-fallback hash=667725b23cd72e129132d47da0ff7db069ff4cece653b2518b7f9756600efaca tm_hit=0 ok=0 finish="refusal" degraded="hard_refusal" cost=0.0054600000000000004 tokens=1000/200/0/500/0 err=""
ch4/0 draft role=translator req=fake-model actual=fake-model hash=95953ad9afa465ccc21cf6ae02556fcc2259593e51fb7d2a50cd24741cedf2d3 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 draft role=translator req=fake-model actual=fake-model hash=df9779e5705e186c4e936d4a1cbdb0bc66d603954bc7dd5ca2b7a4b06c2c9d0d tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 draft role=translator req=fake-model actual=fake-model hash=639d1fdb559d75d1ff72f73d72820e9aac3ff58f6b867408058f6f0761184155 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 draft role=translator req=fake-model actual=fake-model hash=64cd75a5c6410ff1d7c1880dd0ca06c871e517e9cef2ca7923a01fbcd88821ef tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 draft role=translator req=fake-model actual=fake-model hash=e9c1dd1174091275bf8f2975f1238d53b0b557ea1fbef283729a36dfa36f5f0d tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/0 edit role=editor req=fake-model actual=fake-model hash=366482169be171810b4478c7a10245779c921afdd0daf3c443a60495bf2ef737 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch2/0 edit role=editor req=fake-model actual=fake-model hash=a57b6f62ebecf7cbdb04d8fce7679b68dedb4cb95dcac2605942aa3fdc864e05 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch4/0 edit role=editor req=fake-model actual=fake-model hash=51a33a39df9ac10b3de8111d4e6c14c97e52d694d1853d0564a49f081017ffdf tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch5/0 edit role=editor req=fake-model actual=fake-model hash=d331964b915e5496a8c377189df897bdd5c3535e09f9ff4382e75eee7073c681 tm_hit=0 ok=1 finish="stop" degraded="" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch6/0 edit role=editor req=fake-model actual=fake-model hash=1ec612b83d1bd0e657e40e189a988546406929290acbed8babcf230ac9ae592d tm_hit=0 ok=0 finish="stop" degraded="sanitizer_defect" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch7/0 edit role=editor req=fake-model actual=fake-model hash=4e4d6214cb7d85753935d8fc18a8a6fcf1f99bdef9ff4bc958a30749071af75b tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch8/0 edit role=editor req=fake-model actual=fake-model hash=fcc423a73253a774560ba8376f2c3c2db613eea3a74c3785772b56b3f36ce5ce tm_hit=0 ok=0 finish="stop" degraded="sanitizer_stripped" cost=0.00182 tokens=1000/200/0/500/0 err=""
ch1/0 draft role=translator req=fake-model actual=fake-model hash=58e9dbc06bfa575bbb3e1e108d565a8a48a2e07dbcc1b14633adf1b20c33bf21 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch1/1 draft role=translator req=fake-model actual=fake-model hash=f62d390f0624ddcfbc27b048475b749d06b44ab91ade175985b162160235f6e7 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch2/0 draft role=translator req=fake-model actual=fake-fallback hash=afc9e9ff7d6f1e4832365eefe29133bc8b8f70277fea9d8379d41a490c47f35c tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch3/0 draft role=translator req=fake-model actual=fake-model hash= tm_hit=1 ok=0 finish="" degraded="hard_refusal" cost=0 tokens=0/0/0/0/0 err=""
ch4/0 draft role=translator req=fake-model actual=fake-model hash=c74a4b18be42dae5946fc9d33f1172e6be71a37bad7dc08076e5fba59316e858 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch5/0 draft role=translator req=fake-model actual=fake-model hash=407f023c09e54b0399f79cf59e893817065b28129a807c57b25cc796202d75e1 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch6/0 draft role=translator req=fake-model actual=fake-model hash=afd2169fce4c9aaecdf7f3de1f1f87bc08d5b672d19bc1dc772991f8d3321d60 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch7/0 draft role=translator req=fake-model actual=fake-model hash=486facff54a77c86f44cd322200caf4cf982975eeda552fa6dfdee4aa8df0904 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch8/0 draft role=translator req=fake-model actual=fake-model hash=070d13df73e028346b69abe69253a999f109d92b01f39f3dfada0772be50e185 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch1/0 edit role=editor req=fake-model actual=fake-model hash=799b6e6419910a2e52dd4652f2bde9bee59fbdb92a849f291ed33a5e30410a15 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch2/0 edit role=editor req=fake-model actual=fake-model hash=e3f8acb903b0dd5798636597412acadfcf03a968ff049a8604b1142dea5cded1 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch4/0 edit role=editor req=fake-model actual=fake-model hash=5f8f5737a4e02766f8500915341c8b8a8827f8a05b03d77a746a4a8bc48806eb tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch5/0 edit role=editor req=fake-model actual=fake-model hash=2b722376f3691a64c38d9a8026f567dfce0dd0a5ca3d2355bd2a95e4807ee67c tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch4/0 draft role=translator req=fake-model actual=fake-model hash=95953ad9afa465ccc21cf6ae02556fcc2259593e51fb7d2a50cd24741cedf2d3 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch5/0 draft role=translator req=fake-model actual=fake-model hash=df9779e5705e186c4e936d4a1cbdb0bc66d603954bc7dd5ca2b7a4b06c2c9d0d tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch6/0 draft role=translator req=fake-model actual=fake-model hash=639d1fdb559d75d1ff72f73d72820e9aac3ff58f6b867408058f6f0761184155 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch7/0 draft role=translator req=fake-model actual=fake-model hash=64cd75a5c6410ff1d7c1880dd0ca06c871e517e9cef2ca7923a01fbcd88821ef tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch8/0 draft role=translator req=fake-model actual=fake-model hash=e9c1dd1174091275bf8f2975f1238d53b0b557ea1fbef283729a36dfa36f5f0d tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch1/0 edit role=editor req=fake-model actual=fake-model hash=366482169be171810b4478c7a10245779c921afdd0daf3c443a60495bf2ef737 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch2/0 edit role=editor req=fake-model actual=fake-model hash=a57b6f62ebecf7cbdb04d8fce7679b68dedb4cb95dcac2605942aa3fdc864e05 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch4/0 edit role=editor req=fake-model actual=fake-model hash=51a33a39df9ac10b3de8111d4e6c14c97e52d694d1853d0564a49f081017ffdf tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch5/0 edit role=editor req=fake-model actual=fake-model hash=d331964b915e5496a8c377189df897bdd5c3535e09f9ff4382e75eee7073c681 tm_hit=1 ok=1 finish="stop" degraded="" cost=0 tokens=0/0/0/0/0 err=""
ch6/0 edit role=editor req=fake-model actual=fake-model hash= tm_hit=1 ok=0 finish="" degraded="sanitizer_defect" cost=0 tokens=0/0/0/0/0 err=""
ch7/0 edit role=editor req=fake-model actual=fake-model hash=tm-sanitized-v1:def877b81bcdc52e2525fdcaae04b8c64f632c7d6747b31df7f4c7f1ad176e73 tm_hit=1 ok=0 finish="sanitized_export" degraded="sanitizer_stripped" cost=0 tokens=0/0/0/0/0 err=""
ch8/0 edit role=editor req=fake-model actual=fake-model hash=tm-sanitized-v1:5095da45458381184b8798287c934e133c39668fed86c50cce921a1cb7df7893 tm_hit=1 ok=0 finish="sanitized_export" degraded="sanitizer_stripped" cost=0 tokens=0/0/0/0/0 err=""
ch7/0 edit role=editor req=fake-model actual=fake-model hash=tm-sanitized-v1:93ae4ce8992136692f558f136d12483aff728b1a0aa54388072335b4a0231267 tm_hit=1 ok=0 finish="sanitized_export" degraded="sanitizer_stripped" cost=0 tokens=0/0/0/0/0 err=""
ch8/0 edit role=editor req=fake-model actual=fake-model hash=tm-sanitized-v1:e6a81289fc023f94c28749e97eff499ec5222ed807b4a3c10934dbbcfe4a0ffc tm_hit=1 ok=0 finish="sanitized_export" degraded="sanitizer_stripped" cost=0 tokens=0/0/0/0/0 err=""
-- retrieval_state --
ch1/0 snap_match=true exact=3 sticky=0 ambiguous=0 spoiler=1 evicted=0 postcheck_miss=1 style_flags=0 trust_gated=0
injected_ids=["紋章\u001f\u001f0\u001f0","鈴木\u001f\u001f0\u001f0","魔法学院\u001f\u001f0\u001f0"] postcheck_detail=[{"src":"紋章","dst":"герб","disp":"confirmed"}] style_detail= trust_gate_detail=

View file

@ -1,10 +1,88 @@
package terminology
import (
"os"
"path/filepath"
"sort"
"strings"
"testing"
)
// loadBankFullSurfaces reads the distilled BANK-FULL source surfaces (fix-pack §ж fixture) as candidates.
func loadBankFullSurfaces(t *testing.T) []Candidate {
t.Helper()
b, err := os.ReadFile(filepath.Join("testdata", "bankfull-surfaces.txt"))
if err != nil {
t.Fatal(err)
}
var cands []Candidate
for _, ln := range strings.Split(string(b), "\n") {
s := strings.TrimSpace(ln)
if s == "" || strings.HasPrefix(s, "#") {
continue
}
cands = append(cands, cand(s, 1))
}
return cands
}
// TestDetectSeriesOnBankFullFixtureIsStable pins the head-aware rule's LIVE property on the real corpus it was
// calibrated on (fix-pack §ж): exactly the measured grade/rank series form, none of them a family blob, and
// 方源/元石/元海 (the protagonist and two same-prefix-different-head entities) stay OUT of every series — the
// naive "differ in one position" rule produced an 11-surface blob here.
func TestDetectSeriesOnBankFullFixtureIsStable(t *testing.T) {
got := DetectSeries(loadBankFullSurfaces(t), zhSeries)
// The FOUR grade/rank series the corpus forms, each sharing one real head — 转 (rank), 等 (grade), 等资质
// (graded aptitude), 阶 (tier). Legitimately large is not a blob: 一转…九转 is nine ranks sharing 转, the
// canonical §1 case. A blob is a series that MIXES families under a shared rune, which the head-aware rule
// forbids by construction — pinned here by requiring each series be EXACTLY its expected group.
expected := [][]string{
{"一转", "二转", "三转", "四转", "五转", "六转", "九转"},
{"甲等", "乙等", "丙等", "丁等"},
{"甲等资质", "乙等资质", "丙等资质"},
{"初阶", "中阶", "高阶"},
}
ids := map[int]bool{}
size := map[int]int{}
for _, id := range got {
ids[id] = true
size[id]++
}
if len(ids) != 4 {
grouped := map[int][]string{}
for k, id := range got {
grouped[id] = append(grouped[id], k)
}
for id, ms := range grouped {
sort.Strings(ms)
t.Logf("series %d: %v", id, ms)
}
t.Fatalf("expected exactly 4 series on the distilled bank, got %d", len(ids))
}
for _, grp := range expected {
id := got[grp[0]]
if id == 0 {
t.Fatalf("expected group %v did not form a series", grp)
}
for _, k := range grp {
if got[k] != id {
t.Fatalf("%s must join series %d with %s, got %d", k, id, grp[0], got[k])
}
}
if size[id] != len(grp) {
t.Fatalf("series %d has %d members, expected exactly %d (%v) — a member leaked in (a blob)", id, size[id], len(grp), grp)
}
}
// The three entities that must NEVER be series members: the protagonist 方源 and the two same-prefix,
// different-HEAD entities 元石/元海 (石/海 differ IN the head → different entities, the §2 type step's job).
for _, k := range []string{"方源", "元石", "元海"} {
if got[k] != 0 {
t.Fatalf("%s must stay OUT of every series, got id %d", k, got[k])
}
}
}
var zhSeries = SeriesParams{Enabled: true, HeadFinal: true}
func cand(key string, freq int) Candidate { return Candidate{Key: key, Src: key, Type: "term", Freq: freq} }
@ -101,6 +179,20 @@ func TestBatchKeepsSeriesWhole(t *testing.T) {
}
}
// TestBatchRunesFlagsOversize pins the fix-pack §в observability seam: BatchRunes measures a batch the way
// Batch packs it, so the terminologist can WARN when §1 keeps a unit whole past the budget instead of
// silently over-running the output cap. A lone candidate too big for the budget stays ONE over-cap batch.
func TestBatchRunesFlagsOversize(t *testing.T) {
big := Candidate{Key: "元", Src: "元", Type: "term", KWIC: []string{strings.Repeat("к", 500)}}
if BatchRunes([]Candidate{big}) <= 100 {
t.Fatal("a candidate carrying a 500-rune context must render well over a 100-rune budget")
}
batches := Batch([]Candidate{big}, 100, nil)
if len(batches) != 1 || BatchRunes(batches[0]) <= 100 {
t.Fatalf("an oversize lone candidate must stay one over-cap batch the caller can WARN on: %v", batches)
}
}
// TestBatchValueOrder: batches are processed most-frequent first, so a budget ceiling drops the rarest terms
// (feed_cap) rather than the lexicographic tail. Content of each batch is untouched by the ordering.
func TestBatchValueOrder(t *testing.T) {

View file

@ -917,10 +917,17 @@ func ParseReply(reply string, expected []string, normalize func(string) string,
// Batch splits candidates into groups whose rendered size stays under maxRunes. A series (a key present in
// seriesID; pass nil to disable the channel) is kept WHOLE in one batch so the terminologist picks one
// generic head for it (§1); a series or a single candidate larger than the budget still gets its own batch
// rather than being split or silently dropped. The batches are then ordered by descending source frequency,
// so a budget ceiling drops the least-frequent terms instead of the lexicographic tail (feed_cap, §7). Every
// batch's CONTENT — hence its request hash and $0 resume — is independent of this ordering; only the call
// order moves. Deterministic throughout.
// rather than being split or silently dropped (the caller WARNs on such an over-cap unit — see BatchRunes —
// because the whole unit still bills against one output cap). The batches are then ordered by descending
// source frequency, so a budget ceiling drops the least-frequent terms instead of the lexicographic tail
// (feed_cap, §7).
//
// The ordering is DETERMINISTIC — a stable sort on a pure function of each batch's content — so the same
// input reproduces the same batch order every run and the $0 checkpoint resume holds. It is NOT, however,
// independent of that ordering: the consumer addresses batch i as ChunkIdx i (terminologist.go), which folds
// into the request hash, so a batch's POSITION is part of its address. If a later run's frequencies reorder
// the batches their ordinals shift and the affected batches re-pay — the accepted price of value-ordering,
// and no worse than the content edit that would already re-pay them.
func Batch(cands []Candidate, maxRunes int, seriesID map[string]int) [][]Candidate {
if len(cands) == 0 {
return nil
@ -959,6 +966,12 @@ func Batch(cands []Candidate, maxRunes int, seriesID map[string]int) [][]Candida
return out
}
// BatchRunes reports the rendered size RenderBatch produces for a whole batch — the same measure Batch packs
// against. The terminologist uses it to WARN when a co-batched series or a lone candidate exceeds the budget:
// §1 keeps such a unit WHOLE rather than split it, so it is a real over-cap call the operator must see coming
// (the cap-8000 output mine, PROBES §1), not a silent overrun.
func BatchRunes(batch []Candidate) int { return unitRunes(batch) }
// unitRunes is the rendered size the accountant charges a unit: each member's single-block render plus the
// blank line RenderBatch puts between blocks (+2). Without the +2 a 200-term book overruns its budget by
// 2×(N1) runes. Summing per member matches the original per-candidate arithmetic exactly.

View file

@ -0,0 +1,155 @@
# BANK-FULL surface distillation (fix-pack §ж, D39.71): the SOURCE surfaces of the gu-zhenren coldrun-a
# bank (books/gu-zhenren/coldrun-a/BANK-FULL.tsv, col 1), the corpus DetectSeries was calibrated on. Pinned
# so the head-aware series rule's live property — exactly the measured grade/rank series form, no family
# blob, and 方源/元石/元海 stay OUT — cannot silently regress. Pair TEST DATA (legit per the generality
# mandate); one source surface per line, comments with '#'.
方源
古月
方正
家老
蛊师
舅父
舅母
真元
月光蛊
蛊虫
元石
族长
舅父舅母
空窍
炼化
元海
甲等
丙等
山寨
沈翠
开窍
学堂家老
酒虫
一转
困境
甲等资
甲等资质
古月方正
花酒行者
人祖
开窍大典
古月一族
春秋蝉
丙等资
丙等资质
乙等
古月赤练
花海
蛊室
古月山寨
本命蛊
三转
乙等资质
古月族长
古月赤城
四代族长
希望蛊
光点
古月方源
古月漠北
青茅山
二转
五转
古月漠尘
炼蛊
力量蛊
智慧蛊
沈嬷嬷
溶洞
血脉
一转蛊师
丁等
修行资质
光膜
四转
方家
灵泉
高脚吊楼
魔道
三转蛊师
九转
光团
六转
初阶
地下溶洞
家主阁
方之一脉
时辰
白凝冰
空窍元海
精气神
贴身丫鬟
一转蛊虫
三蛊
元气
凡人
古月师
古月陈博
巅峰
月兰花
物是人非
白家寨
真元海
管家
过继
邦邦
酒气
酒肆
青铜真元
魔道巨擘
一转巅峰
中土
中阶
九转境界
五更天
侧室
元海空窍
光阴之河
十大奇蛊
华池
南疆
古月冻土
古月博
古月藻榭
咏梅
天有不测风云
宗祖祠堂
将敬酒
彩雀儿
抽经伐髓
拂开
方家支脉
更夫
梆子
江城子
池塘
熊家寨
珍珠簪子
真元海洋
矛竹
空窍外侧
竹楼
紫府
绣花鞋
老妈子
花酒行者遗藏
赤之一脉
赤脉
趺坐
逆光阴五百年觉悟
采花大盗
难兄难弟
雨帘
雪盐
青铜元海
青铜海
高阶

View file

@ -33,6 +33,21 @@ func RunesEqual(a, b []rune) bool {
// those checks need. The word SCRIPT is the target's declared alphabet (data), so the tokenizer is target-
// general; the ё/е distinction the yofication check measures is preserved (ё is a Cyrillic letter).
func TokenizeScript(text string, ws *unicode.RangeTable) []string {
return tokenizeWords(text, func(r rune) bool { return unicode.Is(ws, r) })
}
// TokenizeLetters is the SCRIPT-AGNOSTIC sibling of TokenizeScript: maximal runs of any Unicode letter,
// lower-cased, with the same combining-mark handling. For a caller that matches words regardless of the
// target's alphabet and has no declared script to pass — the decl-aware bank post-check tokenizes a target
// term head against the output without needing (or knowing) the target's word script. lang.TokenizeWords
// wraps it, so `lang` (which MAY import `text`) keeps one tokenizer instead of a second copy of this loop.
func TokenizeLetters(text string) []string {
return tokenizeWords(text, unicode.IsLetter)
}
// tokenizeWords is the shared core of TokenizeScript/TokenizeLetters: lower-case, then emit maximal runs of
// runes the predicate accepts, dropping a combining mark that sits on an open run rather than breaking it.
func tokenizeWords(text string, isWord func(rune) bool) []string {
var words []string
var b strings.Builder
flush := func() {
@ -43,10 +58,10 @@ func TokenizeScript(text string, ws *unicode.RangeTable) []string {
}
for _, r := range strings.ToLower(text) {
switch {
case unicode.Is(ws, r):
case isWord(r):
b.WriteRune(r)
case b.Len() > 0 && unicode.Is(unicode.Mn, r):
// a combining mark sits ON the preceding word-script base: keep the run open and drop the mark.
// a combining mark sits ON the preceding word base: keep the run open and drop the mark.
default:
flush()
}

View file

@ -0,0 +1,31 @@
package text
import (
"strings"
"testing"
"unicode"
)
// TestTokenizeLetters pins the script-agnostic tokenizer the bank post-check uses (fix-pack §и consolidation):
// maximal any-letter runs, lower-cased, with a combining mark dropped rather than splitting the word — the
// same combining-mark handling TokenizeScript has, now shared through one core.
func TestTokenizeLetters(t *testing.T) {
got := TokenizeLetters("Фан Юаню, к Горе!")
if strings.Join(got, "|") != "фан|юаню|к|горе" {
t.Fatalf("tokens = %v", got)
}
// A stress accent (о + U+0301) must not split the word — the pack-21 #11 fix, now inherited by the
// decl post-check that lang.TokenizeWords feeds.
if got := TokenizeLetters("сло́во"); len(got) != 1 || got[0] != "слово" {
t.Fatalf("a combining mark must not split the word, got %v", got)
}
// Script-agnostic: a Latin word and a Cyrillic word both tokenize (TokenizeScript would drop one).
if got := TokenizeLetters("Гу and Gu"); strings.Join(got, "|") != "гу|and|gu" {
t.Fatalf("any-letter tokenization must keep both scripts, got %v", got)
}
// It agrees with TokenizeScript on a single-script string (the shared core), so the consolidation is
// behaviour-preserving for the target lexical checkers.
if a, b := TokenizeLetters("Меч и Щит"), TokenizeScript("Меч и Щит", unicode.Cyrillic); strings.Join(a, "|") != strings.Join(b, "|") {
t.Fatalf("TokenizeLetters and TokenizeScript diverged on Cyrillic-only text: %v vs %v", a, b)
}
}

View file

@ -1,8 +1,8 @@
# Журнал прогресса
> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-08-02, голова D39.74). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1D39.74); этот файл — ЖУРНАЛ.**
> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-08-02, голова D39.75). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1D39.75); этот файл — ЖУРНАЛ.**
> - **Сделано (сводно; детали — D-лог и архив-слайсы):** Ф0 ✅ · Ф1-инфра ✅ · арх-ресет D39 (7 слоёв, паки 1116) ✅ · паки 17 «канал B» · 18 «долги» · 19 «голос/состояние» · 20 «банк+терминолог» ✅ (D39.2628/31/4156) · мини-прогон (D39.37) и ХОЛОДНЫЙ прогон (D39.58: recall банка 0.918/0.980, банк приходит переведённым) приняты · полигон-пакеты 58, ToS-речек (D39.57), Р6+P4 (D39.61) закрыты · **карта языковой привязки движка (D39.60): 149 сайтов/45 файлов, книго-ось чиста (4), ja→ru безопасно без правки Go, en→ru — нет** · Р1Р4 закрыты целиком (D39.63: Р2 = ЗНАЧЕНИЕ, Р4 = норма потолка recall) · **ФАЗА 2 ОБЩНОСТИ ✅ (D39.64: П0 эмбед-хеш · П1 цель-шов · П2 скрипт-шов, ko-баг закрыт · П3 нарезка · П4 манифест-по-каналам; голден вердикт-нейтрален 0/172, майнер-парити EXACT)** · жанровый словарь отменён как класс (D39.47) · петля ремонта построена и НЕ включена (D39.38).
> - **Курс (D39.5969): ОБЩНОСТЬ ✅ → КАЧЕСТВО БАНКА ✅ (ядро построено).** Банк-пак принят целиком (D39.69: серия-батчинг head-aware · тип-классификатор первичным · Decl-стеммер+neuter; 36а/36в/27/84/feed_cap закрыты; голден вердикт-нейтрален, «4 серии на живом BANK-FULL» воспроизведено независимо). **Текущее: ФИКС-ПАК БАНКА ПЕРВЫМ (промт `BACKEND_BANK_FIXPACK_SESSION_PROMPT.md` выдан D39.71, строка 87) → после его лендинга ПАКЕТ-ЧЕКЕРОВ (промт выдан, общие файлы membank/prompts — не параллелить). Вопросы §9 закрыты (D39.70).** ⚠ **DeepSeek-V4-Flash-0731: платные прогоны СТОП до ре-пробы (строка 74; при ре-пробе добрать платный порог §2 «6/6 harm»). Перекупок нет — стендовый корпус одноразовый (D39.63).**
> - **Курс (D39.5975): ОБЩНОСТЬ ✅ → КАЧЕСТВО БАНКА ✅ (ядро D39.69 + фикс-пак D39.75).** **Текущее: очередь за ПАКЕТОМ-ЧЕКЕРОВ (промт `BACKEND_CHECKER_PACKAGE_SESSION_PROMPT.md` выдан, теперь можно запускать — фикс-пак заленден) + строка 91 у владельца (Decl мягкознаковых имён: forms[] vs anchor-gated).** ⚠ **DeepSeek-V4-Flash-0731: платные прогоны СТОП до ре-пробы (строка 74; там же 6/6-порог классификатора и слоты Q2/Q7). Перекупок нет (D39.63).**
> - **Горизонт (D39.62/67):** пакет-чекеров (строка 25) + фикс-лист банка (87) → ре-проба flash (74; + платный порог 6/6 классификатора + пробы 36б-замера Q2/эмиссии Q7 по слову) → **ДОБОР ИДЕАЛА** (первым прогоном: оси голоса 24 · авто-режим · итерация №2 редакторов 65 · цена 16 · веса K1K12 13а · вне-претрейн чекпоинт 55) → ВТОРАЯ ПАРА живьём (ja→ru; преп 81) → МАСШТАБ → пилот Ф2.5 (гейт резюме-строки 80; строки 6268, 85) → Ф3 ридер-IDE (6971). **Стоячие:** ToS-триггер 25.10 · Ш-2 до go1.27 · строка 74 перед любым платным прогоном.
> - **Стек:** draft deepseek-v4-flash thinking-ON (+банкнота) **⚠0731** → терминолог (та же модель, батчи, экран `target_script`) → editor deepseek-v4-pro БИЛИНГВ ИНТЕРИМ (вендором НЕ тронут; glm-5 резерв) → судья gemini (Ф2, полигон); канал B Mistral+grok; ~$0.85/ранобэ (D30.4) — пере-калибровка после развилки 74.
> - **ЕДИНЫЙ БЭКЛОГ — секция «Бэклог» ниже** (одна таблица, единственный трекер; каждая петля обязана иметь диспозицию: решено / отложено-с-записью / отклонено; ведёт оркестратор).
@ -30,6 +30,7 @@
| 13б | `speech-cue.txt` zh НЕ шипить до замера голос-флаггера на холодном прогоне (данные для недоказанного детектора = инерция; вывод обсуждения 31.07): оси пустые → файл не нужен, оси ловят → решение владельца о шипе (байты двигают `LangpackVersion`) | владелец | отложено-до-замера | первый прогон добора идеала (оси голоса, строка 24) → отдельное решение | D39.56, PACK19_BUILD §7.2, чат 31.07 |
| 14 | Четыре data-инженерных флага фазы-2 общности, каждый с рекомендованным дизайном (§5 отчёта): ё-фолд → target-данные (`char_fold`; смена сигнатуры core-примитива через вердикт-несущий банк, сдвиг `memory_version`) · segmentation-фолбэк громким (ломает контракт corridor-фолбэка `pipeline.go:839-848` + голден-фикстура на нём; лечение — объявить блок в голден-yaml) · `Fertility` per-script map (калибровка ждёт боевого токенизатора) · `source-encoding.txt` + Go-реестр декодеров | владелец | когда-нибудь (следующий пак общности) | отдельное решение | D39.64, GENERALITY_PHASE2 §5.15.4 |
| 86 | Мелкие открытые решения одним списком (свип D39.66): echo-семантика дропнутого c-lite-члена (D39.18 — оставить pre-c-lite?) · magnitude-кап тяжёлой CJK-утечки (D38.3 п.4в; сейчас strip+flag) | владелец | когда-нибудь | отдельные решения | D39.66, D39.69(feed_cap закрыт §1) |
| 91 | Decl-приёмка МЯГКОЗНАКОВЫХ имён (finding-1 фикс-пака, D39.75): стеммером НЕВОЗМОЖНА — «ь» неотличим от коллизии «Синь»↔«синий» (доказано контрфактикой, регресс-гвозди стоят); выбор: (а) сид/терминолог перечисляет `decl.forms[]` для -нь/-ль имён (ложится на Decl-эмиссию) · (б) anchor-gated матч; при решении добавить прямой тест-гвоздь на «Фан Юаню» и заменить инертную пару Линь/линию→Линь/линий | владелец → бэкенд | скоро (малый дизайн) | отдельное решение | D39.75, BANK_FIXPACK §а |
| **— ХВОСТЫ ХОЛОДНОГО ПРОГОНА (исполнен достаточно, D39.58; Р7 владельца) —** | | | | | |
| 16 | Полная цена холодного старта: edit-волна не гонялась (Р7); измерено всё ДО редактуры (драфт+банкнота $0.0247/волна · терминолог $0.0060.013/проход) | бэкенд | когда-нибудь (первый прогон добора идеала) | добор идеала | D39.58 |
| 18 | Эмиссия головы 蛊: сид-половина = чек-лист (определяющий терм книги сидится `allow_short`, D39.50); эмиссия-КАНАЛ демотирован до ИЗМЕР-ГЕЙТА (Q7): полигон-замер «меняет ли эмиссия головы рендер композитов» — строить ТОЛЬКО по эффекту и ПОСЛЕ #10 (однознаковый пост-чек чинится чекер-паком); перед стройкой резолвить K-гейт и KWIC-грязь одиночной головы | полигон → бэкенд | скоро (санкция Q7 D39.70 — после #10, очередью с ре-пробой) | полигон-замер → решение | D39.58, D39.60, D39.69 |
@ -37,8 +38,7 @@
| **— ТЕКУЩАЯ ОЧЕРЕДЬ (D39.59) —** | | | | | |
| 74 | ⚠ **DeepSeek-V4-Flash-0731: боевая черновая форма покупает пустые ответы — блокер ПЛАТНЫХ прогонов.** Оперативное состояние = «(в) ждать» (D39.63; фаза-2 всё равно $0-код). Перед следующим платным прогоном — ре-проба flash за копейки → если не устаканилось, выбор: (а) флор 16000 (сдвиг request_hash безболезнен — перекупок на этой фазе нет; хвост не закрывает, упор 2/5) или (б) слать `reasoning_effort` явно (вендорская модель, дешевле ~22%; но амендмент `echoMineViolation`, эхо ~6% и качество черновика при `low` не судилось — нужен замер ~$0.05; тогда же предмет для постинструкции/префилла); включение ручки #77 (ре-ген эха перед эскалацией, построена D39.64, дефолт 0) — тем же решением; при включении помнить общий счётчик attempt с length-ре-геном + рост `maxTokensForAttempt`; остаток §12.7 (8 несудимых fidelity-пар ≈$0.10 · ja-вход ≈$0.005 · prefill-continuation research/21 §1.17) — вернуться при предмете wiring; ⚠ cap 8000 бьёт и СТАДИЮ ТЕРМИНОЛОГА (reasoning 8063/батч, D39.65) — резолюция обязана накрыть терминолога; тем же заходом: платный порог приёмки §2 классификатора (6/6 harm-набор, ~$0.01, D39.69) + полигон-слоты Q2 (36б-замер) и Q7 (эмиссия головы, после #10) | владелец (по ре-пробе) | перед следующим платным прогоном | ре-проба → решение | D39.61, D39.63, D39.64, POLYGON_PROMPTLANG_ANTIECHO §12.4 |
| 78 | Леджер денег = НИЖНЯЯ граница: «2xx body decode failed (call IS billed)» — провайдер списал, исходная попытка в `request_log` НЕ попадает (≤$0.009/сессия); дизайн фикса ГОТОВ (GENERALITY_PHASE2 §5.6: `BilledDecodeFails` + per-billed-attempt settle с `Estimated=true`), но это money/ledger-путь — не $0-хастл | бэкенд | когда-нибудь (money-паком) | отдельное решение (дизайн §5.6) | D39.61, D39.64 |
| 79 | Остаточные Cyrillic-хардкоды пар-гейтед DC-чекеров: `checks/repair.go:131` (трим DC1-спана) · `checks/checkers.go:457,474` (`isCyrLetter` словограница register) — инертны без dc-данных пары, но пара С DC-чекерами и не-ru целью потребует правки Go; кандидат на `c.wordScript` ; + lintNumberMagnitude гейтить по объявленному письму источника (находка §8 карты D39.60) | бэкенд | когда-нибудь (следующий пак цель-оси) | ближайшее касание чекеров | D39.64 |
| 87 | Фикс-лист банк-пака (D39.69, приёмка): **«ь» (и класс -нь/-ль) в `decl_suffix` — приёмочный кейс «Фан Юаню»→«Фан Юань» сейчас ПРОВАЛИВАЕТСЯ (одна строка данных + пере-захват голдена)** · комментарий `Batch` «request hash independent of ordering» неверен (ChunkIdx фолдится — поправить) · оверсайз-серия одним батчем (помнить мину cap-8000 терминолога) · classify_types при выключенном terminology-гейте = тихий no-op + config-тесты classify-веток · мутационный тест neuter-фолда · voice-checker без стеммера (решить: заводить или объявить не-нужным) · дистилл-фикстура BANK-FULL (пин «4 серии») · `series`/`head_final` → строкой в `lang-script.txt` (сейчас Go-константа CJK-класса — D39.70) · консолидация `lang.TokenizeWords``text.TokenizeScript` · промпт-инструкция роли «реалии — смыслом, транслит — именам» (Q5, данные пар-промпта) | бэкенд | в работе (промт выдан D39.71; ПЕРВЫМ, чекеры после) | `BACKEND_BANK_FIXPACK_SESSION_PROMPT.md` | D39.69 |
| 79 | Остаточные Cyrillic-хардкоды пар-гейтед DC-чекеров: `checks/repair.go:131` (трим DC1-спана) · `checks/checkers.go:457,474` (`isCyrLetter` словограница register) — инертны без dc-данных пары, но пара С DC-чекерами и не-ru целью потребует правки Go; кандидат на `c.wordScript` ; + lintNumberMagnitude гейтить по объявленному письму источника (находка §8 карты D39.60) ; + граница ПЛОТНОСТИ письма для серий: `enabled` через Go-константу `cjkScriptNames` (head-finality уже в данных script-series.txt; новый плотный скрипт требует правки Go — пре-существующая граница, D39.75) | бэкенд | когда-нибудь (следующий пак цель-оси) | ближайшее касание чекеров | D39.64 |
| 89 | Остатки карты общности D39.60 без носителя (пропуск свипа, D39.72): ведро D «удалить» — живые: указатели на несуществующий `checkers_zh_ru.go` (cheapgates.go:56,125,130 · langpack.go:73+) · ложная шапка checkers.go:22-25 · дубль isHeaderContentRune↔isHeaderSeparator · minerPackVersion «zh-universal-v1» · глифы ⟨проверить⟩ tmctl/render · вес palladius miner.go (дюжину сверить по карте §3 — часть могла уйти фазой-2) + сверка ведра B (6 «пар-модуль без гейта») против фазы-2 | бэкенд | скоро | пакет-чекеров (промт дополнен) / мелкая пачка | D39.60, D39.66, D39.72 |
| 90 | Ф2-кандидаты 02-mvp-плана без носителя (свип-хвост, D39.73): CometKiwi/T-index метрики судьи · Batch API судьи (экономия) · llama-server-интеграция локали · стриминг — взять/закрыть при пилот-препе | полигон/бэкенд | когда-нибудь (Ф2.5) | пилот-преп-промт (с 85) | 02-mvp-plan, D39.66, D39.73 |
| **— НАХОДКИ СВИПА ПОЛНОТЫ (D39.66: 951 обязательство проверено, потери возвращены в трекер) —** | | | | | |
@ -104,6 +104,10 @@
| 69 | Gemini API «под-18» обязательство на конечный продукт (независимо от лейблов) | владелец | когда-нибудь | Ф3 / отдельное решение | D39.32, D39.34(7) |
| 70 | Action-security gate перед выдачей tools/webfetch (D25 п.5) | бэкенд | когда-нибудь | Ф3 | D39.34(7) |
| 71 | Планы research/22: epub-tag-rewrite · Q7-леджер ; + F3-brief из D29.3 (chat-edit · Not-useful-петля · cost-of-fix) + Ф3-скоуп 02-mvp-plan (TMX/TBX · Bertalign · дистилляция 714B) | бэкенд | когда-нибудь | Ф3 | D39.34(7) |
## Оркестратор №9 + бэкенд — ФИКС-ПАК БАНКА ПРИНЯТ И ЗАЛЕНДЕН (D39.75), 02.08
Отчёт: [`archive/reports/BANK_FIXPACK_2026-08-02.md`](archive/reports/BANK_FIXPACK_2026-08-02.md) (ревью-шапка). 9/9 пунктов отработаны: 6 сделано (head-finality в данных · правдивый Batch-коммент+WARN · classify fail-loud+тесты · мутационные neuter-тесты · фикстура 4-серий · TokenizeLetters-консолидация, побочно закрывшая combmark-щель), 2 «не делать» с контрактом (voice×стеммер; Q5 уже в промптах с D39.45), 1 ОТКАЧЕН правильно — «ь» ловил бы коллизию Синь/синий (мандат самопроверки сработал: адверсариальный ревьюер сессии поймал до лендинга). Верификация: батарея зелёная, голден маскированно ПУСТ, 14/15 CONFIRMED. Строка 87 закрыта; finding-1 → строка 91 (владелец); плотность-граница → дописка 79. Очередь: ПАКЕТ-ЧЕКЕРОВ можно запускать.
## Оркестратор №9 — АУДИТ tmp-РЕСЁРЧЕЙ 21/22 (D39.74): утерянного нет, два хвоста моего триажа добиты, 02.08
По вопросу владельца о ресёрчах соседних харнессов (/home/ubuntu/projects/tmp, 15 клонов): research/21 транспорт — 9 пунктов построено паками 12/13, вердикт «транспорт не трогаем» ратифицирован, GLM-overflow/prefill-continuation уже несены (59/74), Q7-квирки — только после вендор-сверки (правило стоит); research/22 домен — Q6 gating+repair построен паком-16, Problem-классы в 25, planning в 71. Хвосты пучков моего триажа добиты: Mistral-strip гейт → дописка 59 (латентен при single-shot wire; жив для канала B/multi-turn); halve-chunk-on-retry №7 — ОТКЛОНЁН явно (волны+чекпойнты+флор делают малоценным; реопен при упорах — строка 45).
@ -176,6 +180,8 @@ Q1 = П1 цель-шов санкционирован («главное бэке
## Бэкенд
**Фикс-пак банка (строка 87 + Q5) исполнен (02.08, бэкенд-сессия по `BACKEND_BANK_FIXPACK_SESSION_PROMPT`, D39.71, $0 — только фейк-провайдер в тестах, НЕ закоммичено).** Отчёт: [archive/reports/BANK_FIXPACK_2026-08-02.md](archive/reports/BANK_FIXPACK_2026-08-02.md). СДЕЛАНО: **(б)** head_final → данные (`script-series.txt`+`SeriesHeadFinal`, zh/ja/ko байт-идентичны, тест «плотный не-head-final заводится данными») · **(в)** лживый коммент `Batch` переписан (ChunkIdx фолдится в request-hash) + `BatchRunes`-WARN на оверсайз-серию (не резать — убьёт со-батч §1) · **(г)** classify_types при выкл.гейте = fail-loud + 5 config-тестов · **(д)** мутационный neuter-фолд (mined-scope) · **(ж)** дистилл-фикстура `BANK-FULL` пинит ровно 4 серии (转/等/等资质/阶) · **(и)** `lang.TokenizeWords``text.TokenizeLetters` (общий шов, чинит combmark-щель). НЕ ДЕЛАТЬ с обоснованием: **(е)** стеммер НЕ в voice (accept-bias не единонаправлен; контракт в код) · **(з)** Q5-реалии УЖЕ в `terminologist.md`/`classifier.md` (пинг: `PromptSHA256` не двинулся). **(а) ОТКАЧЕНО — предложенный «ь» ФЛАВЕД: адверсариальное ревью (author≠reviewer, подтверждено white-box) поймало коллизию «Синь/Линь»→«син/лин»=«синий/линия» → ложный `SpoilerLeaks` (шум); «юань»→«юан» неотличим по форме — min-stem не разделяет.** Откат + регресс-гвоздь; finding 1 (палладий) открыт в safe-направлении, нужен сид-decl-перечень или anchor-gated матч — **РЕШЕНИЕ владельца (пинг §а)**. Приёмка: `build`/`vet`/`test -race ./...` зелёные · майнер-парити EXACT · голден пере-захвачен, маскированный дифф ПУСТ (двинулся только `embedded_version`; `memory_version`/`prompt_sha256`/`content_hash`/вердикты — на месте) · греп-общность чист. **СТОП — приёмка оркестратора.**
**Качество банка — этап 1 (дизайн) исполнен (01.08, бэкенд-сессия по `BACKEND_BANK_QUALITY_SESSION_PROMPT`, $0, read-only, НЕ закоммичено).** Отчёт: [archive/reports/BANK_QUALITY_DESIGN_2026-08-01.md](archive/reports/BANK_QUALITY_DESIGN_2026-08-01.md). 6 пунктов свипа сведены в **3 переиспользуемых примитива** (A head-aware кластер-по-морфеме: §1 серия-батчинг + §6 голова 蛊 · B ре-порядок `terminology.Batch`: §1 + feed_cap · C стеммер целевого письма: §3 Decl-шов + omission-бэкстоп чекеров) + дата-правки (тип — ОТДЕЛЬНЫМ классификатором первичным; род neuter enum+seed-lint, ось `RenderFormatVersion`). **Самопроверка — 3 рубежа (грунтинг + анкор-верификация всех 8 tech-debt-якорей CONFIRMED + 5-агентная адверсариальная дизайн-критика): §1 серия-дефиниция «равная длина/1 позиция» и §2 инлайн-тип были BROKEN — переработаны (§1 → head-aware, пере-верифицировано на `BANK-FULL.tsv`: наивное = 19 кластеров вкл. блоб с §2-harm+протагонист, head-aware = 4 годных серии; §2 → классификатор первичным, DO-роль ДО RenderBatch; $0-экран честно пере-скоупнут — провабельно пропускал юаньши/Юаньхай); §5/§6/§7-эмбеддинг WEAKENED вправлены.** §4 (36б второе мнение) и §7-эмбеддинг ЗАКРЫТЫ по нужности; §5 (резюме-слой 80) → пилот-Ф2.5-гейт (проза-суммарайзер закрыт; «дата-состояние свободно от D1» опровергнуто — source-anchored reveal без верификатора); §6-эмиссия головы → полигон-замер + после #10 (seed-половина строится). **First-occurrence-тест ИСПОЛНЕН** ($0, `eval/bank_autonomy/m_firstocc.py`): гипотеза первенства ОПРОВЕРГНУТА (финал==первое 7 == финал==частота 7, большинство — ни то ни другое) ⇒ роль = контекстный ре-решатель, усиливает §1 (со-батч — единственный рычаг). **7 вопросов владельцу** (§9: канон-пин между прогонами · замер 36б · диспозиция резюме · hard-инъективность · вкус авто-транслита реалий юаньци/цзин-ци-шэнь · короткая форма 27 · замер эмиссии головы). **СТОП — ратификация оркестратора/владельца; этап 2 (стройка) — по релею после лендинга пакета-чекеров.** → **РАТИФИЦИРОВАНО ВЛАДЕЛЬЦЕМ, ЭТАП 2 (СТРОЙКА) ИСПОЛНЕН (01.08, та же сессия, $0 — только фейк-провайдер в тестах, НЕ закоммичено, resnapshot НЕ запускался).**
**Качество банка — этап 2 (стройка) исполнен.** Построены 3 примитива дизайна: **§1 серия-батчинг** (`terminology/series.go` `DetectSeries` head-aware + `Batch` серия-целиком + value-порядок feed_cap; пар-данные `lang.SeriesMorphology` из CJK-письма — инертно для алфавита; **пере-верифицировано на живом `BANK-FULL.tsv`: ровно 4 годных серии, ноль блоба**). **§2 тип-классификатор первичным** (новая фаза перед рендером на общем `runBankRoleBatches` — денежный цикл терминолога зарефакторен в общий, чекпойнт-хеш идентичен; корректный тип роутит conform + праймит RenderBatch + банкуется через `attachClassifiedType` БЕЗ смены эмиссии=без реоткрытия D39.50; своя роль/бюджет/промт `classifier.md`; честный $0 label-экран). **§3 Decl-шов** (`lang.TargetStemmer` примитив C + ru `decl_suffix` данные → `dstFormPresent` принимает косвенные падежи без перечисления форм; **neuter** enum→`injection.txt`/`genderConstraintNote`; **SeedLint** валидирует род громко; ось — **scoped `hasNeuter`-фолд** в `memory_version` по образцу `editor-unverified-section-v1`, НЕ бланкетный `RenderFormatVersion`). **Верификация (5 независимых сигналов):** `go test ./...` зелёно · `-race` чисто на затронутых пакетах · греп-общность: ноль пар/книго-литералов в Go-логике (новая пара — без правки Go) · голден Ш-1 **пере-захвачен, 172/172 hex-only = вердикт-нейтрально** (EmbeddedDataVersion сдвинулся от данных injection/target-ru) · **адверсариальное ревью author≠reviewer (4 измерения × refute-verify): 0 подтверждённых дефектов.** Короткая форма (27) — уже работает через `decl.forms[]`, кода не требует (§9-Q6 владельцу открыт). §4/§5/§6-эмиссия/§7-эмбеддинг — как в дизайне (не строятся/гейтнуты).

View file

@ -11,17 +11,17 @@
## Структура
- `architecture/` — синтез. **Источник истины по решениям — [`05-decisions-log.md`](architecture/05-decisions-log.md) (D1D39.74); при конфликте с любым доком он выше.**
- `architecture/` — синтез. **Источник истины по решениям — [`05-decisions-log.md`](architecture/05-decisions-log.md) (D1D39.75); при конфликте с любым доком он выше.**
- `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-ответы · [`13-tech-debt-anchors.md`](architecture/13-tech-debt-anchors.md) — якоря техдолга (справочник к бэклогу, НЕ трекер). **Исполненные (архив 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/` — фактура исследований 0405.07: `0110` базовые, `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 сверху, ниже хронология; НЕ источник решений).
- **Активные хендофф-промты (01.08; всё остальное — в `archive/prompts/` с баннерами-исходами):** [`ORCHESTRATOR_SESSION_PROMPT.md`](ORCHESTRATOR_SESSION_PROMPT.md) (роль/нормы; состояние НЕ дублирует) · [`BACKEND_BANK_FIXPACK_SESSION_PROMPT.md`](BACKEND_BANK_FIXPACK_SESSION_PROMPT.md) (**ТЕКУЩИЙ бэкендный, ПЕРВЫМ**: фикс-пак банка, строка 87) · [`BACKEND_CHECKER_PACKAGE_SESSION_PROMPT.md`](BACKEND_CHECKER_PACKAGE_SESSION_PROMPT.md) (пакет-чекеров против labels; ПОСЛЕ лендинга фикс-пака — общие файлы) · [`POLYGON_PACKAGE4_SESSION_PROMPT.md`](POLYGON_PACKAGE4_SESSION_PROMPT.md) (residual-трекер пилота/18+/echo, отложен; P4-хвост из него закрыт D39.61). Закрытые 30.0701.08 (промты в `archive/prompts/`, отчёты с ревью-шапками в `archive/reports/`): пак-19 «голос/состояние» (D39.5456) · холодный мини-прогон (D39.58) · ToS-речек (D39.57) · ресёрч общности фаза 1 (D39.60) · полигон Р6+P4 (D39.61) · **фаза 2 общности (D39.64)** · **полигон «сырьё арки банка»+пробы (D39.65)** · **качество банка, оба этапа (D39.69)**.
- **Активные хендофф-промты (01.08; всё остальное — в `archive/prompts/` с баннерами-исходами):** [`ORCHESTRATOR_SESSION_PROMPT.md`](ORCHESTRATOR_SESSION_PROMPT.md) (роль/нормы; состояние НЕ дублирует) · [`BACKEND_CHECKER_PACKAGE_SESSION_PROMPT.md`](BACKEND_CHECKER_PACKAGE_SESSION_PROMPT.md) (**ТЕКУЩИЙ бэкендный**: пакет-чекеров против labels + чистка ведра D — фикс-пак заленден, МОЖНО ЗАПУСКАТЬ) · [`POLYGON_PACKAGE4_SESSION_PROMPT.md`](POLYGON_PACKAGE4_SESSION_PROMPT.md) (residual-трекер пилота/18+/echo, отложен; P4-хвост из него закрыт D39.61). Закрытые 30.0701.08 (промты в `archive/prompts/`, отчёты с ревью-шапками в `archive/reports/`): пак-19 «голос/состояние» (D39.5456) · холодный мини-прогон (D39.58) · ToS-речек (D39.57) · ресёрч общности фаза 1 (D39.60) · полигон Р6+P4 (D39.61) · **фаза 2 общности (D39.64)** · **полигон «сырьё арки банка»+пробы (D39.65)** · **качество банка, оба этапа (D39.69)** · **фикс-пак банка (D39.75)**.
- `archive/``prompts/`: закрытые сессионные промты (только история, инструкции оттуда не исполнять) · `reports/`: отчёты паков/ресёрч-сессий с ревью-шапками (живая фактура приёмок — на них ссылаются D-лог и активные промты) · `architecture/`: исполненные арх-доки (07 стратревью · 08 синк-ледджер · 11 план пака-11) · четыре слайса хроники `PROGRESS-2026-07-{04-10,10-13,13-25,25-31}.md`.
## Статус (2026-08-01, голова D39.74 — эра «общность → качество банка»)
## Статус (2026-08-01, голова D39.75 — эра «общность → качество банка»)
Полная карта состояния НЕ здесь: фазы/курс/очередь/горизонт — в шапке [`PROGRESS.md`](PROGRESS.md) (CURRENT-STATE + единый бэклог), решения — в [`architecture/05-decisions-log.md`](architecture/05-decisions-log.md) (карта актуальности; свежая голова — с хвоста файла). Коротко: Ф0/Ф1 ✅ · арх-ресет D39 (паки 1116) ✅ · паки 1720 ✅ · мини-прогон и холодный прогон приняты (D39.37/58) · карта языковой привязки движка снята (D39.60: книго-ось чиста; ja→ru безопасна без правки Go, en→ru — нет) · Р1Р4 закрыты (D39.63) · пак-21 растворён (D39.62) · **фаза 2 общности принята (D39.64: цель/скрипт-швы по данным, эмбед-хеш, манифест-по-каналам; голден вердикт-нейтрален)**. Полигон-ветка закрыта D39.65 (строки 21/22, фиксы 36а/36в проверены живьём). Банк-пак принят целиком (D39.69); вопросы §9 закрыты (D39.70, канон целей — в CLAUDE.md). Текущее: фикс-пак банка ПЕРВЫМ (промт выдан D39.71) → пакет-чекеров после. Свип полноты исполнен и пост-сверен кодом (D39.66: 951 обязательство, потери возвращены; якоря — architecture/13). ⚠ DeepSeek-0731 сменил веса под слагом — платные прогоны СТОП до ре-пробы (бэклог-строка 74). Дальше: стройка банка ∥ пакет-чекеров → ре-проба flash → ДОБОР ИДЕАЛА (мини-прогон и свип гипотез влиты, D39.67) → вторая пара (ja→ru) → МАСШТАБ → пилот Ф2.5 → Ф3 ридер-IDE.
Полная карта состояния НЕ здесь: фазы/курс/очередь/горизонт — в шапке [`PROGRESS.md`](PROGRESS.md) (CURRENT-STATE + единый бэклог), решения — в [`architecture/05-decisions-log.md`](architecture/05-decisions-log.md) (карта актуальности; свежая голова — с хвоста файла). Коротко: Ф0/Ф1 ✅ · арх-ресет D39 (паки 1116) ✅ · паки 1720 ✅ · мини-прогон и холодный прогон приняты (D39.37/58) · карта языковой привязки движка снята (D39.60: книго-ось чиста; ja→ru безопасна без правки Go, en→ru — нет) · Р1Р4 закрыты (D39.63) · пак-21 растворён (D39.62) · **фаза 2 общности принята (D39.64: цель/скрипт-швы по данным, эмбед-хеш, манифест-по-каналам; голден вердикт-нейтрален)**. Полигон-ветка закрыта D39.65 (строки 21/22, фиксы 36а/36в проверены живьём). Банк готов целиком (D39.69 ядро + D39.75 фикс-пак; вопросы §9 закрыты D39.70). Текущее: пакет-чекеров — можно запускать; строка 91 у владельца. Свип полноты исполнен и пост-сверен кодом (D39.66: 951 обязательство, потери возвращены; якоря — architecture/13). ⚠ DeepSeek-0731 сменил веса под слагом — платные прогоны СТОП до ре-пробы (бэклог-строка 74). Дальше: стройка банка ∥ пакет-чекеров → ре-проба flash → ДОБОР ИДЕАЛА (мини-прогон и свип гипотез влиты, D39.67) → вторая пара (ja→ru) → МАСШТАБ → пилот Ф2.5 → Ф3 ридер-IDE.
## Доступные ключи от моделей
DEEPSEEK_API_KEY, ZAI_API_KEY, KIMI_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, XAI_API_KEY, MISTRAL_API_KEY

View file

@ -1,4 +1,4 @@
# Журнал решений оркестратора — контракт D1D39.74 (развязки 04.07 · пакеты 0910.07 · приёмка/качество-первым/пивот/эмпирика 1112.07 · арх-ресет+стройка пере-прогонного стека 1319.07)
# Журнал решений оркестратора — контракт D1D39.75 (развязки 04.07 · пакеты 0910.07 · приёмка/качество-первым/пивот/эмпирика 1112.07 · арх-ресет+стройка пере-прогонного стека 1319.07)
> **КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Читая контракт целиком, держи под рукой, что чем перекрыто:
> ⚠ **Навигация (актуализация 01.08):** карта ниже детально покрывает D1D39.28; решения D39.2963 живут хронологически в теле файла, **свежая голова — С ХВОСТА** (новые ноты аппендятся вниз). Сводка текущей головы и очередь — CURRENT-STATE в `../PROGRESS.md`.
@ -1225,3 +1225,5 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу
## D39.73 — РЕКОНСИЛЯЦИЯ ТРИАЖА: ПОЛНАЯ МАШИННАЯ СВЕРКА НАХОДОК ПРОТИВ НОСИТЕЛЕЙ (владелец 02.08 «что ещё проебал? технические решения сессий проебал?»): все 77 находок свипа D39.66 + рекомендации пост-чека + фикс-листы приёмок D39.6471 прогнаны механически против живых строк/дописок/закрытий. **Вердикт по решениям СЕССИЙ: целы** — их держал 951-позиционный свип (92% доказанных диспозиций) и адверсариальный пост-чек кодом; **провал локализован в ТРИАЖЕ оркестратора**: из ~95 рекомендаций уронено 5 (ведро D — уже исправлено D39.72; идиоматика-словари сехоуюй/васэй-эйго/вэньянь → дописка 52; Ф2-хвосты 02-mvp CometKiwi/T-index · Batch API судьи · llama-server · стриминг → строка 90 к пилот-препу; arity-полоса паспорта D29.2г → дописка 55; платный порог приёмки §2 «6/6 harm» жил только в гниющей шапке → дописка в строку 74 с полигон-слотами Q2/Q7). 7 из 8 греп-кандидатов оказались ложными (носители живы: порог-3 в 25 · lintNumberMagnitude в 79 · источник 74 · триггер 12 · линк 02-mvp починен · F3-brief в 71 · echo-R6 в PACKAGE4). Норма-урок: триаж многоисточниковых находок завершать МАШИННОЙ сверкой «каждая рекомендация → носитель», не глазами (вписано в промт-нормы оркестратора) (02.08, оркестратор №9). ✅
## D39.74 — АУДИТ РЕСЁРЧЕЙ СОСЕДНИХ ХАРНЕССОВ (research/21 транспорт · research/22 домен; вопрос владельца 02.08 «есть ли утерянное из /home/ubuntu/projects/tmp?»): **утерянного НЕТ** — из 15 клонов оба ресёрча покрыли корпус, взятое ПОСТРОЕНО (research/21: 9 транспорт-пунктов паками 12/13 — quota-429/Retry-After/keepalive/джиттер/xAI-гард/GLM-finish/retryable-пин/billed-decode-кап/models-валидация + CostSource и луп-гард; вердикт «наш транспорт опережает/вровень со всеми 11, не трогаем» ратифицирован; research/22: разрыв Q6 gating+repair закрыт паком-16 по прецеденту AiNiee ProofreadTask, Problem-классы №5 → строка 25, планы → строка 71), отклонённое отклонено ЗАПИСЬЮ, кандидаты Q7-квирков ждут вендор-сверки по правилу 10.07 (не абсорбированы — так и задумано). Два хвоста, уроненные МОИМ триажем внутри пучковых находок (класс D39.73), добиты: **(1) Mistral-strip гейт message-builder** (research/21 §1.1: extra_forbidden на reasoning-поля; ЛАТЕНТЕН — наш wire single-shot без reasoning-полей; станет несущим при multi-turn/continuation или прод-пути mistral в канале B) → дописка в строку 59 (транспорт-гигиена); **(2) halve-chunk-on-retry** (research/22 §2 №7) → ОТКЛОНЁН ЯВНО: волновая архитектура с чекпойнтами + флор max_tokens + ре-ген-ручка 77 делают полу-чанк малоценным; реопен — при живых max_tokens-упорах (строка 45, дописка) (02.08, оркестратор №9). ✅
## D39.75 — ФИКС-ПАК БАНКА ПРИНЯТ И ЗАЛЕНДЕН (все 9 пунктов строки 87 отработаны; приёмка исполнением + 3-агентное адверсариальное ревью: 14/15 CONFIRMED · 1 PARTIAL · 0 REFUTED; голден независимо: маскированный дифф ПУСТ, двинулся только `embedded_version`): **СДЕЛАНО** — (б) head-finality В ДАННЫХ (`script-series.txt` + `SeriesHeadFinal`; zh/ja/ko функционально идентичны — пин-тест; синтетический head-initial скрипт заводится данными) · (в) лживый комментарий Batch переписан на правду (ChunkIdx фолдится в RequestHash) + `BatchRunes`-WARN на оверсайз-серию (НЕ режет — резка убила бы со-батч) · (г) `classify_types` при выключенном гейте = fail-loud + 5 config-тестов · (д) мутационные тесты neuter-фолда (версия двигается; mined-scope: BASE байт-идентичен — draft-волна не перепокупает) · (ж) дистилл-фикстура BANK-FULL (150 поверхностей побайтно = боевому TSV; тест требует ТОЧНОГО членства 4 серий и id=0 для 方源/元石/元海) · (и) `TokenizeWords``text.TokenizeLetters` — консолидация чистая (слои однонаправленны) И ПОБОЧНО ЗАКРЫЛА РЕАЛЬНУЮ ЩЕЛЬ: старый токенизатор рвал слово на mid-word U+0301 («сло́во»→[сло во] — воспроизведено на копии старого кода), decl-пост-чек промахивался на ударениях. **НЕ ДЕЛАТЬ с контрактом в код** — (е) стеммер НЕ заводится в voice (направления несимметричны: SelfRef-стемминг безопасен, NG-стемминг = шум; контракт-блок voice.go:368-378, фактура сверена) · (з) Q5-инструкция реалий УЖЕ в `terminologist.md`/`classifier.md` — причём с D39.45 (мисатрибуция отчёта «D39.58» только усиливает клейм), PromptSHA256 не двигался. **(а) ОТКАЧЕН — МАНДАТ САМОПРОВЕРКИ СРАБОТАЛ ПО НАЗНАЧЕНИЮ:** «ь» в `decl_suffix` закрыл бы «Фан Юаню», но неотличим по форме от коллизии мягкознаковых имён с лексикой («Синь»↔«синий/синим» — контрфактика воспроизведена: SameStem=true с «ь»); адверсариальный ревьюер сессии поймал ДО лендинга, откат + два регресс-гвоздя (SameStem + dstFormPresent; пара Линь/линию инертна — заменить на Линь/линий при касании) + NB-комментарий в данных. **Finding-1 ОСТАЁТСЯ ОТКРЫТЫМ → строка 91 (выбор владельца):** приёмка склонений мягкознаковых имён стеммером НЕВОЗМОЖНА — (а) сид/терминолог перечисляет `decl.forms[]` для -нь/-ль имён (естественно ложится на Decl-эмиссию) или (б) anchor-gated матч; + прямой тест-гвоздь на дизайн-кейс добавить при решении. Граница плотности (`enabled` серий через Go-константу `cjkScriptNames`) — пре-существующая, дописана в строку 79. Строка 87 ЗАКРЫТА (02.08, оркестратор №9). ✅

View file

@ -1,8 +1,8 @@
# Промт бэкенд-сессии: ФИКС-ПАК БАНКА (строка 87 + Q5; $0-код, короткий)
**Выдан 02.08.2026, оркестратор №9 (D39.71); санкция владельца («передать записку тем же бэкендерам»).** Ты — бэкенд-сессия TextMachine, продолжение банк-пака D39.69 (если ты свежий инстанс — онбординг: `CLAUDE.md` вкл. НОВЫЙ канон «Цели и мерило» → CURRENT-STATE `docs/PROGRESS.md` → ревью-шапка `docs/archive/reports/BANK_QUALITY_DESIGN_2026-08-01.md` — там все якоря находок). Зона: `backend/` + отчёт. НЕ коммитить. **Первый деливерабл — эхо-блок ≤8 строк** (первым сообщением; подтверждения не жди).
> **⚠ АРХИВ (02.08.2026): исполнен и ПРИНЯТ — D39.75** (9/9 пунктов; (а) откачен по делу — finding-1 → строка 91). Отчёт: [`../reports/BANK_FIXPACK_2026-08-02.md`](../reports/BANK_FIXPACK_2026-08-02.md). Файл несёт живые правки владельца. Инструкции НЕ исполнять.
**$0** (развилка 74). ⚠ **Очерёдность:** этот фикс-пак идёт ПЕРВЫМ; пакет-чекеров стартует после его лендинга (общие файлы membank/prompts). Если в дереве уже есть чужие правки `backend/` — СТОП и пинг. `TM_UPDATE_GOLDEN` — один раз в конце.
**Выдан 02.08.2026, оркестратор №9 (D39.71); санкция владельца («передать записку тем же бэкендерам»).** Ты — бэкенд-сессия TextMachine, продолжение банк-пака D39.69 (если ты свежий инстанс — онбординг: `CLAUDE.md` вкл. НОВЫЙ канон «Цели и мерило» → CURRENT-STATE `docs/PROGRESS.md` → ревью-шапка `docs/archive/reports/BANK_QUALITY_DESIGN_2026-08-01.md` — там все якоря находок). Зона: `backend/` + отчёт. НЕ коммитить. **Первый деливерабл — эхо-блок ≤8 строк** (первым сообщением; подтверждения не жди).
## Состав (= бэклог-строка 87; каждому пункту — краткая вердикт-тройка по мандату D39.68, «не делать с обоснованием» — легитимный исход для (в)/(е)/(и))
@ -18,6 +18,8 @@
## Приёмка (короткая, но обязательная — «ВСЕГДА на важных»)
`go build`/`vet`/`go test -race ./...` зелёные В ФИНАЛЕ · майнер-парити EXACT · голден: пере-захват ОДИН раз в конце, маскированный дифф, отчитать оси (двинутся `embedded_version` от (а)/(б)-данных и `PromptSHA256` от (з) — вердикты двигаться НЕ должны; если двинулись — вскрытые единицы и обоснование) · греп-общность своего диффа · адверсариальное селф-ревью исполнением.
`go build`/`vet`/`go test -race ./...` зелёные В ФИНАЛЕ · майнер-парити EXACT · голден: пере-захват ОДИН раз в конце, маскированный дифф, отчитать оси (двинутся `embedded_version` от (а)/(б)-данных и `PromptSHA256` от (з) — вердикты двигаться НЕ должны; если двинулись — вскрытые единицы и обоснование) · греп-общность своего диффа.
По окончании: селф ревью на алгоритмическую правильность, на алгоритмическое соотвествие стандартам репозитрия и правильность предложенных заранее фиксов.
**Отчёт:** `docs/archive/reports/BANK_FIXPACK_<дата>.md` (компактный: тройки · метрики · голден-протокол) + итог 35 строк в §Бэкенд `docs/PROGRESS.md` (таблицу не трогать). Расхождение промта с кодом = пинг. **СТОП — приёмка оркестратора.**

View file

@ -0,0 +1,53 @@
# Отчёт: ФИКС-ПАК БАНКА (бэклог-строка 87 + Q5) — D39.71
> **✅ ПРИНЯТ И ЗАЛЕНДЕН (оркестратор №9, 02.08.2026, D39.75).** Приёмка исполнением: `build`/`vet`/`-race` все пакеты · майнер-парити EXACT · голден сверен независимо (86/86 строк, маскированный дифф ПУСТ; двинулся только `embedded_version``memory_version`/`prompt_sha256`/`content_hash`/все вердикты побайтно на месте) · 3-агентное адверсариальное ревью: **14/15 клеймов CONFIRMED, 1 PARTIAL, 0 REFUTED**. Ключевое подтверждено контрфактикой: с возвращённым «ь» `SameStem("Синь","синим")=true` — откат (а) верен, регресс-гвозди работают (3 из 4 пар; пара Линь/линию инертна — заменить на Линь/линий при касании); (з) даже сильнее заявленного — инструкция реалий в промптах с D39.45, не D39.58. PARTIAL: дизайн-кейс «Фан Юаню»→«Фан Юань» закреплён только комментарием + анти-коллизионным ассертом — прямой гвоздь добавить при решении finding-1 (строка 91). Combmark-щель (и) воспроизведена живьём на копии старого кода («сло́во»→[сло во]) и закрыта. Ниты: тихий head-final-дефолт незаявленного скрипта (осознанно, все 4 текущих объявлены) · BatchRunes лёгкий overcount (+2/блок, самосогласован). Finding-1 → строка 91 (выбор владельца); граница плотности `cjkScriptNames` → дописка 79. Строка 87 ЗАКРЫТА.
**Дата:** 02.08.2026 · **Сессия:** бэкенд (продолжение банк-пака D39.68/69) · **Зона:** `backend/` + этот отчёт · **Деньги:** $0 (ни одного платного вызова; `--resnapshot`/`--resnapshot`-эквивалент не запускался; голден пере-захвачен фейк-провайдером) · **Статус:** НЕ закоммичено — лендит оркестратор.
## Вердикт-тройки (по мандату D39.68; «не делать с обоснованием» — легитимно для в/е/и)
| # | Пункт | Вердикт | Что сделано / обоснование |
|---|---|---|---|
| **а** | `decl_suffix` +«ь», палладиевский класс | **ОТКАЧЕНО — предложенный фикс ФЛАВЕД (адверс. находка) → ПИНГ** | Bare «ь» был добавлен, затем ОТКАЧЕН: адверсариальное ревью (подтверждено white-box) нашло, что стрип «ь» неотличим по форме от ВРЕДНОЙ коллизии. «юань»→«юан» (нужный выигрыш) и «синь»→«син» (коллизия) — идентичная форма 4-рунного мягкознакового имени→3-рунный стем, ни одно min-stem-правило их не разделяет. Топ-фамилии Палладия «Синь/Линь» (辛/信/新, 林) схлопываются на «син/лин» = стемы общих слов «синий/линия»: `Bank.SpoilerLeaks` на отклонённой «Синь» выдаёт ЛОЖНЫЙ спойлер-лик на любом «синий» (направление ШУМА, против precision-дисциплины); `Postcheck` маскирует дроп «Синь» на любом «синем» (safe-направление, но частота много выше допущения D24.4 — «имена не рекуррируют случайно»; тут рекуррирует ОБЩЕЕ слово). Симметрия «нь/ль/й/ью» проанализирована, но нерелевантна — корень в самом «ь». РЕШЕНИЕ: не тащить коллизию; NB-коммент в `target-ru.txt` + регресс-гвоздь `TestSoftSignNamesDoNotCollideWithCommonWords` (падает, если «ь» вернут). Finding 1 (палладий) остаётся ОТКРЫТЫМ в safe-направлении — правильный путь пингом. |
| **б** | `head_final` → данные | **СДЕЛАНО** | head-finality вынесена из Go-константы в `data/script-series.txt` (`head_position<TAB>script<TAB>final\|initial`). `SeriesMorphology` резолвит head-final через `SeriesHeadFinal(script)` (данные; дефолт для незаявленного плотного скрипта = final, CJK-норма). Плотность (`enabled`) по-прежнему `cjkScriptNames` — предсуществующая граница общности, НЕ трогалась (пинг ниже). Тест: `TestSeriesMorphologyUnchanged` (zh/ja/ko→true,true; en/ru/unknown→false,false) + `TestSeriesHeadFinalIsDataDriven` (синтетический `initial`-скрипт резолвится head-initial без правки Go; corrupt-строки — loud). |
| **в** | Лживый коммент Batch + оверсайз-серия | **СДЕЛАНО (WARN)** | Коммент Batch переписан на правду: порядок ДЕТЕРМИНИРОВАН (стабильная сортировка по чистой функции контента) → $0-resume держится, НО ординал батча фолдится в request-hash (потребитель адресует batch i как ChunkIdx i), значит хеш НЕ независим от порядка (смена частот → сдвиг ординалов → перепокупка затронутых батчей). Оверсайз-серия: вердикт **WARN, не резать** (резать серию убивает со-батчинг §1; тихо оставить — против дисциплины «no silent degradation»). Добавлен `terminology.BatchRunes` + WARN в терминологисте, называющий over-cap-юнит (мина cap-8000). Тест: `TestBatchRunesFlagsOversize`. |
| **г** | `classify_types` при выкл. гейте | **СДЕЛАНО** | При `enabled:false, classify_types:true` — раньше ТИХИЙ no-op (`runClassifier` достижим только через включённый гейт, и валидация была вложена в `if tg.Enabled`). Теперь `else if tg.ClassifyTypes → bad(...)` (fail-loud, класс «гейт, который не может выстрелить»). Тесты: valid-load · нет classifier.md · budget≤0 · неизвестная classify_model · **выкл.гейт+classify_types** (`terminology_classify_test.go`). |
| **д** | Мутационный тест neuter-фолда | **СДЕЛАНО** | `memneuter_test.go` по образцу memvoice-пина: `TestNeuterRowMovesTheBankVersion` (строка gender:neuter двигает memory_version) + `TestNeuterFoldRespectsTheMinedScope` (mined-only neuter фолдится в enriched, НЕ в base — draft-волна не перепокупает; гвоздит порядок `hasNeuter`-после-`continue`). Честная оговорка в комментарии теста: тег `neuter-directive-v1` НЕ black-box-изолируем от уже-фолдящегося поля gender — тесты пинят наблюдаемый контракт. |
| **е** | Voice-checker × стеммер | **НЕ ДЕЛАТЬ (контракт в код)** | Стеммер НЕ заводится в voice-канал. Обоснование (записано в `voice.go`): имена уже приходят склонённо-осведомлёнными (`Forms` = база + перечисленные decl-формы, тот же `decl.forms[]`); accept-biased стеммер поверх добавил бы лишь риск стем-коллизий/мис-атрибуции чекеру с доктриной precision-over-recall. Для SelfRef/NG стеммер НЕ единонаправленно безопасен: стем SelfRef подавил бы ложный self-ref-флаг (безопасно), но стем NG РАСШИРИЛ бы матч запретной леммы (шум) — противоположные направления на одном чекере. Склонение SelfRef/NG — забота ДРАЙВЕРА (перечислять формы из сида, как уже Forms), не match-time стеммера. |
| **ж** | Дистилл-фикстура BANK-FULL | **СДЕЛАНО** | Поверхности `books/gu-zhenren/coldrun-a/BANK-FULL.tsv` (150) → `terminology/testdata/bankfull-surfaces.txt` (пар тест-данные). `TestDetectSeriesOnBankFullFixtureIsStable` пинит ТОЧНО 4 серии по реальному голова-скелету: 转-ранг {一二三四五六九转} (7), 等-грейд {甲乙丙丁等} (4), 等资质 {甲乙丙等资质} (3), 阶-тир {初中高阶} (3). «Ноль блоба» = каждая серия РОВНО равна ожидаемой группе (лишний член = провал), а не эвристика «≤N членов» (легитимная 7-членная ранг-серия — не блоб). 方源/元石/元海 вне. |
| **з** | Q5 — реалии в пар-промпты | **НЕ ДЕЛАТЬ (уже есть) → ПИНГ** | Инструкция «реалии по смыслу; транслитерация — именам» УЖЕ присутствует в `terminologist.md` §26-27 (закоммичено с D39.58) и `classifier.md` §10-27 (с harm-примером 元石→«юаньши»). Это ровно те два реалия-рендерящих промта, что НЕ снапшот-фолдятся (терминология-гейт) → «перекупок нет» уже соблюдено. Переводчик/translator-banknote рендерят реалии инлайн, НО их промт снапшот-фолдится (draft-стадия) → правка перекупила бы draft-волну, против «перекупок нет» → исключены сознательно. Итог: `PromptSHA256` НЕ двинулся (голден подтверждает). |
| **и** | Дубль токенизации | **СДЕЛАНО (консолидация)** | `lang.TokenizeWords` теперь делегирует в новый `text.TokenizeLetters` (script-agnostic, combining-mark-aware), разделяющий ядро с `text.TokenizeScript`. Слой соблюдён: `lang` импортирует `text` (не наоборот; цикла нет — сборка/vet зелёные). Побочно чинит combining-mark щель в decl-пост-чеке (стресс-акцент больше не рвёт слово). Тест: `text.TestTokenizeLetters` (в т.ч. совпадение с TokenizeScript на односкриптном тексте). |
## Метрики / приёмка
- `go build ./...` · `go vet ./...`**зелёные**.
- `go test -race ./...`**зелёные, все пакеты** (в т.ч. `internal/miner` майнер-парити EXACT, `internal/pipeline` голден).
- Новые тесты: lang (2 функции series + soft-sign регресс-гвоздь), config (5 classify-веток), membank (2 neuter + анти-коллизия «Синь»), terminology (BANK-FULL фикстура + BatchRunes), text (TokenizeLetters).
## Голден-протокол (пере-захват ОДИН раз в конце)
- `TM_UPDATE_GOLDEN=1 go test ./internal/pipeline/ -run TestGoldenDeterminism` — 86 строк изменилось.
- Маскированный дифф (`sed 's/^[+-]//; s/[0-9a-f]{12,}/HEX/g' | sort | uniq -u`) — **ПУСТ**: каждое изменение отличается ТОЛЬКО в hex-токенах → вердикт-нейтрально.
- Оси (HEAD vs new, точечная сверка; голден пере-захвачен ДВАЖДЫ — второй раз после отката «ь», финальный авторитетный):
- `embedded_version`: `embed-v1-37bf9699835f``embed-v1-3df4329e3b9e`**ЕДИНСТВЕННЫЙ корень** (NB-коммент `target-ru.txt` (а) + новый `script-series.txt` (б); функциональных данных «ь» в файле НЕТ после отката).
- Каскад: `snapshot_draft`/`snapshot_edit`, все request-хеши (`final_hash` = «request_hash авторитетного чекпойнта», `chunkstatus.go:31` — АДРЕС, не хеш текста; request-log `hash=`) — сдвинулись как адресация, фолдящая снапшот.
- **НЕ двинулись**: `memory_version`, `content_hash`, `prompt_sha256` (⇒ (з) не правил промт), `render_format_version`, и ВСЕ dispositions/flags/finish/degraded/cost/tokens.
- Общность: греп ДОБАВЛЕННЫХ engine-Go-строк (без комментов/тестов/данных) на CJK/пар/книго-литералы — **пусто**. Движок остаётся общим (пара не-из-репо заведётся без правки Go; head-position — данные).
## Самопроверка исполнением + адверсариальное ревью (author≠reviewer)
Самопроверка (алгоритмы · соответствие стандартам репо · правильность предложенных фиксов): соло-проход пере-верифицировал каждый пункт исполнением (тесты · маскированный голден · греп-общность · точечная сверка осей) и не нашёл дефектов сам.
Адверсариальное ревью (Workflow `wf_fea3802b-5a1`: 5 refute-biased ревьюеров по измерениям × refute-verify; 7 агентов, 0 ошибок):
- **1 ПОДТВЕРЖДЁННАЯ находка (medium) — акцептирована и ИСПРАВЛЕНА:** коллизия bare «ь» (пункт (а) выше). Ревьюер репродуцировал white-box против реального кода; я независимо пере-верифицировал (`SameStem("Синь","синим")=true`, `dstFormPresent(«Синь», …синим…)=true`; до диффа — false). Действие: откат «ь» + регресс-гвоздь + пинг. ЭТО и есть ценность мандата само-проверки — флавед-фикс пойман ДО лендинга.
- **1 ОТВЕРГНУТА (refuted):** критика комментария об исключении «й» — верно по механике (пре-существующий safe-направленный miss на не-гейтящем флаггере), но НЕ дефект, вносимый диффом; исключение «й» — осознанное задокументированное решение (и «й» всё равно не закрыл бы «Юаньхай» из-за коллизии с «-ая»).
- **4 из 5 измерений (б·в+и·г+д·общность/ж/е/з): findings пусты** — 0 дефектов.
## Пинги оркестратору (расхождение промта с кодом · открытые решения)
1. **(а) finding 1 НЕ закрыт — нужен НЕ-стеммерный механизм (РЕШЕНИЕ владельца).** Предложенный «ь» флавед (см. выше). Палладиевский мягкознаковый gap («Фан Юаню»~«Фан Юань») требует одного из: **(1) сид-перечисление decl-форм** для мягкознаковых имён собственных (тот же драйвер-путь, что записан для (е) — точный матч, коллизия невозможна; вне бэкенд-скоупа, задача полигона/сида); либо **(2) anchor-gated soft-final матч** — новая ДАННЫЕ-категория мягких окончаний + правило «декл-матч требует ≥1 ТОЧНО совпавшего компонента-якоря», так что одиночные «Синь» без якоря не матчатся, а «Фан Юань» матчится через якорь «Фан». Вариант (2) — новое ДИЗАЙН-решение (по моему уроку — валидировать на корпусе и ратифицировать, не совать в фикс-пак реактивно). Прошу выбор владельца.
2. **(з) уже сделано:** Q5-инструкция реалий уже в `terminologist.md` §26-27 (D39.58)/`classifier.md` — фикс-пак предполагал её отсутствие. Правки не требуется; `PromptSHA256` не двинулся (в отличие от ожидания приёмки).
3. **(б) граница общности:** данными вынесена ТОЛЬКО head-finality; `enabled` (плотность) по-прежнему piggyback на Go-константе `cjkScriptNames` — новый плотный скрипт всё ещё требует Go-строки там (предсуществующая граница, вне (б)-скоупа). Полной дата-общности серий — отдельная задача, если нужна.
4. **`docs/BACKEND_BANK_FIXPACK_SESSION_PROMPT.md`** в рабочем дереве несёт ЖИВУЮ правку владельца (снят «$0/очерёдность»-абзац, переписана строка приёмки на «селф ревью») — НЕ моя, не трогал (чужой незакоммиченный файл).
**СТОП — приёмка оркестратора.**