Skip to content
This repository was archived by the owner on Aug 11, 2026. It is now read-only.

Latest commit

 

History

History
161 lines (131 loc) · 8.05 KB

CONTRIBUTING.en.md

File metadata and controls

161 lines (131 loc) · 8.05 KB

Contributing

English | 中文

Thanks for your interest in the Cindy protocol repository. This repo is the single source of truth for the wire protocols shared between Cindy's client and server. Consuming repositories reference it as a git submodule — which means every line changed here is a cross-repo contract change, so the process and discipline are stricter than a typical repository.

Contribution scope

This repo is open-sourced primarily to unblock external local development: the server code is not open source, but the server and client share this protocol — publishing it (the client mounts it as a git submodule) is what lets external developers fully clone the client, pnpm install, and build / debug it locally. In other words, this repo is mostly a prerequisite dependency for developing against the client, not the main arena for external contributions.

By contribution type:

  • Docs / translations (e.g. translating docs/*.md to English), validator (parse) bug fixes, additional tests, tooling / CI: external PRs are welcome and can be reviewed and merged on their own, with no server-side coordination needed.
  • Protocol-semantic changes (new message types / fields / envelope / routing semantics / constants): these require coordinated server + client implementation, and the server is closed-source — so they are led by maintainers through the two-stage flow below. If you have an idea, open a protocol proposal issue first; a maintainer implements it.
  • Security issues: see 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
  • pnpm install — install dependencies
  • pnpm test — run all tests
  • pnpm typecheck — tsc check across every package

Which repository should I open a PR against?

  • Changing only client/server code: open the PR in the corresponding consuming repository; you don't need to touch this repo. Remember to 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

Because a consuming repo's submodule pointer can only reference a commit that is already merged here, a protocol change proceeds in two steps:

  1. PR in this repo: the protocol change + parse validation + tests + docs (the relevant section under docs/) submitted together, with the motivation and compatibility impact spelled out. Wait for it to merge.
  2. PR in the consuming repo(s): after the PR here merges, open a PR in each consuming repo — bump the submodule pointer to the new commit and include the consumer-side adaptations.

Pointing a consuming repo's submodule at a commit that only exists in your fork does not work: the upstream CI cannot fetch objects that live only in your fork.

PR conventions

  • One logical change per PR: keeps review and rollback clean; don't mix a protocol change with unrelated refactors.
  • Branch naming: <type>/<short-description>, with type matching the commit type (feat/fix/docs/…), e.g. feat/slack-hook-multi-team, 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 an existing package: see the "extension guide / versioning" section of the relevant protocol doc — slack-hook §9, device-link §9.

Protocol-evolution discipline (hard rules)

  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.
  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".
  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.
    • device-link-protocol: the relay silently drops unknown kinds (the sender experiences a timeout black hole) — adding a kind that needs relaying is a both-sides-upgrade-together change; the EnvelopeKind set and PROTOCOL_VERSION must move in lockstep.
    • plugin-protocol: the manifest and the client HTTP envelope are versioned 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.
  6. Incompatible changes must bump the protocol version, and the PR must state the upgrade-window plan for the consuming repos.
  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/.

Test requirements

  • New message type / new field: add a round-trip case plus rejection cases for the field-interaction constraints.
  • Test fixtures must use neutral placeholder values (cindy, example.com, etc.). Do not use real email addresses, real organization names, or internal system identifiers.

Commit messages

Commit messages follow Conventional Commits: <type>(<scope>): <subject>.

  • type: feat / fix / docs / refactor / test / chore / ci / revert
  • 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 6 above).

The repo ships a commit template; after cloning, run it once so git commit prints the format hint:

git config commit.template .gitmessage

See docs/commit-convention.md for the reference (currently Chinese).

License and contribution grant

This repository is released under the Apache License 2.0. Unless you explicitly state otherwise, any contribution you intentionally submit for inclusion is licensed under the same terms, per Section 5 of Apache-2.0.