Skip to content

refactor(public-safety): decide the compact opaque id body in one owner - #5880

Merged
huangruiteng merged 1 commit into
loopx-project:mainfrom
Hsuehtan:refactor/opaque-id-shape-owner
Oct 8, 2026
Merged

huangruiteng merged 1 commit into
loopx-project:mainfrom
Hsuehtan:refactor/opaque-id-shape-owner

Conversation

@Hsuehtan

@Hsuehtan Hsuehtan commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Goal And Delivered Outcome

  • Outcome basis / optional anchor: the owner that already states this rule — loopx/public_safe_text.py
    carries the comment "Shared identifier shapes stay here with the sibling public-safe text shapes. They
    express syntax only: each consuming contract retains its own field names, error text, and any
    additional validation policy", and tests/control_plane/test_compact_identifier_shape_owner.py is the
    guard that enforces it for three other bodies. Refs [Task]: Semantic vocabulary convergence follow-ups after RFC PR #4433 / 语义词表收敛后续任务 #4447, Track A; no issue is closed here and [Task]: Semantic vocabulary convergence follow-ups after RFC PR #4433 / 语义词表收敛后续任务 #4447 stays open as the track.
  • Goal/source and gap: the body ^[A-Za-z0-9._:-]{1,200}$ — "a compact, path-safe opaque identifier" —
    was compiled five times in Python under five different names, each with its own message:
    loopx/chat_actions.py:55 (_OPAQUE_ID), loopx/chat_action_store.py:57 (_OPAQUE_ID),
    loopx/control_plane/goals/goal_ref_validation.py:6 (GOAL_ID),
    loopx/control_plane/goals/botmux_runtime.py:31 (_SAFE_TOKEN) and
    loopx/control_plane/goals/deletion_service.py:49 (_OPAQUE_ID, declared here — see Scope). So the
    200-character bound had five places to land, and a change to any one of them would have silently left
    four behind. deletion_service's own docstring asserts the property the body is supposed to carry
    ("safe for path segments"), which is exactly the sentence a fifth copy cannot be trusted to keep.
  • Observable before → after: one owner export OPAQUE_ID_PATTERN plus five alias bindings; the guard's
    census now fails on any file in loopx/ that compiles that body, in or out of a roster. Proven by
    the unit row (3 → 16 collected cases in the guard) and by P2/P5 in the mutation table, where a
    one-character bound change and an anchor removal both go red.
  • Issue/task and intended base: Refs [Task]: Semantic vocabulary convergence follow-ups after RFC PR #4433 / 语义词表收敛后续任务 #4447, Track A. No issue is closed here.

Author Declaration

  • Written by: model_agent (Claude, Anthropic), against this repository's main at aa87cc019.

Implemented against

  • Specification and revision: the owner-module contract quoted above, and the sibling precedent this
    guard already enforces for PUBLIC_SAFE_REFERENCE_PATTERN, COMPACT_TOKEN_PATTERN and
    MODULE_QUALIFIED_SURFACE_PATTERN — one compiled owner, per-site names and messages, census over the
    whole tree.
Criterion (spec clause) Disposition Symbol / path Test or command
"shared identifier shapes stay here" implemented loopx/public_safe_text.py OPAQUE_ID_PATTERN test_compact_identifier_shapes_are_declared_once
"they express syntax only" implemented — bodies unchanged, so no site's answer moves all five sites test_the_opaque_id_contract_is_bounded_safe_characters_only
"each consuming contract retains its own field names, error text" implemented must be a compact opaque id, must be 1-200 safe characters, botmux <label> is invalid all unchanged test_each_contract_holds_the_owners_object_and_reports_its_own_field
the census must not be a roster implemented whole-tree count of the body test_the_opaque_id_body_is_compiled_only_by_the_owner_and_declared_sites
a declared site that gets converted must lose its declaration implemented OPEN_OPAQUE_SITES for deletion_service.py same test (mutation P3)
same value with a required family prefix is a different question implemented, pinned chat_action_normalization._AUTHORITY_PRINCIPAL test_a_principal_reference_is_a_different_question_and_stays_separate
  • Self-check before submission: read every .fullmatch call site of all five names before touching them,
    and confirmed there is no .search/.match use, so no anchoring question arises; checked that none of
    the six files is a project_registry_io_manifest_v1 host, so no pinned line anchor moves (verified by
    counting the paths in the manifest, not by assuming); ran the twin/budget gates rather than reasoning
    about them — the architecture/canary sets are identical to base, which is what proves the TypeScript
    pairing was untouched.

