Skip to content

Releases: Cognitive-Delivery/contract

1.2.1

Choose a tag to compare

@github-actions github-actions released this 06 Oct 06:33
3088e44

Tooling only; the schema set is still 1.2 and /1.x/ serves the same bytes. Found by pinning the
reference implementation to 1.2.0: the type generator, the Pages deploy on a tag, and the narrowing
vectors being the reference's own bytes. One additive field, capabilities.tool_args on the plugin
schemas, so capabilities stays the lease allow shape property for property.

Fixed

  • The type generator reads .schema.json only, names lease-record, and says what a type cannot.
    It tripped over schemas/index.json and had no name for the record; and it silently dropped every
    keyword TypeScript has no words for. The generated file's header now lists them, per keyword with a
    count and an example site (allOf with if/then, propertyNames, pattern, the bounds), so a
    reader of the types knows to validate with the schema as well.
  • capabilities.tool_args on both plugin schemas: the lease allow shape, property for property,
    now that allow carries tool_args (development stability, as there).
  • The narrowing vectors are the reference's bytes. tool-args-dropped was added by hand in 1.2.0;
    the file is now regenerated from the reference implementation (CDF Harness) as the others always
    were, which placed the vector second and gave it the diff the generator records.
  • A valid lease-record fixture's reason no longer contains the word "secret", which the reference
    deployment's corpus hygiene test bans as a marker.
  • The frozen copy deploys after a release. The github-pages environment admits main only, so
    the tag push that was meant to lay out /1.2.0/ was refused at the deploy step; release.yml now
    dispatches pages.yml on main after publishing (it fetches every tag), and the tag trigger is
    gone. 1.2.0's copy was deployed by hand the same way and is byte-identical to the tag.

npm: https://www.npmjs.com/package/@cognitive-delivery/contract/v/1.2.1
Provenance: published from this workflow run with npm publish --provenance; verify with npm audit signatures after installing, or search Sigstore for pkg:npm/%40cognitive-delivery/contract@1.2.1.
Schemas: https://cognitive-delivery.github.io/contract/1.x/

1.2.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 06:05
8cd038d

Schema set 1.2 (CDF spec contract-batch-two-implement-all, DR-183): the second review's fourteen
improvements, from a package that could not run its own test to a corpus that runs under six other
validators. Additive within 1.x by the contract's own rule, mechanically checked: against 1.1.0
the guard reports 115 allow-listed tightenings, each naming the exact value it admits and the
fixture or recorded check that proves no conformant writer ever produced what it now refuses, and
every real journal in the reference deployment validates with zero rejections (33,608 audit
records, 19,231 CDI signals, 995 provenance records, 2 assessments, the six-line lease journal, the
workspace config). No byte of any existing hash or signature changes. One new schema,
lease-record; one field at development stability, allow.tool_args; everything else stable.

Added

  • lease-record, the decision side of the lease (SPEC §6.1). One line of the lease journal:
    nine events (granted, refused, narrowed, heartbeat, attached, revoked, stopped,
    completed, expired) with what each must carry, enforced by conditional requirements; a
    refusal's reasons are reserved codes (R<n>, runtime_error:<name>, vendor:<vendor>:<code>)
    with the prose in reason; declared_hash and granted_hash tie a record to the exact bytes
    decided; a revoked record names by (an ancestor lease or issuer) and rule L3 refuses any
    other, given the journal. The schema carries the manifest and the lease inlined, held identical to
    their sources (conformance/inlined-copies.mjs). Eleven valid fixtures, five invalid, one by rule.
    The reference deployment's real journal validates unchanged; a 1.1 granted record carries no
    granted_hash and a reader may compute it.

