Skip to content
This repository was archived by the owner on Aug 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ labels: [bug]

## 涉及的包 / Affected package

<!-- @cindy/slack-hook-protocol | @cindy/device-link-protocol | @cindy/voice-protocol -->
<!-- @cindy/slack-hook-protocol | @cindy/device-link-protocol | @cindy/plugin-protocol | @cindy/model-access-protocol -->

## 复现 / Reproduction

Expand All @@ -24,4 +24,4 @@ labels: [bug]

## 版本 / Version

<!-- commit hash / 协议版本 (PROTOCOL_VERSION / HOOK_PROTOCOL_VERSION / VOICE_PROTOCOL_VERSION) -->
<!-- commit hash / 协议版本 (PROTOCOL_VERSION / HOOK_PROTOCOL_VERSION / schema version) -->
3 changes: 2 additions & 1 deletion .github/ISSUE_TEMPLATE/protocol_proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ labels: [proposal]

## 涉及的包 / Affected package

<!-- @cindy/slack-hook-protocol | @cindy/device-link-protocol | @cindy/voice-protocol -->
<!-- @cindy/slack-hook-protocol | @cindy/device-link-protocol | @cindy/plugin-protocol | @cindy/model-access-protocol
本仓库不接受新 package / This repository does not accept new packages. -->

## 动机 / Motivation

Expand Down
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
## 检查项 / Checklist

- [ ] 遵循 [CONTRIBUTING](../CONTRIBUTING.md) 的协议演进纪律 / Follows CONTRIBUTING's protocol-evolution discipline
- [ ] 未新增 package、未增加 package 数量、未扩大存量 package 的业务边界 / Adds no package, does not increase package count, and does not expand an existing package into an unrelated domain
- [ ] commit message 符合[规范](../docs/commit-convention.md) / Commit messages follow the convention
- [ ] 一个 PR 只做一个逻辑变更 / One logical change per PR
- [ ] 测试 fixture 使用中性占位值(无真实邮箱/组织名/内部标识) / Test fixtures use neutral placeholders
Expand Down
51 changes: 31 additions & 20 deletions CONTRIBUTING.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ By contribution type:
implements it.
- **Security issues**: see [SECURITY.md](SECURITY.md).

### The package set is closed (hard rule)

- This repository no longer accepts new packages. The set under `packages/`
may stay the same or shrink, but it must not grow.
- Compatible fixes, necessary evolution for real consumers, tests, docs,
removals, and consolidation of existing packages remain allowed.
- Do not create a package through a split or rename, keep a compatibility copy,
or put an unrelated domain into an existing package to bypass this rule.
- New domains, services, or cross-end protocols belong in their owning business
repository or require a separate architecture and distribution decision
outside this repository.
- Do not prebuild protocols for hypothetical future needs. Before deleting a
package, confirm that every known consumer has stopped referencing it; old
checkouts retain the content through their historical git pin.

## Development environment

- Node.js >= 22, pnpm 10.x
Expand All @@ -45,6 +60,9 @@ By contribution type:
`git clone --recurse-submodules`.
- **Changing the protocol itself** (message types, fields, envelope, routing
semantics, constants): open a PR here and follow the two-stage flow below.
- **Adding a protocol package**: this repository does not accept it. Return to
the owning business repository or complete an architecture decision outside
this repository first.

## Two-stage protocol-change flow

Expand All @@ -70,23 +88,23 @@ Because a consuming repo's submodule pointer can only reference a commit that is
`fix/device-link-version-check`.
- **PR description**: state the motivation and compatibility impact, and complete
the repo's PR-template checklist (especially the "trio" for protocol changes).
- **Where new message types/fields go and how to extend**: see the
- **Where new message types/fields go and how to extend an existing package**: see the
"extension guide / versioning" section of the relevant protocol doc —
[slack-hook §9](docs/slack-hook-protocol.md),
[device-link §9](docs/device-link-protocol.md),
[voice](docs/voice-protocol.md).
[device-link §9](docs/device-link-protocol.md).

## Protocol-evolution discipline (hard rules)