Scope And Continuation

  • Completed scope and remaining work:
    • Done: four of the five compiles deleted, the body exported once, and the guard extended with a
      whole-tree census, four consumer identity assertions, a bound/class edge table (including
      U+2028, which str.splitlines treats as a line break but this class never matches), a
      positive-control pair that both chat helpers still raise their own message, and the deliberate
      non-merge of the principal rule.
    • Declared, not converted: loopx/control_plane/goals/deletion_service.py, because fix(goals): fence deletion against canonical writes #5854 is
      restructuring that file. It is recorded with a count, and converting it without deleting the entry
      fails the census (mutation P3).
    • Not merged, on purpose: chat_action_normalization's principal (family prefix plus an opaque
      body — merging it would either widen the opaque id to accept prefixes or narrow a principal to a bare
      id), and the three chat rules that share the name _OPAQUE_ID/GOAL_ID but not the value
      (chat_endpoints.py {1,80}, chat_store.py {1,160}, multi_subagent/native_child_receipts.py
      with a leading alnum requirement). A shared name with different values is not drift under
      loopx/semantics/inventory.py's own note, so they keep their own bodies.
  • Slice boundary / successor: independently reversible — reverting restores five lines and one guard
    section. Successor 1: the two TypeScript files that compile the same body,
    control_plane/goals/goal_instance_identity.ts:5 and operator_actions.ts:18 — a cross-runtime owner
    is a different decision (it moves the dual-runtime twin pairing), so this slice leaves them and the
    census is Python-only, stated rather than hidden. Successor 2: make this census fold AST values the way
    the Lark guard does, which is where the surviving mutation below comes from.

Validation

  • Tested revision: e9944906a (1 commit, 6 files, +88 −4)
  • Run state: finished
  • Input classes: public_fixture, synthetic
Check kind Result Public-safe evidence / limitation
unit passed python -m pytest -q tests/control_plane/test_compact_identifier_shape_owner.py -> 16 passed; the same file collects 3 at the base revision, so the +13 are the census, the four consumer rows, the 10-value edge table and the principal pin.
unit passed (net zero) python -m pytest -q tests/control_plane -k "chat or botmux or goal_ref or opaque or deletion" — head 8 failed / 151 passed, the same selection in an unmodified worktree at the base revision 8 failed, failure ids identical in both; the eight are the packaged-Chat HTTP bundle cases that need a gitignored compiled artifact, so they are environment red on both sides, not this diff.
integration passed (net zero) python -m pytest -q tests/architecture tests/canary -> head 2 failed / 1444 passed, base 2 failed / 1444 passed, ids identical (test_top_level_module_budget… = the 148-vs-147 pin reported in #5685, which every main-based PR inherits; test_source_session_registry_denial…). Same interpreter, same qualified Node line, run in one window.
static passed python -m ruff check loopx tests/control_plane/test_compact_identifier_shape_owner.py clean; python -m mypy -> Success: no issues found in 19 source files; loopx check --scan-path over all six changed paths -> errors=0. ruff format --check is not a gate here and is unchanged: the guard file reports 1 hunk before and 1 after.
census passed (net zero) python examples/semantic-vocabulary-drift-smoke.py measured on this head and on an unmodified worktree at the base revision, byte-for-byte the same line: same_runtime_forks=2/2 same_runtime_fork_definitions=5/5 conflicting_values=16/16 conflicting_definitions=54/54 schema_version_same_runtime_forks=0/0 multi_value_twins=8/8 multi_value_forks=2/2 multi_value_forks_semantic=1/1 multi_value_fork_definitions=6/6 same_runtime_forks_semantic=2/2 conflicting_values_semantic=0/0, twins_raw=45, generated_verified=2, independently_maintained=43/43. No registry budget and no BUDGET_ANCHOR literal moves in this PR, and the registry I/O census test inside the compared architecture selection reports the same outcome on both sides.
regression_parity passed Six mutations, five killed: a converted site compiling the body again (1), the owner's bound tightened 200 → 199 (3, including the edge-table row that pins 200 as accepted), the declared site converted while its declaration stayed (1), the principal rule merged onto the opaque body (1, exactly the failure the non-merge note predicts), and the owner dropping its trailing anchor (2 — killed by the census and the declared-once assert only, which is the equivalence evidence: no behavioural case notices, because every site applies fullmatch). One variant survives and is the stated limit: a converted site assembling the body from two literals ("^" + _GOAL_BODY + "$") is not caught, because this census counts the literal string in the file text rather than folding AST values like the Lark guard does; that gap is Successor 2 rather than a claim.
  • Coverage and gaps: the four migrated sites are covered by object identity (a restated copy is a
    distinct object even when the text matches) and by the whole-tree census, so both a silent recopy and
    a silent unlinking go red. Gaps named plainly: (a) deletion_service keeps its own compile until
    fix(goals): fence deletion against canonical writes #5854 lands, so "one owner" is a program, not a fact; (b) the two TypeScript copies of the same body are
    outside this census, and this PR makes no cross-runtime claim; (c) the literal-count census is evaded by
    an assembled pattern (surviving mutation above); (d) no site's message or field name changed, so there
    is no user-visible behaviour to cover — the edge table is the behavioural evidence.

See validation disclosure guidance.

Frontend / Visual Evidence

  • UI impact: none
  • Before: N/A
  • After: N/A
  • States and viewports shown: N/A
  • Source data: none
  • Attention review: N/A

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring (no functional changes) — the bodies are byte-identical and every site applies
    fullmatch, asserted over a 10-value edge table
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)

