From 6796de7ce4eb28d94bdecbea0f775ae4c8fa716c Mon Sep 17 00:00:00 2001 From: liangbo Date: Sun, 20 Sep 2026 20:22:23 +0800 Subject: [PATCH 1/3] docs(readme): derive the memory inventory counts instead of restating them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README.md and its seven translations state three counts that live in source: retained stores, observed buffers, and state-store registrations. Nothing compared them, so all eight pages have advertised 12 and 24 since the block was written (`6c14e3433`, 2026-08-13). `usage_snapshot` became the thirteenth retained store the next day, and three more state-store registrations landed after that. The counts now come from the rosters the runtime actually registers, and `tests/ci-workflows/docs-readme-memory-inventory.test.ts` asserts them per page. Each page is anchored by a locale-specific label rather than by the digits, so a reworded sentence fails loudly and asks to be re-anchored instead of silently matching nothing. Each claim captures the number it states rather than searching for the derived value, because `toContain` would also match the `60 s interval` later in the same bullet. The opening sentence loses its total. It claimed "36 categories of process-retained state" while the three rosters below it summed to 40 when it was written and 45 now, and two of the bullets are not registrations at all — the last describes a ledger that keeps no process-level RAM index. A total over that set has no source to derive from, so this page no longer states one. The manifest hashes move with the README, and the new guard is registered in `scripts/test-layout/layout.json` and its fixture. Co-Authored-By: Claude Code --- README.md | 6 +- readme/README.fr.md | 6 +- readme/README.ja.md | 6 +- readme/README.ko.md | 6 +- readme/README.ru.md | 6 +- readme/README.tr.md | 6 +- readme/README.zh-CN.md | 6 +- readme/README.zh-TW.md | 6 +- readme/i18n-manifest.json | 14 +- scripts/test-layout/layout.json | 1 + .../docs-readme-memory-inventory.test.ts | 165 ++++++++++++++++++ tests/fixtures/test-layout-expected.json | 1 + 12 files changed, 198 insertions(+), 31 deletions(-) create mode 100644 tests/ci-workflows/docs-readme-memory-inventory.test.ts diff --git a/README.md b/README.md index d5a2b010cf0..ca363a7573d 100644 --- a/README.md +++ b/README.md @@ -284,14 +284,14 @@ see the [installation docs](https://opencodex.me/getting-started/installation/).
Memory ownership details -OpenCodex tracks 36 categories of process-retained state. Each has a documented bound: +OpenCodex tracks process-retained state in the categories below. Each has a documented bound: -- **12 retained stores** (request log, debug rings, image cache, model cache, vision +- **13 retained stores** (request log, debug rings, image cache, model cache, vision descriptions, cursor blobs, responses continuation, etc.) are byte-accounted and evicted by the app-owned memory budget (default 256 MiB). - **4 observed buffers** (translator accumulators, image/OAuth/Grok tails) are monitored for in-flight byte pressure without eviction. -- **24 state-store registrations** handle expiry sweeps (60 s interval) and +- **28 state-store registrations** handle expiry sweeps (60 s interval) and config-generation reconciliation so stale provider/account keys are removed. - **Path and fingerprint memos** (workspace metadata, hardened identities, installation salts, mode-hint capabilities) use insertion-order LRU caps (8–128 entries). diff --git a/readme/README.fr.md b/readme/README.fr.md index 446bdaaf104..68fbd8ed2c7 100644 --- a/readme/README.fr.md +++ b/readme/README.fr.md @@ -286,14 +286,14 @@ consultez la [documentation d'installation](https://opencodex.me/fr/getting-star
Détails de la gestion de la mémoire -OpenCodex suit 36 catégories d'état conservé par le processus. Chacune possède une limite documentée : +OpenCodex suit l'état conservé par le processus dans les catégories ci-dessous. Chacune possède une limite documentée : -- **12 stockages conservés** (journal des requêtes, tampons circulaires de débogage, cache d'images, cache de +- **13 stockages conservés** (journal des requêtes, tampons circulaires de débogage, cache d'images, cache de modèles, descriptions visuelles, blobs de curseurs, continuation des réponses, etc.) sont comptabilisés en octets et évincés selon le budget mémoire géré par l'application (256 Mio par défaut). - **4 tampons observés** (accumulateurs de traduction, segments finaux d'images/OAuth/Grok) sont surveillés pour détecter la pression des octets en cours de traitement, sans éviction. -- **24 enregistrements de stockages d'état** gèrent les balayages d'expiration (intervalle de 60 s) et la +- **28 enregistrements de stockages d'état** gèrent les balayages d'expiration (intervalle de 60 s) et la réconciliation des générations de configuration afin de supprimer les clés obsolètes des fournisseurs et des comptes. - **Les mémos de chemins et d'empreintes** (métadonnées de l'espace de travail, identités renforcées, sels d'installation, capacités indiquées par le mode) utilisent des limites LRU selon l'ordre d'insertion (8 à 128 entrées). diff --git a/readme/README.ja.md b/readme/README.ja.md index 7d8369c0285..859bda65407 100644 --- a/readme/README.ja.md +++ b/readme/README.ja.md @@ -282,14 +282,14 @@ ocx init # 対話式セットアップ: ~/.opencodex/config.json を書き
メモリ所有権の詳細 -OpenCodex はプロセスが保持する状態を 36 種類に分けて追跡し、それぞれに文書化された上限があります: +OpenCodex はプロセスが保持する状態を以下のカテゴリで追跡し、それぞれに文書化された上限があります: -- **保持ストア 12 個**(リクエストログ、デバッグリング、画像キャッシュ、モデルキャッシュ、ビジョンの +- **保持ストア 13 個**(リクエストログ、デバッグリング、画像キャッシュ、モデルキャッシュ、ビジョンの 説明、カーソル blob、responses の継続など)はバイト単位で集計され、アプリが持つメモリ予算 (既定 256 MiB)によって退避されます。 - **観測バッファ 4 個**(トランスレーターのアキュムレーター、画像・OAuth・Grok の tail)は処理中の バイト圧力を監視するだけで、退避はしません。 -- **state-store の登録 24 個**が期限切れの掃除(60 秒間隔)と config 世代の reconciliation を担い、 +- **state-store の登録 28 個**が期限切れの掃除(60 秒間隔)と config 世代の reconciliation を担い、 古いプロバイダー/アカウントのキーを取り除きます。 - **パスとフィンガープリントのメモ**(ワークスペースのメタデータ、hardened identity、インストール salt、mode-hint の capability)は挿入順の LRU 上限(8〜128 件)を使います。 diff --git a/readme/README.ko.md b/readme/README.ko.md index c1bafb47fe7..c6b14e7a202 100644 --- a/readme/README.ko.md +++ b/readme/README.ko.md @@ -272,14 +272,14 @@ ocx init # 대화형 설정: ~/.opencodex/config.json을 쓰고 Codex를
메모리 소유권 상세 -OpenCodex는 프로세스가 붙잡고 있는 상태 36종을 추적합니다. 각각에 문서화된 한도가 있습니다: +OpenCodex는 프로세스가 붙잡고 있는 상태를 아래 항목에서 추적합니다. 각각에 문서화된 한도가 있습니다: -- **유지 저장소 12개**(요청 로그, debug ring, image cache, model cache, vision 설명, cursor blob, +- **유지 저장소 13개**(요청 로그, debug ring, image cache, model cache, vision 설명, cursor blob, responses continuation 등)는 바이트 단위로 집계되며, 앱이 소유한 메모리 예산(기본 256 MiB)이 eviction합니다. - **관측 버퍼 4개**(translator accumulator, image/OAuth/Grok tail)는 진행 중 바이트 압력을 감시만 하고 eviction하지 않습니다. -- **state-store 등록 24개**는 만료 sweep(60초 간격)과 config-generation reconciliation을 돌려, +- **state-store 등록 28개**는 만료 sweep(60초 간격)과 config-generation reconciliation을 돌려, 낡은 프로바이더/계정 키를 지웁니다. - **경로·fingerprint 메모**(워크스페이스 메타데이터, hardened identity, 설치 salt, mode-hint capability)는 삽입 순서 LRU cap(8–128개)을 씁니다. diff --git a/readme/README.ru.md b/readme/README.ru.md index e470afd26c2..f1ac99dba77 100644 --- a/readme/README.ru.md +++ b/readme/README.ru.md @@ -289,15 +289,15 @@ ocx init # интерактивная настройка: пишет ~/.ope
Подробности владения памятью -OpenCodex отслеживает 36 категорий состояния, удерживаемого процессом. У каждой есть +OpenCodex отслеживает состояние, удерживаемое процессом, в категориях ниже. У каждой есть документированная граница: -- **12 удерживаемых хранилищ** (журнал запросов, отладочные кольца, кэш изображений, кэш +- **13 удерживаемых хранилищ** (журнал запросов, отладочные кольца, кэш изображений, кэш моделей, vision-описания, cursor-блобы, продолжение responses и т. д.) учитываются в байтах и вытесняются бюджетом памяти приложения (по умолчанию 256 MiB). - **4 наблюдаемых буфера** (аккумуляторы транслятора, хвосты image/OAuth/Grok) мониторятся по байтовому давлению in-flight без вытеснения. -- **24 регистрации state-store** выполняют sweeps истечения (интервал 60 с) и сверку +- **28 регистрации state-store** выполняют sweeps истечения (интервал 60 с) и сверку поколений конфигурации, чтобы удалять устаревшие ключи провайдеров и аккаунтов. - **Мемо пути и отпечатков** (метаданные рабочей области, усиленные идентификаторы, соли установки, возможности mode-hint) используют LRU-потолки в порядке вставки diff --git a/readme/README.tr.md b/readme/README.tr.md index ea79384a412..1cca3f94890 100644 --- a/readme/README.tr.md +++ b/readme/README.tr.md @@ -282,14 +282,14 @@ betiklerini engellediyse [kurulum belgelerine](https://opencodex.me/tr/getting-s
Bellek sahipliği ayrıntıları -OpenCodex, süreçte tutulan durumu 36 kategoride izler. Her birinin belgelenmiş bir sınırı vardır: +OpenCodex, süreçte tutulan durumu aşağıdaki kategorilerde izler. Her birinin belgelenmiş bir sınırı vardır: -- **12 tutulan depo** (istek günlüğü, hata ayıklama halkaları, görsel önbelleği, model önbelleği, görü +- **13 tutulan depo** (istek günlüğü, hata ayıklama halkaları, görsel önbelleği, model önbelleği, görü açıklamaları, imleç blob'ları, responses devamlılığı vb.) bayt olarak hesaplanır ve uygulamanın sahip olduğu bellek bütçesiyle (varsayılan 256 MiB) tahliye edilir. - **4 gözlenen arabellek** (çevirici biriktiricileri, görsel/OAuth/Grok kuyrukları) tahliye edilmeden, yalnızca uçuştaki bayt baskısı için izlenir. -- **24 state-store kaydı**, süre dolumu taramalarını (60 sn aralık) ve yapılandırma kuşağı uzlaştırmasını +- **28 state-store kaydı**, süre dolumu taramalarını (60 sn aralık) ve yapılandırma kuşağı uzlaştırmasını yürüterek eski sağlayıcı/hesap anahtarlarını kaldırır. - **Yol ve parmak izi notları** (çalışma alanı meta verileri, sağlamlaştırılmış kimlikler, kurulum tuzları, mod ipucu yetenekleri) ekleme sıralı LRU sınırları kullanır (8–128 girdi). diff --git a/readme/README.zh-CN.md b/readme/README.zh-CN.md index 18e0c506c07..1526a6fe39b 100644 --- a/readme/README.zh-CN.md +++ b/readme/README.zh-CN.md @@ -270,14 +270,14 @@ Bun,Windows 也不需要 WSL。如果 npm 拦截了捆绑运行时的安装脚
内存所有权详情 -OpenCodex 跟踪 36 类进程保留状态。每一类都有文档化的边界: +OpenCodex 在下列类别中跟踪进程保留状态。每一类都有文档化的边界: -- **12 个保留存储**(请求日志、调试环、图片缓存、模型缓存、视觉 +- **13 个保留存储**(请求日志、调试环、图片缓存、模型缓存、视觉 描述、光标 blob、responses 续写等)按字节记账,并由应用自有的内存预算 (默认 256 MiB)逐出。 - **4 个观测缓冲区**(翻译累加器、图片/OAuth/Grok 尾部)会监测飞行中的字节压力, 但不做逐出。 -- **24 个状态存储注册** 负责过期扫描(60 秒间隔)和配置世代对账,从而移除过期的 +- **28 个状态存储注册** 负责过期扫描(60 秒间隔)和配置世代对账,从而移除过期的 提供商/账户键。 - **路径与指纹备忘**(工作区元数据、加固身份、安装盐、模式提示能力)使用按插入顺序的 LRU 上限(8–128 条)。 diff --git a/readme/README.zh-TW.md b/readme/README.zh-TW.md index 828cb1fed64..7af74602376 100644 --- a/readme/README.zh-TW.md +++ b/readme/README.zh-TW.md @@ -267,14 +267,14 @@ Bun,Windows 也不需要 WSL。若 npm 攔截了打包執行環境的安裝腳
記憶體所有權細節 -OpenCodex 追蹤 36 類行程保留狀態。每一類都有文件化的上限: +OpenCodex 在下列類別中追蹤行程保留狀態。每一類都有文件化的上限: -- **12 個保留儲存**(請求日誌、除錯環形緩衝、圖片快取、模型快取、視覺 +- **13 個保留儲存**(請求日誌、除錯環形緩衝、圖片快取、模型快取、視覺 描述、cursor blob、responses 延續等)以位元組計帳,並由 應用程式自己的記憶體預算淘汰(預設 256 MiB)。 - **4 個觀測緩衝區**(翻譯累加器、image/OAuth/Grok 尾端)會 監控進行中的位元組壓力,但不淘汰。 -- **24 個狀態儲存註冊**負責到期清掃(間隔 60 秒)與 +- **28 個狀態儲存註冊**負責到期清掃(間隔 60 秒)與 設定世代調和,以移除過期的供應商/帳號鍵。 - **路徑與指紋 memo**(工作區中繼資料、強化身分、安裝 salt、mode-hint 能力)使用插入順序 LRU 上限(8–128 筆)。 diff --git a/readme/i18n-manifest.json b/readme/i18n-manifest.json index a5e76f90b73..5fb5f0b57db 100644 --- a/readme/i18n-manifest.json +++ b/readme/i18n-manifest.json @@ -6,43 +6,43 @@ "file": "readme/README.fr.md", "label": "Français", "docsPath": "fr", - "sourceSha256": "44610e2f78949ba13c99682e59e3366cbe38e8c853ddc9dfe470a327f53118d2" + "sourceSha256": "896526b41ac24a3f6afec2f86cabe688407f50b01dbbc1ae856cc9e1a46187cc" }, "ko": { "file": "readme/README.ko.md", "label": "한국어", "docsPath": "ko", - "sourceSha256": "44610e2f78949ba13c99682e59e3366cbe38e8c853ddc9dfe470a327f53118d2" + "sourceSha256": "896526b41ac24a3f6afec2f86cabe688407f50b01dbbc1ae856cc9e1a46187cc" }, "zh-CN": { "file": "readme/README.zh-CN.md", "label": "简体中文", "docsPath": "zh-cn", - "sourceSha256": "44610e2f78949ba13c99682e59e3366cbe38e8c853ddc9dfe470a327f53118d2" + "sourceSha256": "896526b41ac24a3f6afec2f86cabe688407f50b01dbbc1ae856cc9e1a46187cc" }, "zh-TW": { "file": "readme/README.zh-TW.md", "label": "繁體中文", "docsPath": "zh-tw", - "sourceSha256": "44610e2f78949ba13c99682e59e3366cbe38e8c853ddc9dfe470a327f53118d2" + "sourceSha256": "896526b41ac24a3f6afec2f86cabe688407f50b01dbbc1ae856cc9e1a46187cc" }, "ru": { "file": "readme/README.ru.md", "label": "Русский", "docsPath": "ru", - "sourceSha256": "44610e2f78949ba13c99682e59e3366cbe38e8c853ddc9dfe470a327f53118d2" + "sourceSha256": "896526b41ac24a3f6afec2f86cabe688407f50b01dbbc1ae856cc9e1a46187cc" }, "ja": { "file": "readme/README.ja.md", "label": "日本語", "docsPath": "ja", - "sourceSha256": "44610e2f78949ba13c99682e59e3366cbe38e8c853ddc9dfe470a327f53118d2" + "sourceSha256": "896526b41ac24a3f6afec2f86cabe688407f50b01dbbc1ae856cc9e1a46187cc" }, "tr": { "file": "readme/README.tr.md", "label": "Türkçe", "docsPath": "tr", - "sourceSha256": "44610e2f78949ba13c99682e59e3366cbe38e8c853ddc9dfe470a327f53118d2" + "sourceSha256": "896526b41ac24a3f6afec2f86cabe688407f50b01dbbc1ae856cc9e1a46187cc" } } } diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index c6aee6166be..f452fe9d8dd 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -736,6 +736,7 @@ "docs-bun-source-requirement.test.ts": "ci-workflows", "docs-provider-billing-claims.test.ts": "ci-workflows", "docs-provider-preset-counts.test.ts": "ci-workflows", + "docs-readme-memory-inventory.test.ts": "ci-workflows", "docs-readme-translation-parity.test.ts": "ci-workflows", "docs-remote-hub-claims.test.ts": "ci-workflows", "doctor-codex-envkey-readiness.test.ts": "service", diff --git a/tests/ci-workflows/docs-readme-memory-inventory.test.ts b/tests/ci-workflows/docs-readme-memory-inventory.test.ts new file mode 100644 index 00000000000..83796e41317 --- /dev/null +++ b/tests/ci-workflows/docs-readme-memory-inventory.test.ts @@ -0,0 +1,165 @@ +/** + * The README memory inventory restates three counts that live in source, and nothing compared + * them. + * + * The block was written once (`6c14e3433`, 2026-08-13) and never revisited. `usage_snapshot` + * became the thirteenth retained store the very next day, and three more state-store + * registrations landed after that, so all eight README files have advertised 12 and 24 ever + * since. Review does not catch this: an English diff of one number looks complete on its own, + * and the seven translations were copied from a source that was already stale. + * + * AGENTS.md calls this class out directly: derive a count from the thing it describes rather + * than restating it. The three numbers below are derived from the rosters the runtime actually + * registers, so the next store that lands fails this test in all eight pages at once instead of + * silently disagreeing with the proxy for months. + * + * Each page is anchored by a locale-specific label rather than by the digits, so a reworded + * sentence fails loudly and asks to be re-anchored. That is the intended behavior: a sentence + * nobody can locate is a sentence nobody is checking. + * + * The opening sentence states no total, and this test holds it that way. The three rosters below + * it sum to 45, but the page also lists stores belonging to no roster, and its last bullet + * describes a ledger that keeps no process-level RAM index at all, so a single total over + * "categories of process-retained state" has no source to be derived from. A count that cannot + * be derived is not stated here. + * + * The numbers moved while this guard was written, so the diff that adds it also corrects the + * documents; the test would otherwise land red. + */ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; + +import { + APP_OWNED_OBSERVED_BUFFER_REGISTRATIONS, + APP_OWNED_RETAINED_STORE_REGISTRATIONS, +} from "../../src/lib/app-owned-memory-stores"; +import { STATE_STORE_REGISTRATIONS } from "../../src/lib/state-store-registrations"; +import { repoPath } from "../helpers/repo-root"; + +const RETAINED = APP_OWNED_RETAINED_STORE_REGISTRATIONS.length; +const OBSERVED = APP_OWNED_OBSERVED_BUFFER_REGISTRATIONS.length; +const STATE_STORES = STATE_STORE_REGISTRATIONS.length; + +/** + * One README, the three counts it states, and the sentence that must carry no count. + * + * Each claim captures the number it states instead of merely searching for the derived value: + * `toContain("4")` would pass on the `60 s interval` further down the same bullet, which is the + * one place in this block where a second number sits close enough to be mistaken for the claim. + */ +const PAGES = [ + { + locale: "en", + path: "README.md", + retained: /(\d+) retained stores/, + observed: /(\d+) observed buffers/, + stateStores: /(\d+) state-store registrations/, + totalSentence: "process-retained state", + }, + { + locale: "fr", + path: "readme/README.fr.md", + retained: /(\d+) stockages conservés/, + observed: /(\d+) tampons observés/, + stateStores: /(\d+) enregistrements de stockages/, + totalSentence: "état conservé par le processus", + }, + { + locale: "ja", + path: "readme/README.ja.md", + retained: /保持ストア (\d+) 個/, + observed: /観測バッファ (\d+) 個/, + stateStores: /state-store の登録 (\d+) 個/, + totalSentence: "プロセスが保持する状態を", + }, + { + locale: "ko", + path: "readme/README.ko.md", + retained: /유지 저장소 (\d+)개/, + observed: /관측 버퍼 (\d+)개/, + stateStores: /state-store 등록 (\d+)개/, + totalSentence: "프로세스가 붙잡고 있는 상태를", + }, + { + locale: "ru", + path: "readme/README.ru.md", + retained: /(\d+) удерживаемых хранилищ/, + observed: /(\d+) наблюдаемых буфера/, + stateStores: /(\d+) регистрации state-store/, + totalSentence: "удерживаемое процессом", + }, + { + locale: "tr", + path: "readme/README.tr.md", + retained: /(\d+) tutulan depo/, + observed: /(\d+) gözlenen arabellek/, + stateStores: /(\d+) state-store kaydı/, + totalSentence: "süreçte tutulan durumu", + }, + { + locale: "zh-CN", + path: "readme/README.zh-CN.md", + retained: /(\d+) 个保留存储/, + observed: /(\d+) 个观测缓冲区/, + stateStores: /(\d+) 个状态存储注册/, + totalSentence: "进程保留状态", + }, + { + locale: "zh-TW", + path: "readme/README.zh-TW.md", + retained: /(\d+) 個保留儲存/, + observed: /(\d+) 個觀測緩衝區/, + stateStores: /(\d+) 個狀態儲存註冊/, + totalSentence: "行程保留狀態", + }, +] as const; + +/** + * The number one page states for one claim. + * + * Exactly one occurrence is required. A page that states the count twice has two places to + * update and this check only sees one of them, so the second is a silent drift waiting to + * happen; a page that states it zero times has been reworded past the anchor. + */ +function statedOnce(path: string, claim: RegExp): number { + const hits = [...readFileSync(repoPath(path), "utf8").matchAll(new RegExp(claim, "g"))]; + expect(hits.length, `${path} states this count ${hits.length} times; re-anchor this check`).toBe(1); + return Number(hits[0]![1]); +} + +/** The single line carrying `anchor`, which is the line a claim is required to sit on. */ +function anchoredLine(path: string, anchor: string): string { + const lines = readFileSync(repoPath(path), "utf8") + .split("\n") + .filter(line => line.includes(anchor)); + expect(lines.length, `${path} has no line containing "${anchor}"; re-anchor this check`).toBe(1); + return lines[0]!; +} + +describe("documented memory inventory counts match the registered rosters", () => { + test("the rosters are the only source of the numbers under test", () => { + // A derived count that collapsed to zero would make every assertion below vacuous. + expect(RETAINED).toBeGreaterThan(0); + expect(OBSERVED).toBeGreaterThan(0); + expect(STATE_STORES).toBeGreaterThan(0); + }); + + for (const page of PAGES) { + test(`${page.locale} README states ${RETAINED} retained stores`, () => { + expect(statedOnce(page.path, page.retained)).toBe(RETAINED); + }); + + test(`${page.locale} README states ${OBSERVED} observed buffers`, () => { + expect(statedOnce(page.path, page.observed)).toBe(OBSERVED); + }); + + test(`${page.locale} README states ${STATE_STORES} state-store registrations`, () => { + expect(statedOnce(page.path, page.stateStores)).toBe(STATE_STORES); + }); + + test(`${page.locale} README states no total it cannot derive`, () => { + const line = anchoredLine(page.path, page.totalSentence); + expect(line, `${page.path} states a total; derive it from a roster or drop it`).not.toMatch(/[0-9]/); + }); + } +}); diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json index da49f05ab6c..5eaf74e380e 100644 --- a/tests/fixtures/test-layout-expected.json +++ b/tests/fixtures/test-layout-expected.json @@ -564,6 +564,7 @@ "docs-bun-source-requirement.test.ts": "ci-workflows", "docs-provider-billing-claims.test.ts": "ci-workflows", "docs-provider-preset-counts.test.ts": "ci-workflows", + "docs-readme-memory-inventory.test.ts": "ci-workflows", "docs-readme-translation-parity.test.ts": "ci-workflows", "docs-remote-hub-claims.test.ts": "ci-workflows", "doctor-codex-envkey-readiness.test.ts": "service", From 753819381a8ad1bc9e369fbcc96d448f31ba9430 Mon Sep 17 00:00:00 2001 From: liangbo Date: Sun, 20 Sep 2026 20:56:00 +0800 Subject: [PATCH 2/3] docs(readme): correct the Russian inflection the new count requires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `регистрации` was the correct genitive for the 24 the page used to claim. 28 takes `регистраций`, so the digit change alone left the Russian page ungrammatical and the guard's anchor locking that in. The Russian anchor now follows the corrected wording, which also means a page that puts the stale number back has to put the stale inflection back with it, and the test fails. Co-Authored-By: Claude Code --- readme/README.ru.md | 2 +- tests/ci-workflows/docs-readme-memory-inventory.test.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/readme/README.ru.md b/readme/README.ru.md index f1ac99dba77..c5730de6cb2 100644 --- a/readme/README.ru.md +++ b/readme/README.ru.md @@ -297,7 +297,7 @@ OpenCodex отслеживает состояние, удерживаемое п в байтах и вытесняются бюджетом памяти приложения (по умолчанию 256 MiB). - **4 наблюдаемых буфера** (аккумуляторы транслятора, хвосты image/OAuth/Grok) мониторятся по байтовому давлению in-flight без вытеснения. -- **28 регистрации state-store** выполняют sweeps истечения (интервал 60 с) и сверку +- **28 регистраций state-store** выполняют sweeps истечения (интервал 60 с) и сверку поколений конфигурации, чтобы удалять устаревшие ключи провайдеров и аккаунтов. - **Мемо пути и отпечатков** (метаданные рабочей области, усиленные идентификаторы, соли установки, возможности mode-hint) используют LRU-потолки в порядке вставки diff --git a/tests/ci-workflows/docs-readme-memory-inventory.test.ts b/tests/ci-workflows/docs-readme-memory-inventory.test.ts index 83796e41317..7ce8d221d27 100644 --- a/tests/ci-workflows/docs-readme-memory-inventory.test.ts +++ b/tests/ci-workflows/docs-readme-memory-inventory.test.ts @@ -25,6 +25,11 @@ * * The numbers moved while this guard was written, so the diff that adds it also corrects the * documents; the test would otherwise land red. + * + * One locale needed a word changed and not only a digit: Russian agrees its numeral with the + * noun, and `регистрации` was the right genitive for the 24 the page used to claim while + * `регистраций` is the right one for 28. The anchor here follows the corrected wording, so a + * page that reverts to the stale number has to revert the inflection too, and this fails. */ import { describe, expect, test } from "bun:test"; import { readFileSync } from "node:fs"; @@ -85,7 +90,7 @@ const PAGES = [ path: "readme/README.ru.md", retained: /(\d+) удерживаемых хранилищ/, observed: /(\d+) наблюдаемых буфера/, - stateStores: /(\d+) регистрации state-store/, + stateStores: /(\d+) регистраций state-store/, totalSentence: "удерживаемое процессом", }, { From 17713e0b5c821f658276af1759b84fafdc67abca Mon Sep 17 00:00:00 2001 From: liangbo Date: Mon, 21 Sep 2026 10:42:29 +0800 Subject: [PATCH 3/3] test(readme): record that every Russian anchor is numeral-governed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `удерживаемых хранилищ`, `наблюдаемых буфера` and `регистраций` each change form with the count that precedes them, so a future count can stop one of these anchors matching. That failure asks for a re-anchor rather than passing quietly, which is the point of anchoring on prose; the comment says so instead of leaving the next author to infer it from a missing match. Co-Authored-By: Claude Code --- tests/ci-workflows/docs-readme-memory-inventory.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/ci-workflows/docs-readme-memory-inventory.test.ts b/tests/ci-workflows/docs-readme-memory-inventory.test.ts index 7ce8d221d27..c0f5dd2f721 100644 --- a/tests/ci-workflows/docs-readme-memory-inventory.test.ts +++ b/tests/ci-workflows/docs-readme-memory-inventory.test.ts @@ -30,6 +30,13 @@ * noun, and `регистрации` was the right genitive for the 24 the page used to claim while * `регистраций` is the right one for 28. The anchor here follows the corrected wording, so a * page that reverts to the stale number has to revert the inflection too, and this fails. + * + * Every Russian anchor carries an inflection its numeral governs, not only the state-store one: + * `удерживаемых хранилищ`, `наблюдаемых буфера` and `регистраций` each change form with the + * count. A future count that moves one of them stops this check matching and fails it, asking + * for a re-anchor. That is the designed outcome. The alternative — a pattern loose enough to + * match any noun form — would accept a sentence nobody re-read, which is the failure this guard + * exists to prevent. */ import { describe, expect, test } from "bun:test"; import { readFileSync } from "node:fs";