diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1e23f84..6638bc6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,5 @@ -# Control-plane review paths. These assignments request review on pull requests; -# direct default-branch deposits remain governed by AI_ENTRYPOINT.md. +# Control-plane review paths. These assignments request review on pull requests. +# Canonical source maintenance is governed by the repository's GitHub ruleset and CONTRIBUTING.md. /AI_ENTRYPOINT.md @Wonderforge-Lab /lobby/ @Wonderforge-Lab /docs/ @Wonderforge-Lab diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 6dea6df..18bebc5 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -24,6 +24,19 @@ jobs: - run: python tests/test_generated_views.py - run: python tests/test_markdown_links.py - run: python tests/test_locale_invariants.py + - name: Check localized source freshness + env: + BEFORE: ${{ github.event.before }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + base="${{ github.event.pull_request.base.sha }}" + elif [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then + base="$(git merge-base "origin/$DEFAULT_BRANCH" "$GITHUB_SHA")" + else + base="$BEFORE" + fi + python tests/test_locale_freshness.py "$base" "$GITHUB_SHA" - run: python tests/test_bridge_config.py - run: python tests/test_tag_promotion.py - name: Enforce tag-promotion separation diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 880d8e3..a11ee15 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,3 +13,9 @@ For collaborator changes: - Avoid private identifiers, real user data, account names, local paths, and internal project references. - Keep docs plain and useful to engineers. - Small text artifacts belong here; bulky material belongs elsewhere. + +## Maintaining the canonical source repository + +This section applies to collaborators maintaining `Wonderforge-Lab/CapstanAI-LabNote`, the public source scaffold. Use a branch, pull request, validation, review, and release process. + +Canonical maintenance is not a LabNote visitor session. Do not create visitor records, datadrops, messages, notifications, responses, runtime registry records, or signoffs in this repository. To use LabNote as a workspace, create a separate clone or template-derived repository and follow its runtime protocol. diff --git a/README.md b/README.md index f64f70e..44c893a 100644 --- a/README.md +++ b/README.md @@ -223,17 +223,28 @@ LabNote is the ledger, not the warehouse. Keep the workspace focused on small, i Detailed runtime rules live in those canonical files rather than being duplicated here. +## Acknowledgements + +CapstanAI - LabNote is human-led. Recent language-layer and verification work was assisted by the following AI collaborators: + +- **OpenAI Codex** — implementation support, verification design, and repository review. +- **DeepSeek** — independent Simplified-Chinese language, cultural, and adversarial review. + +These acknowledgements describe assisted contributions. Human maintainers retain authorship, decision authority, and release control. + ## Status -Latest published scaffold release: `v0.3.1 - Registry Guidance Cleanup`. +Latest published scaffold release: `v0.3.2 - Locale Route Freshness`. This patch release: -* removes stale live guidance that still allowed manual CSV registry edits, -* marks the localization source-drift note and pre-v1 registry audit as historical provenance, -* leaves Registry Contract v1, schemas, runtime paths, machine fields, statuses, localization protocol, and generated views unchanged. +* corrects stale zh-CN CSV guidance on live visitor routes; +* aligns the zh-CN visitor-lobby model with the canonical routing and registration rule; +* adds CI checks for protected locale invariants and for unreviewed changes to paired English sources; + +It leaves Registry Contract v1, schemas, runtime paths, machine fields, statuses, permissions, generated views, and the shared protocol unchanged. -`v0.3.0 - Registry v1 and Simplified Chinese Revision` remains the feature baseline beneath this patch. +`v0.3.0 - Registry v1 and Simplified Chinese Revision` remains the feature baseline beneath these patch releases. CapstanAI may later grow a richer deterministic layer, along with relay, vault, and protocol modules. LabNote begins as the simplest useful ledger. diff --git a/archive/20260903_v0.3.2_locale_route_freshness.md b/archive/20260903_v0.3.2_locale_route_freshness.md new file mode 100644 index 0000000..0af3a98 --- /dev/null +++ b/archive/20260903_v0.3.2_locale_route_freshness.md @@ -0,0 +1,39 @@ +# v0.3.2 — Locale Route Freshness + +## English + +### What changed + +- Corrected stale zh-CN instructions that still allowed manual CSV registry edits on live visitor routes. +- Aligned the zh-CN visitor-lobby model with the canonical rule that visitor identifiers are routing handles and registration is optional. +- Extended locale invariant coverage to the affected lobby, message-routing, and visitor-model surfaces. +- Added a CI freshness check: when a paired English source changes, its zh-CN counterpart must change or receive a hash-bound review acknowledgement. +- Added transparent acknowledgements for OpenAI Codex and DeepSeek as AI collaborators in this work. + +### What did not change + +This release does not change Registry Contract v1, schemas, runtime paths, JSON keys, machine fields, status values, permissions, generated views, or the shared English-canonical protocol. + +### Validation + +The release PR must pass the repository's Registry validation workflow, including locale invariants and the locale freshness check. + +--- + +## 简体中文 + +### 变更内容 + +- 修正了访客会话实际路由中仍允许手工编辑 CSV 登记视图的过时 zh-CN 指令。 +- 使 zh-CN 访客会话入口模型与基准规则保持一致:访客会话标识是路由标识,登记并非前提条件。 +- 扩展了本地化不变项检查,覆盖受影响的入口区、消息路由和访客会话模型表面。 +- 新增 CI 新鲜度检查:成对英文来源发生变更时,其 zh-CN 对应文件必须同步变更,或获得绑定该来源哈希值的审阅确认。 +- 新增对 OpenAI Codex 和 DeepSeek 的透明致谢,说明其作为本次工作 AI 协作者的角色。 + +### 未发生变化的部分 + +本版本不改变 Registry Contract v1、schema、运行时路径、JSON 键、机器字段、状态值、权限、生成视图或共享的英语基准协议。 + +### 验证 + +发布 PR 必须通过仓库的 Registry validation 工作流,其中包括本地化不变项检查和本地化新鲜度检查。 diff --git a/locales/zh-CN/CONTRIBUTING.md b/locales/zh-CN/CONTRIBUTING.md index 6352ef5..9694573 100644 --- a/locales/zh-CN/CONTRIBUTING.md +++ b/locales/zh-CN/CONTRIBUTING.md @@ -13,3 +13,9 @@ CapstanAI - LabNote 的基准(canonical)仓库由协作者维护,**不接 - 避免出现私人标识符、真实用户数据、账户名称、本地路径和内部项目引用; - 文档应保持简洁、清晰,并对工程人员有用; - 小型文本类文件适合放在这里,体积较大的材料应存放在别处。 + +## 维护基准源仓库 + +本节适用于维护公开源脚手架 `Wonderforge-Lab/CapstanAI-LabNote` 的协作者。请使用分支、拉取请求、验证、审阅和发布流程。 + +基准仓库维护不是 LabNote 访客会话。不要在此仓库中创建访客记录、资料投递、消息、通知、回复、运行期登记记录或签退记录。若要把 LabNote 用作工作区,请创建单独的克隆或由模板派生的仓库,并遵循其中的运行期协议。 diff --git a/locales/zh-CN/README.md b/locales/zh-CN/README.md index 9a3d9ad..8a34999 100644 --- a/locales/zh-CN/README.md +++ b/locales/zh-CN/README.md @@ -229,17 +229,28 @@ LabNote 是工作台账(ledger),不是仓库(warehouse)。工作区应 详细运行规则见上述对应文件,此处不再重复。若简体中文说明与英文基准文件在协议含义上冲突,以英文基准文件为准。 +## 致谢 + +CapstanAI - LabNote 由人类主导。近期的语言层和验证工作得到了以下 AI 协作者的协助: + +- **OpenAI Codex** — 提供实施支持、验证设计和仓库审阅。 +- **DeepSeek** — 提供独立的简体中文语言、文化和对抗性审阅。 + +这些致谢说明的是辅助性贡献。人类维护者保有作者身份、决策权和发布控制权。 + ## 状态 -当前最新发布的框架版本:`v0.3.1 - Registry Guidance Cleanup`。 +当前最新发布的框架版本:`v0.3.2 - Locale Route Freshness`。 这个补丁版本: -* 清理仍允许手工编辑 CSV 登记视图的过时当前指导; -* 将本地化来源漂移说明和 v1 之前的登记库审计明确标记为历史溯源材料; -* 不改变 Registry Contract v1、schema、运行时路径、机器字段、状态值、本地化协议或生成的登记视图。 +* 修正了访客会话实际路由中已过时的 zh-CN CSV 指导; +* 使 zh-CN 访客会话入口模型与基准的路由和登记规则保持一致; +* 增加了 CI 检查,用于保护本地化中的关键不变项,并识别未经审阅的成对英文来源变更; + +本版本不改变 Registry Contract v1、schema、运行时路径、机器字段、状态值、权限、生成视图或共享协议。 -`v0.3.0 - Registry v1 and Simplified Chinese Revision` 仍是本补丁所基于的功能版本基线。 +`v0.3.0 - Registry v1 and Simplified Chinese Revision` 仍是这些补丁版本所基于的功能版本基线。 以后,CapstanAI 可能会发展出更丰富的确定性层,以及 relay、vault 和 protocol 等模块。LabNote 则从最简单、真正有用的工作台账开始。 diff --git a/locales/zh-CN/docs/visitor_lobby_model.md b/locales/zh-CN/docs/visitor_lobby_model.md index 6d87120..b083cce 100644 --- a/locales/zh-CN/docs/visitor_lobby_model.md +++ b/locales/zh-CN/docs/visitor_lobby_model.md @@ -15,4 +15,4 @@ 不要自行编造访客会话标识,也不要静默复用先前对话上下文中的标识。 -访客会话资料可以存放在 `lobby/visitors/` 下。保持内容简短、通用。 +访客会话标识是路由标识;访客会话资料登记并不是消息、通知或访问路由的前提条件。如已登记访客会话,基准记录为 `registry/visitors/.json`。仅供人类阅读定位的小型可选资料可放在 `lobby/visitors/` 下。保持可选资料简短、通用。 diff --git a/locales/zh-CN/lobby/README.md b/locales/zh-CN/lobby/README.md index a439f9a..2be08c1 100644 --- a/locales/zh-CN/lobby/README.md +++ b/locales/zh-CN/lobby/README.md @@ -10,6 +10,6 @@ 3. 读取 `VISITOR_CHECKLIST.md`。 4. 普通投递按照 `ROUTINE_DEPOSIT_QUICKSTART.md` 执行。 5. 在仓库根目录的基准 `registry/` 路径下创建 JSON 登记记录。 -6. 除非操作者明确要求,否则不要编辑 CSV 汇总文件。 +6. 不要手动编辑生成的 CSV 登记视图。 入口区留下的是工作轨迹,不是登录系统。 diff --git a/locales/zh-CN/messages/ROUTING_RULES.md b/locales/zh-CN/messages/ROUTING_RULES.md index 0354cb7..7272789 100644 --- a/locales/zh-CN/messages/ROUTING_RULES.md +++ b/locales/zh-CN/messages/ROUTING_RULES.md @@ -4,7 +4,7 @@ - 对于某一类访客会话或较宽泛的接收组,使用 `to_group`。 - 先检查明确指向当前访客会话的消息,再检查组消息。 - 在 `registry/messages/` 下创建基准 JSON 消息记录。 -- 普通访客会话工作不要编辑旧版 CSV 消息登记表,除非操作者明确要求。 +- 不要手动编辑生成的 CSV 消息登记表或 `registry/INDEX.md`。 - 在有帮助时,链接相关工作包和回复。 - 当必须由操作者把消息转递到另一个会话时,设置 `needs_human_relay`。 - 在收件方回复、操作者确认,或消息被关闭之前,不要假定消息已经送达。 diff --git a/tests/locale_review_acknowledgements.json b/tests/locale_review_acknowledgements.json new file mode 100644 index 0000000..fa11d71 --- /dev/null +++ b/tests/locale_review_acknowledgements.json @@ -0,0 +1,4 @@ +{ + "schema_version": 1, + "acknowledgements": [] +} diff --git a/tests/test_locale_freshness.py b/tests/test_locale_freshness.py new file mode 100644 index 0000000..19aa632 --- /dev/null +++ b/tests/test_locale_freshness.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""Require a zh-CN review when a paired English source changes. + +This deliberately checks review freshness, not translation quality. It treats +the repository's current diff as the unit of work: a changed canonical source +must either change its zh-CN counterpart or receive a specific, hash-bound +review acknowledgement. +""" +from __future__ import annotations + +import hashlib +import json +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +LOCALE_ROOT = ROOT / "locales" / "zh-CN" +ACKNOWLEDGEMENTS = ROOT / "tests" / "locale_review_acknowledgements.json" + +# Locale paths whose English canonical source does not follow the normal +# locales/zh-CN/ -> relationship. +SOURCE_OVERRIDES = { + "GLOSSARY.md": "docs/localization/GLOSSARY.md", + "registry/TAG_DISPLAY_CATALOG.md": "docs/localization/TAG_DISPLAY_CATALOG.md", +} + + +def paired_surfaces() -> dict[str, str]: + pairs: dict[str, str] = {} + for locale_path in sorted(LOCALE_ROOT.rglob("*.md")): + locale_rel = locale_path.relative_to(ROOT).as_posix() + relative = locale_path.relative_to(LOCALE_ROOT).as_posix() + source_rel = SOURCE_OVERRIDES.get(relative, relative) + if (ROOT / source_rel).is_file(): + pairs[source_rel] = locale_rel + return pairs + + +def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def changed_paths(base: str, head: str) -> set[str]: + result = subprocess.run( + ["git", "diff", "--name-only", base, head], + cwd=ROOT, + check=True, + text=True, + capture_output=True, + ) + return {line for line in result.stdout.splitlines() if line} + + +def acknowledgements(pairs: dict[str, str]) -> dict[str, str]: + payload = json.loads(ACKNOWLEDGEMENTS.read_text(encoding="utf-8")) + if payload.get("schema_version") != 1: + raise AssertionError("locale review acknowledgements require schema_version 1") + + reviewed: dict[str, str] = {} + for entry in payload.get("acknowledgements", []): + source = entry.get("source") + source_sha256 = entry.get("source_sha256") + reason = entry.get("reason") + if not isinstance(source, str) or source not in pairs: + raise AssertionError(f"invalid acknowledgement source: {source!r}") + if not isinstance(source_sha256, str) or len(source_sha256) != 64: + raise AssertionError(f"invalid source_sha256 for {source!r}") + if not isinstance(reason, str) or not reason.strip(): + raise AssertionError(f"missing review reason for {source!r}") + if source in reviewed: + raise AssertionError(f"duplicate acknowledgement for {source!r}") + reviewed[source] = source_sha256 + return reviewed + + +def main() -> int: + if len(sys.argv) != 3: + raise SystemExit("usage: test_locale_freshness.py ") + + pairs = paired_surfaces() + changed = changed_paths(sys.argv[1], sys.argv[2]) + reviewed = acknowledgements(pairs) + failures: list[str] = [] + + for source_rel, locale_rel in pairs.items(): + if source_rel not in changed or locale_rel in changed: + continue + current_digest = digest(ROOT / source_rel) + if reviewed.get(source_rel) == current_digest: + continue + failures.append( + f"{source_rel} changed without a zh-CN counterpart update or " + f"hash-bound review acknowledgement ({locale_rel})" + ) + + if failures: + raise AssertionError("Locale freshness check failed:\n" + "\n".join(failures)) + + print(f"locale freshness check passed ({len(pairs)} paired surfaces)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_locale_invariants.py b/tests/test_locale_invariants.py index 0983f35..fb32350 100644 --- a/tests/test_locale_invariants.py +++ b/tests/test_locale_invariants.py @@ -108,6 +108,34 @@ "accepted", ), }, + "lobby/README.md": { + "locale": "locales/zh-CN/lobby/README.md", + "literals": ( + "AI_ENTRYPOINT.md", + "README_FIRST.md", + "VISITOR_CHECKLIST.md", + "ROUTINE_DEPOSIT_QUICKSTART.md", + "registry/", + "CSV", + ), + }, + "messages/ROUTING_RULES.md": { + "locale": "locales/zh-CN/messages/ROUTING_RULES.md", + "literals": ( + "to_visitor_id", + "to_group", + "registry/messages/", + "registry/INDEX.md", + "needs_human_relay", + ), + }, + "docs/visitor_lobby_model.md": { + "locale": "locales/zh-CN/docs/visitor_lobby_model.md", + "literals": ( + "registry/visitors/.json", + "lobby/visitors/", + ), + }, "registry/README.md": { "locale": "locales/zh-CN/registry/README.md", "literals": ( @@ -124,10 +152,6 @@ "CI", ), }, - "SECURITY.md": { - "locale": "locales/zh-CN/SECURITY.md", - "literals": (), - }, "templates/datadrop_packet.md": { "locale": "locales/zh-CN/templates/datadrop_packet.md", "literals": (