Changed

  • Hosts, commands, tools and approvals have one identity rule each, enforced by the schemas
    (SPEC §4.4). A host is a lower-case DNS name (IPv4 literals and localhost included), with at most
    a single leading *. label, or *; a scheme, a port, a path, whitespace, upper case, a trailing
    dot and an interior wildcard are refused. A command is the executable's basename: no path, no
    arguments, no shell operator. A tool or approval is an identifier of at most 128 characters that
    is never * and carries no whitespace. The same rules apply to the plugin schemas' capabilities,
    which is the lease allow shape. Eighteen invalid fixtures, one per refused form, and one valid
    fixture carrying every admitted form. agent.name is capped at 120 characters and described as
    never a person's name; intent.purpose at 500. tooling/inline-granted-manifest.mjs rewrites the
    inlined copy in agent-lease.schema.json from its source, so the identity check has a tool to
    satisfy it.
  • Lease rules L1 and L2, and one timestamp form inside the signed bytes (SPEC §6). A schema
    cannot say "expires_at is after issued_at" or "not its own parent", so conformance/lease-rules.mjs
    does, the runner applies it to every valid lease and to fixtures/invalid-by-rule/ (Expired,
    TooEarly, SelfParent, named after the UCAN 1.0 fixture errors where one exists), and an
    adapter proves its own rules through a rules hook, reported unchecked when absent.
    issued_at and expires_at admit only UTC with milliseconds and Z, because an offset form
    hashes the same instant to different bytes; every real lease already uses it. attestation.signature
    is 64 hex like the lease's own; budget.depth ≤ 16 and budget.fan_out ≤ 256.
  • An allow-list entry admits one tightening, not every later one at the same path. Every guard
    finding now carries after, the exact value it admits (pattern=…, maximum=16, enum=[…]),
    and an entry must name it. Found while lowering depth's ceiling: batch one's entry for the
    2^53 maximum would have covered it silently. Every existing entry gained its after; three
    guard scenarios prove the match is exact.
  • Inline plugin hooks and MCP servers are typed, and a manifest cannot carry a credential.
    An inline hooks object validates as the event map (thirty-three events, five handler types
    with their required fields, after the Claude Code settings schema of 2026-10-05) and an inline
    mcpServers object as a map of server configs (stdio requires command; http, sse, ws
    and streamable-http require url). A value in a hook header, an MCP env or an MCP headers
    that matches one of seven credential shapes is refused; a marketplace entry's headers refuses
    an authorization key in any case; a contributed provider's baseUrl is https:// or
    http:// to loopback only. Nine invalid fixtures, one valid fixture with every admitted inline
    form; Anthropic's bundled-plugin manifest and the reference deployment's own manifests validate
    unchanged.
  • The lock follows local $refs (lock format 4). A property re-pointed from one definition to a
    stricter one used to change only its ref string, which the guard never compared, so the typed
    hook and MCP shapes above would have landed unseen; and a value that became a $ref lost its
    recorded type and read as TYPE_CHANGED. The referenced definition is now digested at the
    referring path, with ref recorded beside it; a cycle stops at its second visit.
  • Evidence hygiene (SPEC §6.2). audit-event.event_type is two or more lower-case dotted segments,
    with the reference writer's first segments reserved and a vendor name for anyone else; summary
    and reasoning refuse any control character; a details string value is at most 200 characters;
    actor.runtime_agent (optional, the closed vocabulary) is added while actor.runtime stays open,
    because the real journal spells it eleven ways; schema_version is major.minor on every schema
    (the lease schemas widen from the literal 1.0); provenance.spec is a slug; a CDI assessment has
    exactly six dimensions, each id once, with integer scores; a sealed config path is dotted lower-case
    and the runner checks it names a field the fixture carries. Ten invalid fixtures, one valid. Every
    real audit record, signal, provenance record and assessment in the reference deployment validates.
  • allow.tool_args (SPEC §4.4), a per-tool argument-schema declaration marked x-stability: development: an issuer MAY omit it from the grant and MUST NOT treat it as authority, because
    the reference gate does not yet evaluate argument schemas and a rule without an enforcing gate is
    a claim the corpus cannot test. The vector tool-args-dropped shows the reference dropping it.
    The stability marker is a $comment (draft-07 defines it): Bowtie showed Ajv's strict mode in
    another harness refusing a custom x-stability keyword, and a schema only this repository can
    compile is not portable. Python's re likewise rejected \p{Cc}, so the control-character
    class is written as literal characters, which every engine reads the same way.
  • Every normative clause of the SPEC has a named test (conformance/traceability.json,
    checked by npm test). conformance/spec-clauses.mjs extracts the 87 clauses with stable ids
    and a drift key; each is mapped to the fixtures, vectors, checks or rules that test it, or
    excluded with a reason (16 are: runtime behaviour, SHOULDs, definitions). Seven fixtures were
    added where a clause had nothing to point at: a manifest with a bare-major schema_version, a
    runtime_agent outside the vocabulary, a model without family, an allow or deny missing a
    list, an attestation with an unknown issuer, and a valid home-relative deny path.
  • The corpus runs under six other validators. conformance/suite/draft7/ is the corpus in the official
    JSON-Schema-Test-Suite format (one file per schema, every fixture a test, written by
    npm run lock and checked current by npm test), and CI runs it through Bowtie against
    go-jsonschema, rust-jsonschema, python-jsonschema, java-json-schema,
    dotnet-jsonschema-net and js-ajv, failing on any disagreement. A case is kept under 60 KB as one line, chunking a schema's tests across cases where needed, because a harness that reads a case as a line (the Go one) errors above 64 KiB. A second job runs
    Sourcemeta's jsonschema metaschema and lint (six style rules excluded by name, each with
    its reason in the workflow). Two orphan componentSource definitions the typed hook and MCP
    shapes had left behind are removed, and the marketplace's empty relevance.signals schema
    gained a description, both found by that lint.
  • Every property says what it promises. All 664 declared properties carry a $comment of
    stability: stable or stability: development (only allow.tool_args is development), with
    ; deprecated: <replacement> for retiring a field. conformance/metaschema.json holds that and
    the other conventions (dialect, $id, title, description, no format) and npm test validates
    every schema against it. Lock format 5 records stability, deprecated and the names an
    allOf if/then makes required (the lease record's per-event requirements were invisible to
    earlier formats); the guard reports STABILITY_LOWERED and CONDITIONAL_REQUIRED_ADDED as
    breaking and treats deprecation as additive. Two new guard scenarios.
  • Governance written down. GOVERNANCE.md: one maintainer (@datajace, the sole CODEOWNER,
    stated rather than padded), a proposal under docs/proposals/ before any semantic change, how a
    change lands and how a release is cut. A repository-owned DCO check refuses a pull request with
    an unsigned commit (the third-party app was not used because a suspended app's check disappears
    silently). Issue templates for a defect and a proposal, a pull-request template with the checks
    the automation cannot see, Dependabot for npm and Actions weekly, and the proposal template with
    Backw...