Technical Direction

Shared-authority RFC fixture impact

N/A for this slice — no .ts file, conformance fixture or dual-runtime twin budget is touched. That is
why the two TypeScript copies of the same body are listed as a successor instead of being folded here: a
cross-runtime owner would be a claim against that programme and needs its own evidence.

The compact opaque id -- 1 to 200 characters of [A-Za-z0-9._:-], fullmatch -- was
compiled four times under four names (chat_actions._OPAQUE_ID,
chat_action_store._OPAQUE_ID, goal_ref_validation.GOAL_ID,
botmux_runtime._SAFE_TOKEN), each with its own field wording, so a bound change
had four places to land and three of them would silently keep the old limit.

The body now lives in public_safe_text.OPAQUE_ID_PATTERN beside the sibling
identifier shapes, which already state that they carry syntax only. Each site
binds it under its own name, so error text and field names are unchanged.
deletion_service still compiles its own copy and is declared by file and count
because loopx-project#5854 restructures that file.

Signed-off-by: Hsuehtan <296098438+Hsuehtan@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=xhigh

动机

维护 Chat action、Goal 引用和 BotMux 的贡献者,需要保持同一种标识符格式一致。此前同一个200字符格式在五处重复定义;现在四个入口直接引用公共定义,字段名、拒绝消息和附加权限规则保留。四个实际校验入口的604组输入与基线输出、错误类型及消息一致;源码规则改为集中维护。这次不完成整个语义收敛计划,也不把格式合法当成发布或路径操作授权。删除服务的第五处定义和两个 TypeScript 副本仍独立存在;动态拼接模式的防复制守卫尚不完备。

评审 head e9944906a550f062ed1e31161fbf4852acc5877e,实际共同基线 aa87cc019e1062455e79c934764b5e85688663cd。规范依据 issue #4447,文本revision sha256:6d3b7a43054bf5d8124a8974574b6f8992230fae4134d50125eb23732d960ccb(2026-09-29更新):Track A 要证明修复了具体重复权威并验证调用者行为。此项实现;没有改预算或关闭整项tracker。基线公共owner的“syntax only / consumer retains policy”约束也保留。

改动思路

共享格式归入已有 public_safe_text,调用方保留字段、消息和权限策略;本次无需新增能力或通用框架。四个 Python 调用入口共用不变格式;删除服务、TS副本和动态 census 留在已说明的边界之外。相似但不同长度、首字符或principal前缀的规则没有强行合并。Python这里只复用既有语法owner,没有新增控制面决策源;TS配对不因这次小切片改写。

具体改动

