Skip to content

refactor: one owner for shared values, wire types and sync checks - #426

Merged
Grivn merged 11 commits into
mainfrom
claude/contracts-one-owner
Oct 17, 2026
Merged

Grivn merged 11 commits into
mainfrom
claude/contracts-one-owner

Conversation

@Grivn

@Grivn Grivn commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator

摘要 / Summary

技术债审查(M2、M3)发现:Starter 的 Host 复制了 Source 中的取值,部分 wire 结果类型声明在浏览器安全的协议之外,RPC 端点名在两端各写一份,设置表单重新实现了 Host 的 Git 同步校验。本次让它们各自只有一个归属:Host 直接从各 Source 的 contracts 取值;结果类型移入 protocol.ts;MNEMON_RPC_ENDPOINTS 列出 Host 提供的全部 75 个端点并为客户端调用加上类型;Git 同步校验规则放进浏览器安全的 sync-rules.ts,前后端共用。用户和模型看到的内容不变,已保存的配置照旧通过校验,超时范围不变(路线图第 4 项)。

The technical-debt review (M2, M3) found values the Starter's Host copied from the Sources, wire result types declared outside the browser-safe protocol, RPC endpoint names spelled freely on both sides, and a settings form that re-implemented the Host's Git sync checks. Each now has one owner: the Host takes the values from the Sources' contracts, the result types live in protocol.ts, MNEMON_RPC_ENDPOINTS lists the 75 endpoints the Host serves and types the client's calls, and one browser-safe sync-rules.ts holds the Git sync checks both sides run. Nothing users or the model see changes, every stored configuration validates as before, and the timeout bounds stay (roadmap item 4).

关联 Issue 或背景 / Related Issue or Context

