Documentation menu

Conformance tooling

check_conformance.py is dependency-free and uses only the Python standard library. Run it from any working directory:

python3 tools/check_conformance.py

It resolves the draft specification, schema, fixture, and conformance roots as one logical snapshot by default. Pass --snapshot draft|YYYY-MM-DD to validate one snapshot.

The selected key must be the literal draft or a real calendar date in exact YYYY-MM-DD form. To validate one frozen snapshot explicitly:

python3 tools/check_conformance.py --snapshot 2026-08-27

To validate draft, discover and validate every dated snapshot, and enforce released immutability:

python3 tools/check_conformance.py --all

In --all mode, the checker scans the four parallel roots below, rejects every non-draft directory whose name is not a valid exact date, and validates draft plus each discovered date. A version present under only some roots therefore fails as an incomplete snapshot. It selects the chronologically newest exact-date release tag reachable from HEAD and compares every date snapshot present at that tag against the complete working tree. Modification, deletion, rename, replacement, or addition beneath a released version fails even when a refreshed manifest would otherwise match. A wholly new version remains allowed and must independently pass complete-snapshot validation. When no release tag is reachable, the repository is in first-release bootstrap mode: internal validation still runs, but there is no released immutable baseline yet.

Release preparation uses python3 tools/freeze_snapshot.py YYYY-MM-DD. The command validates the mutable draft, copies all four trees, retargets only the defined version/status/path/schema-ID and on-wire fields, refreshes copied hashes, validates the date snapshot in staging, and refuses an existing destination. Review and commit the resulting snapshot before creating the matching date tag.

Each selected snapshot must exist under all four parallel roots:

spec/<snapshot>/
schema/<snapshot>/
fixtures/<snapshot>/
conformance/<snapshot>/

The directory key, manifest snapshotVersion, and on-wire protocolVersion must agree. They are all draft for mutable work and all the same publication date for a release. The requirement and artifact manifests provide the revision metadata that the checker cross-checks.

It checks:

  • JSON parsing with duplicate-key rejection and basic Draft 2020-12 schema structure;
  • snapshot/status agreement, public schema identifiers, protocol-version constants, and offline-only $ref resolution;
  • golden fixture framing, schema outcomes, hashes, and request/event ID equality;
  • requirement ID uniqueness, requirement text hashes, document anchors, headings, and artifact references;
  • absence of private Notion URLs and broken relative Markdown links;
  • schema, fixture, and conformance profile manifest drift; and
  • SDK-generation protocol version and stable names for schema roots and definitions in the schema manifest.

CI fetches full history, runs the standard-library checker tests, and invokes check_conformance.py --all; the Python checker owns snapshot discovery and newest-reachable-release-tag selection.

Supported JSON Schema subset

The built-in fixture validator supports the keywords used by this repository: $ref with local JSON Pointers, type, const, enum, required, properties, patternProperties, additionalProperties, propertyNames, items, minItems, maxItems, uniqueItems, contains, minContains, maxContains, string lengths and patterns, numeric bounds, allOf, anyOf, oneOf, not, if/then/else, and the uri and date-time formats.

This validator is intentionally not presented as a complete JSON Schema implementation. The schemas declare Draft 2020-12 and can also be consumed by a conforming general-purpose validator. The local subset keeps repository CI deterministic and dependency-free.

While Working Draft artifacts are changing, refresh manifest hashes with:

python3 tools/check_conformance.py --update-manifests

--update-manifests is limited to the mutable draft snapshot and only refreshes hashes already declared in its manifests. It is not an approval step or a release-freezing command. Date-stamped snapshots are immutable.

Focused checker tests

Run the focused checker tests with:

python3 -m unittest discover tools/tests

The suite exercises the snapshot resolver, --all behavior, and structural safeguards, including literal-draft and calendar-date formats, release retargeting, cross-root version agreement, required parallel roots, path confinement, manifest hash drift, offline schema references, aggregate schema coverage, first-release behavior, newest reachable release-tag selection, rejection of manifest updates for frozen snapshots, and full-tree comparison against temporary Git repositories. Run both the focused tests and python3 tools/check_conformance.py --all when changing checker behavior or any part of a logical snapshot.