1. **Zero runtime dependencies**: no package here may introduce a runtime
1. **The package set may only shrink**: do not add packages; evolve an existing
package only for a real consumer requirement.
2. **Zero runtime dependencies**: no package here may introduce a runtime
dependency.
2. **`device-link-protocol` and `voice-protocol` must be React Native-safe**: no
`node:*` and no Node-only imports (mobile compiles these packages' source
directly).
3. **Append-only first**: prefer adding optional fields / new message types over
3. **`device-link-protocol` must be React Native-safe**: no `node:*` and no
Node-only imports (mobile compiles this package's source directly).
4. **Append-only first**: prefer adding optional fields / new message types over
changing the semantics of existing fields. Every new optional field must
document each side's degradation behavior "when the peer is an older version".
4. **Each protocol's compatibility strategy differs — don't mix them up**:
5. **Each protocol's compatibility strategy differs — don't mix them up**:
- `slack-hook-protocol`: `type` is an open set; an older peer that receives an
unknown type drops the frame without disconnecting — new message types are
inherently backward-compatible, but you must define the degraded experience.
Expand All @@ -98,16 +116,9 @@ Because a consuming repo's submodule pointer can only reference a commit that is
independently. Unknown optional fields may be ignored; an unsupported
manifest or envelope version must be rejected outright — the client keeps
its existing installs and never applies a partial update.
- `skill-protocol`: the publish manifest and client HTTP envelope are versioned
independently. Unknown optional fields may be ignored; unsupported versions
must be rejected, and a failed client update retains the existing Skill.
- `voice-protocol`: session request/response allow unknown fields and roll out
via optional fields; the refiner business payload strictly rejects unknown
fields to prevent project-key abuse. A missing `protocolVersion` is
interpreted as v1; an explicitly unsupported version is rejected.
5. **Incompatible changes must bump the protocol version**, and the PR must state
6. **Incompatible changes must bump the protocol version**, and the PR must state
the upgrade-window plan for the consuming repos.
6. **Changing a protocol means changing the trio**: type definition + parse-time
7. **Changing a protocol means changing the trio**: type definition + parse-time
runtime validation (error messages carry the field path) + tests (at minimum
round-trip and bad-frame rejection). Behavioral/semantic changes also update
the corresponding doc under `docs/`.
Expand All @@ -126,12 +137,12 @@ Commit messages follow [Conventional Commits](https://www.conventionalcommits.or
`<type>(<scope>): <subject>`.

- **type**: `feat` / `fix` / `docs` / `refactor` / `test` / `chore` / `ci` / `revert`
- **scope**: `slack-hook` / `device-link` / `plugin` / `voice` / `docs` / `repo`
- **scope**: `slack-hook` / `device-link` / `plugin` / `model-access` / `docs` / `repo`
- **subject**: imperative mood, no trailing period; `type`/`scope` in English,
the subject may be English or Chinese.
- **Breaking changes**: append `!` after the type (e.g. `feat(device-link)!: ...`),
write a `BREAKING CHANGE:` note in the body, and **bump the corresponding
protocol version in the same commit** (see rule 5 above).
protocol version in the same commit** (see rule 6 above).

The repo ships a commit template; after cloning, run it once so `git commit`
prints the format hint:
Expand Down
27 changes: 18 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
- **协议语义变更**(新消息类型 / 字段 / 信封 / 路由语义 / 常量):需要服务端 + 客户端协同实现,而**服务端闭源**——这类由内部维护者主导,走下面的两段式流程落地。外部若有想法,请先开一个[协议提案 issue](../../issues/new/choose)讨论,由内部实现。
- **安全问题**:见 [SECURITY.md](SECURITY.md)。

### Package 集合已封闭(硬性)

- 本仓库不再接收任何新 package;`packages/` 的集合只能保持或减少,不能增加。
- 允许现有 package 的兼容修复、真实消费方所需的必要演进、测试、文档,以及删除或合并。
- 禁止拆分或重命名出新 package、保留兼容副本,或把无关业务域塞进存量 package 绕过限制。
- 新业务域、新服务或新跨端协议必须在所属业务仓库处理,或在仓库外另行完成架构与分发决策。
- 不为推测性的未来需求预建协议。删除 package 前必须确认所有已知消费仓均已停止引用;旧
checkout 仍由历史 git pin 保留原内容。

## 开发环境

- Node.js >= 22,pnpm 10.x
Expand All @@ -24,6 +33,7 @@

- **只改客户端 / 服务端代码**:去对应消费方仓库,不需要动本仓库。消费方仓库 clone 时记得 `git clone --recurse-submodules`。
- **要改协议本身**(消息类型、字段、信封、路由语义、常量):在本仓库提 PR,走下面的两段式流程。
- **要新增协议 package**:本仓库不接收;请回到所属业务仓库或先完成仓库外的架构决策。

## 两段式协议变更流程

Expand All @@ -39,21 +49,20 @@
- **一个 PR 只做一个逻辑变更**:便于评审与回滚;协议改动与无关重构不要混在一起。
- **分支命名**:`<type>/<简短描述>`,`type` 与 commit 一致(`feat`/`fix`/`docs`/…),如 `feat/slack-hook-multi-team`、`fix/device-link-version-check`。
- **PR 描述**:写清动机与兼容性影响,并按仓库 PR 模板逐项勾选(协议变更尤其要过「三件套」)。
- **新增消息类型 / 字段落在哪、怎么扩展**:见对应协议文档的「扩展指南 / 版本纪律」小节——[slack-hook §9](docs/slack-hook-protocol.md)、[device-link §9](docs/device-link-protocol.md)、[voice](docs/voice-protocol.md)。
- **新增消息类型 / 字段落在哪、怎么扩展**:见对应存量协议文档的「扩展指南 / 版本纪律」小节——[slack-hook §9](docs/slack-hook-protocol.md)、[device-link §9](docs/device-link-protocol.md)。

## 协议演进纪律(硬性)

1. **零 runtime 依赖**:本仓库任何包不得引入 runtime dependency。
2. **`device-link-protocol` 与 `voice-protocol` 必须 React Native 安全**:禁止 `node:*` 及任何 Node-only import(mobile 端直接编译这些包源码)。
3. **append-only 优先**:优先加可选字段 / 新消息类型,不改已有字段语义;每个新增可选字段必须写清"对端是旧版"时两侧各自的降级行为。
4. **各协议的兼容策略不同,不要套错**:
1. **package 集合只减不增**:不得新增 package;存量 package 只有真实消费需求时才演进。
2. **零 runtime 依赖**:本仓库任何包不得引入 runtime dependency。
3. **`device-link-protocol` 必须 React Native 安全**:禁止 `node:*` 及任何 Node-only import(mobile 端直接编译该包源码)。
4. **append-only 优先**:优先加可选字段 / 新消息类型,不改已有字段语义;每个新增可选字段必须写清"对端是旧版"时两侧各自的降级行为。
5. **各协议的兼容策略不同,不要套错**:
- `slack-hook-protocol`:`type` 是开放集合,老端收到未知类型丢帧不断连——新消息类型天然向后兼容,但必须定义降级体验;
- `device-link-protocol`:relay 对未知 kind 静默丢弃(发送方表现为超时黑洞)——新增需要转发的 kind 属于**两侧同步升级**的变更,`EnvelopeKind` 集合与 `PROTOCOL_VERSION` 必须同步调整。
- `plugin-protocol`:manifest 与客户端 HTTP envelope 分别版本化。未知可选字段可忽略;不支持的 manifest 或 envelope 版本必须拒绝应用,客户端保留已有安装,不做部分更新。
- `skill-protocol`:发布 manifest 与客户端 HTTP envelope 分别版本化。未知可选字段可忽略;不支持的版本必须拒绝发布/安装,客户端更新失败时保留已有 Skill。
- `voice-protocol`:会话请求/响应允许未知字段并以可选字段做滚动升级;refiner 业务 payload 为防止项目 Key 被滥用而严格拒绝未知字段。缺省 `protocolVersion` 按 v1 解释,显式不支持的版本直接拒绝。
5. **不兼容改动必须升协议版本号**,并在 PR 里写明两个消费方仓库的升级时间窗安排。
6. **改协议必改三件套**:类型定义 + parse 运行时校验(错误信息带字段路径)+ 测试(至少覆盖 roundTrip 与坏帧拒收);涉及行为语义的同步更新 `docs/` 对应文档。
6. **不兼容改动必须升协议版本号**,并在 PR 里写明两个消费方仓库的升级时间窗安排。
7. **改协议必改三件套**:类型定义 + parse 运行时校验(错误信息带字段路径)+ 测试(至少覆盖 roundTrip 与坏帧拒收);涉及行为语义的同步更新 `docs/` 对应文档。

## 测试要求

Expand Down
Loading
Loading