Read more

1.1.0

Choose a tag to compare

@github-actions github-actions released this 05 Oct 18:18
c04f53c

The first release after the review of 4 October 2026 (CDF spec contract-fix-batch-one-4,
DR-182). Five of the review's ten improvements, in the order the guard first so every tightening
that follows is classified and allow-listed by name. Additive within 1.x by the contract's own
rule, mechanically checked
: against 1.0.2 the guard reports 46 allow-listed tightenings, each
with the fixture or real-data count that proves no conformant writer ever produced what it now
refuses, and every real journal in the reference deployment (33,557 audit records, 5,652 signals,
4,535 provenance lines) validates with zero rejections. No existing hash or signature changes.

Added

  • The additive-only guard sees tightenings (schemas.lock.json is now lock format 3). The lock
    records, per property path, pattern, the allOf[].not.pattern set, minLength, maxLength,
    minimum, maximum, additionalProperties and the number of anyOf branches, and per schema
    whether its root is open or closed; enum values keep their JSON type instead of being
    stringified. check-additive.mjs reports named findings (PATTERN_TIGHTENED,
    BOUND_TIGHTENED, CONTENT_MODEL_CLOSED, UNION_CHANGED beside the four it already knew) and
    says plainly when a baseline predates the format and those four cannot be compared. A
    --baseline-ref baseline is digested from the schemas as they were at that ref with the current
    generator, so two lock formats are never compared; the committed lock stays the drift record for
    its own commit. Format 3 records a union's branch types and patterns on the parent entry, and
    propertyNames refusals, both of which format 2 lost.

    Until now a pattern could be tightened inside 1.x and the guard would print "additive only",
    which is the class of change the 4 October 2026 review found it blind to. Nothing in the
    schemas changed in this entry; the guard learned to see.

  • compat-allowlist.json: the one way a tightening passes within a major. Each entry names
    the finding, the schema, the path, a reason, a date and an existing evidence file; the guard
    refuses an entry with no evidence and refuses a change that drops an entry the baseline had.
    Documented in CONTRIBUTING.

  • The guard's own tests run in npm test (conformance/guard-tests.mjs): 27 scenarios in
    which every finding is seen to fire, every additive change is seen to pass, and the allow-list is
    seen to refuse an entry without evidence.

  • Three checks the contract's own CI now holds (conformance/schema-checks.mjs, in npm test):
    every schema compiles under Ajv strict mode; the granted manifest inlined in
    agent-lease.schema.json is byte-for-byte the manifest schema dereferenced (this check used to
    live only in the consuming harness, so the contract could not fail on its own drift); and
    package.json's cdfContract.schemaSetVersion matches the package version.

  • A regex-portability job. tooling/regex-portability compiles every pattern in every schema
    with Go's regexp (RE2: no lookahead, no backreferences), because Go validators use it
    unconditionally and a pattern RE2 rejects is a schema set a Go implementation cannot load. The
    job is expected to fail until the path patterns are rewritten without lookahead in the next
    change, and is made a required check then.

Added

  • Narrowing vectors (conformance/narrowing-vectors.json). SPEC §9 called narrowing "the heart
    of the specification" and left it to each implementation's own tests. The corpus now carries
    declared-and-parent pairs with the granted manifest or the refusal codes narrowing must produce:
    every row of the §5 table, every containment row of §5.1 (including src/*.ts not containing
    src/a.ts), every refusal of §5.2, unknown accepted for external and refused for native, an
    escaping path refusing the whole manifest, and a root manifest narrowed against a root policy.
    The vectors were generated from the reference implementation's narrowManifest and committed. An
    adapter supplies narrow(declared, parent) and the runner compares granted manifests by canonical
    bytes and refusal sets exactly; one that does not is reported as not checked. Every adapter has
    the vectors checked for shape. §5.2's six conditions gain stable identifiers R1 to R6.

  • A published test key, verifiable signed fixtures and signature vectors
    (conformance/test-key.txt, conformance/signature-vectors.json). The lease fixtures carried
    cccc… and dddd… for declared_hash and signature, so no verifier could be tested against the
    corpus. They are re-signed under a public test key (which a verifier must refuse outside a
    conformance run), a root lease fixture is added, and an adapter offering hash and verify is
    checked for matching declared_hash, acceptance, and refusal when one byte of the signature or
    of the granted manifest changes. The reference adapter implements the reference HMAC-SHA256.

  • Every invalid fixture carries an .expect.json naming the instance path the rejection must be
    reported at, and the runner checks it when the adapter reports its errors. Adding them found two
    fixtures (cdi-signal.bad-outcome, cdi-signal.unknown-event) that this release's workspace_id
    shape had started rejecting first for the wrong reason; both now carry a digest-shaped
    workspace_id so they fail only for the reason their .reason states. The unknown-field round
    trip now plants the field inside the first nested object as well as at the top level.

  • Every key the Claude Code plugin and marketplace references document (read 2026-10-05) is
    modelled: $schema, icon, documentationUrl, supportUrl, privacyPolicyUrl,
    termsOfServiceUrl, dependencies (string, name@marketplace or object), settings,
    userConfig (strict options: type, title, description required; required, default,
    options, multiple, sensitive, min, max), types, channels (strict), commands as an
    object map of source-or-content entries, hooks/mcpServers/lspServers as path, inline or a
    mixed array (with .mcpb, .dxt and https:// bundles), strict lspServers entries with
    command and extensionToLanguage required, outputStyles, workflows, top-level themes
    (deprecated, still loaded) and experimental (themes, monitors as strict entries, evals);
    marketplace forceRemoveDeletedPlugins, entry relevance and dependencies and every
    manifest field an entry may carry; command source timeout (1 to 600) and mode (copy or
    link); archive sha256 in either case; and the if/then rule that headersHelper requires
    "strict": false. Names follow Claude Code's rule (letters, digits, ., _, -, leading
    alphanumeric) instead of kebab-case only. Where Claude Code's object is strict the contract's is
    too, because honouring a key "with the same meaning" there means refusing an unknown one.

    Two fixtures carry the evidence: the manifest reference's own example manifest, and Anthropic's
    marketplace for its bundled plugins (anthropics/claude-code at a pinned commit, author emails
    removed). Four invalid fixtures pin the strict shapes. The local source form and plugin-level
    category stay as CDF extensions, named as such in the schema descriptions and, in the next
    change, the README.

Changed

  • The README no longer claims the plugin schemas contain every key Claude Code has. The
    sentence was true at 1.0.0 and stopped being true as Claude Code grew; a standing superlative
    about a moving target is the kind of sentence this contract exists to refuse. The README now says
    what is modelled and as of which date, names the two CDF extensions (cdf; the local source
    form; plugin-level category) so nobody mistakes them for Claude Code's, and the old wording is
    a banned claim in the reference implementation's documentation gate.

  • Canonical bytes are declared to be RFC 8785. SPEC §7 now says normatively that canonical bytes
    are the RFC 8785 (JSON Canonicalization Scheme) serialisation after removing absent members, with
    the field-by-field rules kept as an informative restatement. The restatement was already RFC 8785
    (the reference canonicaliser passes RFC 8785's own vectors unchanged), so no existing hash or
    signature changes; what changes is that an implementer in Go, Java, Python, Rust or .NET can use
    an existing JCS library and check it against the corpus, which now carries RFC 8785's six
    reference vectors (conformance/jcs/, vendored at a pinned commit under Apache-2.0 with its
    source recorded) beside the contract's nine. Every integer field is bounded at 2^53 − 1 because
    RFC 8785 presumes I-JSON; the ten BOUND_TIGHTENED findings are allow-listed with a fixture.

  • The privacy properties are enforced by shape, not prose. request_hash, prompt_hash,
    steering_hash and content_hash require a 64-character lower-case hex digest; details on the
    audit event and the CDI signal refuses by propertyNames any key whose segment is one of the
    reference writer's eleven words (authorization, content, file, password, path, payload,
    prompt, request, secret, token), split on non-alphanumerics and camelCase boundaries
    exactly as the writer does; summary is capped at 300 characters and reasoning at 500, the
    writer's own caps. Before this a raw prompt in request_hash validated, which SECURITY.md itself
    calls a security issue. Each tightening is allow-listed with its fixture; the check against the
    reference deployment's journals (33,538 audit records, 5,634 signals, 4,535 provenance lines) rejects
    nothing.

  • workspace_id is widened, and its description corrected. It accepts a SHA-256 digest or a
    lower-case UUID, because the collector writes a per-checkout UUID when the workspace has no git
    remote and 4,910 of the ...

Read more