The release freeze commands and required pre-tag validation are documented in the release policy.

SDK model generator

The Rust generator reads SDK-generation metadata directly from the schema manifest and emits a language-neutral IR or TypeScript wire models and loss-preserving structural codecs:

cargo run --locked --manifest-path tools/sdk-codegen/Cargo.toml -- \
  check --revision draft

cargo run --locked --manifest-path tools/sdk-codegen/Cargo.toml -- \
  generate --revision draft --language typescript --output /tmp/ahp.generated.ts

The structural codecs preserve unknown JSON. See tools/sdk-codegen/README.md for their compatibility contract.

Full draft TypeScript bundle

python3 tools/generate_sdk.py --sdk ../typescript-sdk emits the full draft codec, canonical schemas, and source lock. Add --check for byte-for-byte reproducibility without writing SDK artifacts. See boundary APIs and runtime scope.

python3 tools/generate_sdk.py --all regenerates TypeScript plus the sibling Python, Go and Rust codecs, canonical schema bundles and source locks. Use --all --check to compare every output without modifying SDK artifacts. TypeScript codecs and schema bundles use Prettier 3.6.2 via pinned npx, with configuration discovery disabled. Python codecs use Ruff 0.12.12 with isolated defaults and target Python 3.11; the generator rejects other versions. Install it into the Python interpreter used for generation: python3 -m pip install ruff==0.12.12. Node.js 20 with npm is also required; npx downloads the pinned Prettier on first use (or uses its local cache). Go output is normalized with gofmt (CI uses Go 1.24); Rust output uses rustfmt +1.88.0 --edition 2024. Install these toolchains before generating or checking. Both integration and synchronization CI install formatter dependencies. These same formatter versions/options apply to handwritten SDK sources; do not hand-format generated code. Run regeneration followed by --all --check to verify that the committed output is reproducible. CI uses --output-dir generated to stage the same codecs, canonical bundles, and content-derived locks per language, then installs them at the local-script paths. Source commit provenance remains in the synchronization PR, not in the reproducible content lock. See the draft specification for effect, discovery, content and event shapes, and the boundary API documentation for runtime limits.

Format validation in the canonical subset

The checker applies absolute RFC3986 URI grammar and RFC3339 lexical/calendar checks, not scheme-only URI checks or Python’s broader ISO-date acceptance. Unescaped whitespace, invalid percent escapes, malformed IP literals, compact dates and missing timezone separators are rejected. Integer-valued JSON numbers such as 1.0 satisfy integer; booleans do not. This is not URL authorization or HTTP header validation. JSON Schema patterns retain search semantics; exact SHA-256 fields therefore additionally enforce minLength/maxLength 64.

SDK sync app permissions

The post-merge sync-sdks.yml workflow uses install_generated_sdk.py to install generated artifacts and update each SDK CI checkout to the lock’s immutable protocol source commit. Reuse the AHP bot installed on all four SDK repositories and the website. Configure organization Actions secrets AHP_BOT_APP_ID and AHP_BOT_APP_PRIVATE_KEY, with selected-repository access restricted to agent-hooks-protocol and website. These are the two repositories that mint tokens; target SDK repositories do not need the private key. SDK sync, website dispatch, and website sync all use these shared names. Do not create same-named repository entries, which override organization values.

Create the organization entries before merging the workflow migration. After successful SDK and website sync runs, remove the obsolete repository variables and secrets with the SDK_SYNC_ and WEBSITE_SYNC_ prefixes.

Before enabling sync, the app must request Contents: read and write, Pull requests: read and write, and Workflows: read and write repository permissions, and the organization installation must approve those permissions. Workflows write is required because synchronization updates .github/workflows/ci.yml; Contents write alone cannot push that change. An app owner must configure the additional permission and an authorized organization owner must approve the installation permission update. Changing this workflow does not grant app permissions.

Token creation explicitly requests all three write permissions for the selected SDK repository. If the app or installation lacks one, the sync job fails at Generate installation token, rather than later when pushing the SDK PR. Confirm the approved installation permissions before merging a sync change or retrying a failed sync run.