CLAUDE.md@packages · git:20260706.7279a29 · 2026-07-06 · sha256 1e4f45742bd7006c

CLAUDE.md@packages git:20260706.7279a29A

Immutable. This exact content is served forever at /api/v1/blob/1e4f45742bd7006c.

# CLAUDE.md - packages/

`packages/aci-protocol` and `packages/plugin-format` are **frozen interfaces**.
Read the root `AGENTS.md`'s "Frozen contracts: STOP and escalate" section first
-- it is not optional here.

## The rule

Every lane in this repo (runner, worker, CLI, UI) compiles against these two
packages across three languages. Pydantic models are the single source of
truth; JSON Schema, generated TypeScript, and generated Rust are committed
derivatives, never hand-edited.

**Do not change either package unilaterally from a dependent lane.** If your
task needs a change here, stop and raise it in an issue/PR first -- a contract
change lands as its own reviewed, backward-compatible change before dependent
lanes proceed.

## When a change is genuinely approved

1. Bump the relevant version constant (`aci_protocol.version.PROTOCOL_VERSION`
   for the ACI; the plugin-format schema has no separate version today).
2. Regenerate every committed artifact and check for drift:
   ```bash
   ./scripts/check-contracts.sh
   ```
   This regenerates both JSON Schemas, the generated Rust crate, and the
   generated TypeScript, then diffs them against what is committed. It fails
   loudly if anything drifted and was not regenerated.
3. Commit the regenerated schema and generated types together with the model
   change, in the same commit.

## Enforcement

- `tests/test_schema_compat.py` in each package regenerates its schema
  in-process and fails if the committed copy differs -- this is the CI gate,
  not just a local nicety.
- CI additionally compiles the generated Rust (`cargo test` against
  `packages/aci-protocol/generated/rust`) and the generated TypeScript
  (`tsc --noEmit`), so a schema that "compiles in Python" but breaks either
  target still fails the build.

## Model conventions specific to these packages

- **`aci-protocol` is strict.** The wire contract rejects unknown fields
  (`deny_unknown_fields` equivalent in both Python and generated Rust) and
  rejects any `version` other than the exact `PROTOCOL_VERSION` (no
  same-major looseness in the 0.x line). If you are tempted to loosen this
  for a consumer's convenience, that is a version-policy change -- raise it in
  an issue/PR first, do not quietly relax a model.
- **`plugin-format` is lenient by design.** Its models use `extra="allow"`
  because real Claude Code plugin bundles carry keys this MVP does not model;
  rejecting them would reject valid bundles. Do not add strict validation
  here without checking this is compatible with the "verbatim Claude Code
  shape" mandate -- the wedge is compatibility, not schema purity.
- **Field names mirror the real Claude Code plugin format verbatim**
  (`allowed-tools`, not `tools`; `.claude-plugin/plugin.json` as the primary
  manifest location). Never invent a friendlier field name here.

## Verify

```bash
uv run pytest packages/aci-protocol/tests packages/plugin-format/tests -q
uv run ruff check packages/
uv run mypy
```