Items M2 and M3 of the technical-debt review after v0.5.31 (#413).

涉及区域 / Affected Areas

  • Host 激活、Headless 或 bundle / Host activation, Headless, or bundle
  • 运行时记忆 / Runtime Memory
  • 项目档案 / Project Documents
  • 记忆空间或 Provider / Memory Spaces or Providers
  • 子 Agent 或 Agent 工作流 / Subagent or Agent workflow
  • Web UI 或对话交互 / Web UI or conversation interaction
  • 设置、存储或安全 / Settings, storage, or security
  • CLI、RPC、命令或工具 / CLI, RPC, commands, or tools
  • 安装、更新或发布 / Installation, update, or release
  • 测试、构建或文档 / Tests, build, or documentation
  • 其他(请说明)/ Other (explain below)

PR 类型 / PR Type

  • 面向用户的功能或行为变更 / User-facing feature or behavior change
  • Bug 修复 / Bug fix
  • 增强或优化 / Enhancement or optimization
  • 兼容性适配 / Compatibility change
  • 维护或重构 / Maintenance or refactor
  • 测试或构建 / Tests or build

变更内容 / Changes

  • Values from the Sources (M2). protocol.ts drops its copies of the memory categories, sources, edge types, intents and embedding defaults; tools.ts (the named tools' enums) and config-values.ts take them from dsh-mnemon-source-memory-spaces/contracts. Runtime limit defaults come from RUNTIME_MEMORY_LIMITS and the ceiling from a new RUNTIME_MEMORY_LIMIT_MAX_BYTES; the placement capabilities from a new MEMORY_PLACEMENT_CAPABILITIES. A test keeps the mnemon Entry in cordis.patch.yml equal to the schema and resolveConfig defaults. The two timeout bounds stay, each with a comment naming the other.
  • Wire types and one endpoint list (M3). DelegatedAnswerResult, CoordinatedDocumentResult and CoordinatedRuntimeMemoryResult move into protocol.ts (re-exported by subagent.ts). MNEMON_RPC_ENDPOINTS lists the 75 endpoints on 9 channels; the client's calls, component operations by mode and the remote settings guard (a typed map that fails closed) are typed against it, and a test keeps it equal to what the Host dispatches.
  • One set of sync checks. src/host/sync-rules.ts (browser-safe, no imports, re-exported by protocol.ts) holds the branch, directory, repository, author email, machine name and GitHub repository rules both sides call; messages are byte-identical, and where the two sides disagreed each keeps its old behavior (listed on the roadmap candidates in the record).
  • Docs. The two Source READMEs name the new exports; interfaces.md (both languages) now says the review channel needs a trusted host, as the code does. Record docs/pr-assets/contracts-one-owner with an equivalence harness. Changeset: dsh-mnemon, dsh-mnemon-source-memory-spaces, dsh-mnemon-source-runtime patch.

最新代码确认 / Latest Codebase Confirmation

  • 我已基于最新 main 分支开发,或在提交前已 rebase 或合并最新 main。 / I developed from the latest main, or rebased or merged the latest main before submitting.

同步命令 / Sync command:

git fetch origin main
git merge origin/main

Branched from 03dd40a4 (#423); main 7a7e1ae1 (#424, #425) merged in 5aa86e40.

AI 编码披露 / AI Coding Disclosure

  • 完全 AI 编码:全部编程改动由 AI 产出,并由贡献者接受和审查。 / Fully AI-coded: AI produced all programming changes, which the contributor accepted and reviewed.
  • 部分 AI 辅助:AI 帮助编写或修改了部分内容。 / Partially AI-assisted: AI helped write or modify part of the change.
  • 未使用 AI 编码辅助。 / No AI coding assistance was used.

使用的 AI 模型 / AI model used: Claude Opus 5.5 (claude-opus-5-5)

使用的编码 Agent 工具 / Coding Agent tool used: Claude Code

仓库规范检查 / Repository Rules

  • 未修改 DSH 官方源码,未让 tsconfig 指向 DSH 源码 checkout,仅使用正式的 @deepseek-ai/* NPM 契约。 / I did not modify DSH source or point tsconfig at a DSH source checkout, and used only published @deepseek-ai/* NPM contracts.
  • Client 与 Host 边界仍以浏览器安全的 src/host/protocol.ts 为准,没有在两侧重复定义 wire DTO。 / The Client and Host boundary still uses browser-safe src/host/protocol.ts as the source for wire DTOs.
  • 持久化格式、RPC 权限、路径或凭据处理的变更包含兼容或拒绝路径、安全分析和相应测试。 / Changes to persistence formats, RPC authority, paths, or credentials include compatibility or rejection paths, security analysis, and tests.
  • 没有提交 token、密钥、私有记忆、未脱敏日志或生成的 lib/ 文件。 / I did not commit tokens, credentials, private memory, unredacted logs, or generated lib/ files.
  • 用户可见文案和长期文档已同步维护中文与英文版本,命令、配置键和路径保持一致。 / User-facing copy and long-lived documentation are synchronized in Chinese and English, with matching commands, configuration keys, and paths.
  • 会改变发布制品或其元数据的 PR 已添加 changeset;仅测试、CI 或站点文档变更可不添加。 / A PR that changes a published artifact or its metadata includes a changeset; test-only, CI-only, and site-documentation-only changes may omit one.
  • 新增和修改的代码、注释、文档、提交信息不含 emoji。 / New and modified code, comments, documentation, and commits contain no emoji.

兼容性与数据安全 / Compatibility and Data Safety

  • No behavior change. Tool schemas (the model-tools fixture is unchanged), the Host settings schema, Config({}), resolveConfig({}) and the config-values exports are byte-identical; every endpoint keeps its name, channel and authority; a malformed remote settings endpoint is refused exactly as on main (with a test); sync check messages are identical (a harness over 4,650 inputs gives the same digest on main and the branch).
  • Version skew. The Host imports two new contract exports from Memory Spaces and Runtime at load. DSH resolves the Starter's pinned copies under the Starter first, so a Profile's own copy of a Source cannot shadow them; the three packages release together under exact pins (a dry run gives Starter 0.5.32 pinning the new Memory Spaces and Runtime). Only lib/index.js changes among the Starter's entries, so Sources importing dsh-mnemon/* are unaffected.
  • Client bundle. +1,870 bytes for the sync rules and the component host's endpoint lookup; no Node-only code; the boundary specs pass.

本地验证 / Local Validation

执行的命令 / Commands run:

pnpm run verify
MNEMON_CLI_PATH=/nonexistent/mnemon MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 pnpm run verify:plugins
DSH_HOST_ROOT=<DSH 0.2.0-rc.2> pnpm run test:installed-dsh
node scripts/verify-sync-git.mjs
CHANGESET_BASE_REVISION=<main> CHANGESET_REVISION=HEAD pnpm run release:intent

结果摘要 / Result summary:

  • On the merge 5aa86e40 and on a8c40265: verify passed (root tests 2,108 passed, 7 skipped in 151 files; plugin tests 593 passed, 3 skipped; deterministic build; Headless; package with publint and attw); verify:plugins passed; installed DSH 0.2.0-rc.2: 2,105 passed, 10 skipped; Git sync 64/64 on 0.2.1-rc.2 and on 0.2.0-rc.2. After the review follow-ups: typecheck, the affected specs (78 tests in 7 files), the root tests (2,109 passed, 7 skipped), verify:docs and release:intent (all three packages) pass.
  • The author's review ran a differential fuzz of the sync checks over about 300,000 inputs and found no behavior change. An independent review then checked packaging and resolution in DSH 0.2.1-rc.2, all 75 endpoints channel by channel, the tool enums and settings schema byte for byte, and the client bundle; it found nothing must-fix, and its three nits (the malformed-endpoint refusal, the record's compile claim, the READMEs) and a pre-existing interfaces.md error are fixed in 1928b1ab..6e928b30.

用户可见变更证据 / Local Feature Evidence

证据 / Evidence: N/A, nothing users see changes; the record has the equivalence harness and the checks.

🤖 Generated with Claude Code

Grivn and others added 11 commits October 17, 2026 22:51
…eir contracts

The Host kept its own copies of values two Sources own:

- protocol.ts declared Memory Spaces' categories, sources, edge types,
  intents and embedding defaults. Since #421 no client code reads them, so
  protocol.ts stops declaring them: the named tools build their enums from
  Memory Spaces' ./contracts and config-values.ts re-exports the embedding
  defaults from there. Nothing is added to the client build's bundled list.
- config-values.ts copied the Runtime limits. The defaults now come from
  RUNTIME_MEMORY_LIMITS, and the ceiling from RUNTIME_MEMORY_LIMIT_MAX_BYTES,
  a new export of the Runtime contracts.
- The placement capabilities were listed in config.ts, in Memory Spaces'
  contracts (as a type) and in its config.ts. Memory Spaces' contracts now
  export MEMORY_PLACEMENT_CAPABILITIES and derive the type from it; both
  schemas take it. Memory Spaces' Source also builds its own category,
  source, intent and edge checks from its contract lists.

The timeout bounds stay as they were in both places (100..120,000 ms in the
Host, 1..600,000 ms in Memory Spaces); a comment at each names the other and
the roadmap item that will align them.

Tests: the Starter patch writes only the code's defaults into the mnemon
Entry (the schema's and resolveConfig's); the Host bounds the Runtime limits
as the Runtime Source does; both schemas take exactly the listed placement
capabilities. The model-visible tool fixture is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocol

CONTRIBUTING makes src/host/protocol.ts the browser-safe source of the
Client and Host wire DTOs. Three results that cross the RPC boundary were
still declared in the coordinator (subagent.ts): DelegatedAnswerResult
(agent-search), CoordinatedDocumentResult (Document writes and archives) and
CoordinatedRuntimeMemoryResult (runtime memory writes, as a management
result's value). They move into protocol.ts; subagent.ts re-exports them as
it does DelegatedWriteResult. RecallResult stays: it is a tool result the
model reads, not an RPC answer.

The endpoint names were free strings on both sides. protocol.ts now lists
every endpoint the Host serves, by channel (MNEMON_RPC_ENDPOINTS), with the
same names. callMnemonRpc, MnemonClient's calls, its component operations
and the operations a page lends a component are typed against it, so a
misspelled name or one sent on the wrong channel no longer compiles. The
View handler and the remote settings guard read their names from it instead
of their own literal lists. No name, message or dispatch order changes.

tests/rpc-endpoints.spec.ts registers the Host's handlers and checks that,
against every name the list holds and every word the handler modules spell,
each channel serves exactly the listed endpoints; it then records what the
client sends from each of its callers and checks that every call is listed,
and that the listed endpoints it never calls are exactly the 28 the Host
keeps for other callers (the direct paths the workbench left in September
2026, the single-turn activity read and the View preview and plugin
install).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and the Host

src/client/sync-view.ts re-implemented, "as the Host checks it", the rules
in src/host/sync-config.ts, and the machine name and GitHub repository name
rules of machine-identity.ts and github-auth.ts. The rules move into
src/host/sync-rules.ts, which imports nothing and which protocol.ts
re-exports, with MNEMON_MACHINE_LABEL_MAX. Both sides call them; every
message stays where it was, word for word.

Where the two sides differed, each keeps what it did:

- A local repository path: the form, which cannot know the Host's platform,
  takes portableAbsolutePath (a POSIX path, a Windows path on a drive or a
  UNC share); the Host passes its own platform's path.isAbsolute, so the
  same values validate on each platform as before.
- A NUL byte in a repository address or a directory: only the Host refuses
  it, as before.

The rest were the same rules: the branch checks the Host alone spelled out
follow from the shared pattern, and an empty author address keeps meaning
this machine's Git identity on the Host.

tests/sync-rules.spec.ts pins both sides' verdicts and where they differ,
and the browser boundary test now follows every module protocol.ts takes
values from, sync-rules.ts included, for Node imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to the endpoint list

Two places the endpoint list typed loosely:

- MnemonClient.componentOperation took any read or write endpoint in either
  mode. Its endpoint is now typed by the mode it is given, so a read
  operation named after a write endpoint no longer compiles. The page's
  lent operations, which pass either mode, compile as before.
- The API Gateway's settings guard let through any name in the shared
  settings list and asked for the trusted-host grant only for mutate, so a
  settings endpoint added later would have been reachable remotely without
  a decision. It now keeps what a remote page needs for each settings
  endpoint (get: read, mutate: management) in a map typed against the list,
  which does not compile until a new endpoint is given one.

Nothing a page receives changes: get and mutate answer as before, and any
other name, constructor and __proto__ included, gets the same unknown
endpoint answer, which a new test in tests/remote-rpc.spec.ts pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The endpoint test now also counts a View endpoint the list names but
  nothing dispatches, which passes the View guard and meets "Unknown View
  operation", as refused, so such a name fails it as it does on the other
  channels.
- The configuration tests pin the literal runtime limits (10,240, 4,096 and
  1,048,576 bytes) that stored settings are checked against, and the schema
  messages for a limit over the ceiling and for an unknown placement
  capability.
- The sync rule test checks the GitHub repository names through GitHub
  sign-in's own create(), with a stand-in credential store and GitHub, so it
  compares the form with the Host rather than with the shared rule.

All three pass with main's Host and client modules swapped in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…owner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ync checks

The record lists what moved where, where the settings form and the Host
still differ and why each keeps its rule, what stays as it is (the 28
endpoints only other callers use among them), the bundle and package
comparison with main 7a7e1ae, and a harness that runs both sides' sync
checks over 4,650 inputs with the same output on main's modules and this
branch's. Its results are those of the merge with main 7a7e1ae (#424,
#425).

The changeset covers dsh-mnemon and the two Sources whose contracts gain an
export, MEMORY_PLACEMENT_CAPABILITIES and RUNTIME_MEMORY_LIMIT_MAX_BYTES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… before

The remote settings map (3dd8b72) looked a name up with Object.hasOwn,
which reads its key as text, so a malformed endpoint from a paired remote
page such as ["get"] or ["mutate"] reached the settings handler or the
trusted-host refusal. Before the map, any name other than the strings get
and mutate got "unknown remote Mnemon settings endpoint" without the
handler being asked. Only a string is looked up now, so it does again. No
authority changed either way.

The new test sends ["get"], ["mutate"], 7 and null with and without the
grant; it fails without the check and passes with main's remote-rpc.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… READMEs

As #416 did for its contracts exports, each package README now names what
this branch adds to its ./contracts entry: RUNTIME_MEMORY_LIMIT_MAX_BYTES,
beside the existing RUNTIME_MEMORY_LIMITS, in the runtime Source's, and
MEMORY_PLACEMENT_CAPABILITIES, from which MemoryPlacementCapability is
derived, in Memory Spaces'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w channel

The channel table in the Interfaces reference said remote pages may read
/dsh-mnemon-review and need remoteAccess: trusted-host only for reconcile
and apply. MnemonRemoteService.review refuses every review endpoint without
the grant, as the paragraph below the table already says, so the row now
says that too, in English and Chinese. A test pins the refusal for each of
the six review endpoints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The record now says what the types prove about channels and modes: a call
that names its channel or mode itself cannot send a name that channel does
not serve, while one that picks it at run time, as assistSource and the
page's lent operations do, takes any name one of its channels serves. It
lists the string check in the remote settings guard, the two README
mentions and the corrected review row of the Interfaces reference, and the
checks run after them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Grivn
Grivn merged commit bd70979 into main Oct 17, 2026
7 checks passed
@Grivn
Grivn deleted the claude/contracts-one-owner branch October 17, 2026 18:11
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.

1 participant