关键代码讲解

  • OPAQUE_ID_PATTERN(public_safe_text.py:322)增加既有1–200字符格式的共享export,保留原字节及fullmatch调用。
  • chat_actions._opaque 与 chat_action_store._opaque_id(均:96)原先的强制转换、strip、返回值和逐字段错误消息不变,只改pattern来源;后续operation存储/执行规则不在此处重建。
  • goal_ref_validation.require_goal_id(:10)保留原“不隐式strip”的校验和Goal instance检查;botmux_runtime._validated_private_id(:376)保留自己的strip及label错误,引用同一个语法对象。
  • 既有 test_compact_identifier_shape_owner 扩展为16项:声明位置、四个alias、200/201边界、空值/斜杠/换行/U+2028及独立principal格式。全Python literal census只允许公共owner和声明尚未迁移的deletion_service;它仍是源码字面量扫描。

全部6路径已阅读:5个生产文件、1份现有测试,+88/-4。没有新配置、UI入口、持久化字段、调度或权限概念。

对主干的风险

无阻断发现;一个非阻断P2建议:不要将对象identity描述为独立防复制证明。 在当前Python中,动态拼接后 re.compile 同样格式会从regex缓存返回公共owner的同一对象;实际探针确认,既有identity断言仍通过。literal census又看不到拼接,因此两者结合仍不能覆盖这个已承认的动态逃逸。建议修正文案的证明范围,后续在现有guard中复用AST/import检查,替换字面量census而非另加平行规则。当前四个实际调用方确实引用owner,不受此限制影响。

独立验证:owner16项、公安全/BotMux相关289项通过;不可变实际base/head的604输入×4真实函数结果完全一致(含200/201、None、数值、空白、Unicode和字段消息)。operation/source-session共65项在base/head均63通过、2失败;两项相同node与断言细节均在未改的 goal_instance_scope.py:75 抛出 stopped-Goal 的ValueError,而fixture期待另一异常。它们不是regex变更引入,单独保留测试健康/合并资格缺口,没有降低断言。

原生canary在精确head选择本PR六路径,5项直接检查和10项风险检查通过、无manualhold;全树semantic通过。开发advisory0个受支持新载体只是有界扫描结果,不证明完整语义等价。早先误用不支持的canary参数、归档未完成时的测试定位失败均保留并纠正,不算有效验证。按当前配置没有查询CI。上述测试使用隔离合成state;没有更改活动Goal或运行模型任务。

我的整体评价

APPROVE,head e9944906a550f062ed1e31161fbf4852acc5877e,带上述非阻断P2建议。 它将未来格式维护从四个同步编辑点收敛到已有owner,实际用户入口和错误路径保持一致,对维护效率有正向价值;不声称运行时速度提升。源码单源化本身已交付,剩余第五处、TS以及动态guard完整性如实留出。

未来改造检查已用于删除四处重复知识、保留不同policy的caller边界;更强census可在现有测试owner内有界替换,当前不需要投机框架。实际消费者/负例经验用于选择独立对照,未继承历史裁决或证明记忆因果收益。两项基线测试失败属于独立质量问题,通过评审不等于自动合并授权。

English verdict: APPROVE - e994490: four production callers reuse the existing syntax owner with identical results over604 inputs each;16+289 tests and5direct/10canary checks pass. Two unchanged stopped-Goal fixture failures reproduce on the immutable parent. Nonblocking P2: Python regex caching makes identity alone insufficient to detect dynamically reconstructed duplicate definitions; retain the stated census limit.

@huangruiteng
huangruiteng merged commit 73dfd1f into loopx-project:main Oct 8, 2026
1 check passed
huangruiteng added a commit that referenced this pull request Oct 9, 2026
* fix(control-plane): use canonical digest matcher

Signed-off-by: mika <211269698+mikamikasuki@users.noreply.github.com>

* test(control-plane): pin canonical digest consumer

Signed-off-by: mika <211269698+mikamikasuki@users.noreply.github.com>

* feat(benchmark): allow explicit monitored shared startup margin

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

* feat(benchmark): preserve native ranking with scalar best feedback

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

* fix(benchmark): require native winner for new best signals

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

* refactor(benchmark): use native ranking as sole best feedback rule

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

* refactor(public-safety): decide the opaque id body in one owner (#5880)

* test(manager): align exact Todo evidence mock shape (#5961)

* fix(benchmark): keep empty native winners silent

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

---------

Signed-off-by: mika <211269698+mikamikasuki@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Co-authored-by: mika <spacexstarship01@outlook.com>
Co-authored-by: huangruiteng <huangrt01@163.com>
Co-authored-by: Hsuehtan <haonhsu@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants