Repository navigation
Releases: Cognitive-Delivery/contract
Release list
1.2.1
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.jsononly, nameslease-record, and says what a type cannot.
It tripped overschemas/index.jsonand 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 (allOfwithif/then,propertyNames,pattern, the bounds), so a
reader of the types knows to validate with the schema as well. capabilities.tool_argson both plugin schemas: the leaseallowshape, property for property,
now thatallowcarriestool_args(development stability, as there).- The narrowing vectors are the reference's bytes.
tool-args-droppedwas 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 thediffthe 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
mainonly, so
the tag push that was meant to lay out/1.2.0/was refused at the deploy step;release.ymlnow
dispatchespages.ymlonmainafter 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
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'sreasonsare reserved codes (R<n>,runtime_error:<name>,vendor:<vendor>:<code>)
with the prose inreason;declared_hashandgranted_hashtie a record to the exact bytes
decided; arevokedrecord namesby(an ancestor lease orissuer) 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.1grantedrecord carries no
granted_hashand 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 andlocalhostincluded), 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 leaseallowshape. Eighteen invalid fixtures, one per refused form, and one valid
fixture carrying every admitted form.agent.nameis capped at 120 characters and described as
never a person's name;intent.purposeat 500.tooling/inline-granted-manifest.mjsrewrites the
inlined copy inagent-lease.schema.jsonfrom 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_atis afterissued_at" or "not its own parent", soconformance/lease-rules.mjs
does, the runner applies it to every valid lease and tofixtures/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 aruleshook, reported unchecked when absent.
issued_atandexpires_atadmit only UTC with milliseconds andZ, 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 andbudget.fan_out≤ 256. - An allow-list entry admits one tightening, not every later one at the same path. Every guard
finding now carriesafter, the exact value it admits (pattern=…,maximum=16,enum=[…]),
and an entry must name it. Found while loweringdepth's ceiling: batch one's entry for the
2^53maximumwould have covered it silently. Every existing entry gained itsafter; three
guard scenarios prove the match is exact. - Inline plugin hooks and MCP servers are typed, and a manifest cannot carry a credential.
An inlinehooksobject 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
mcpServersobject as a map of server configs (stdiorequirescommand;http,sse,ws
andstreamable-httprequireurl). A value in a hook header, an MCPenvor an MCPheaders
that matches one of seven credential shapes is refused; a marketplace entry'sheadersrefuses
anauthorizationkey in any case; a contributed provider'sbaseUrlishttps://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 itsrefstring, which the guard never compared, so the typed
hook and MCP shapes above would have landed unseen; and a value that became a$reflost its
recorded type and read asTYPE_CHANGED. The referenced definition is now digested at the
referring path, withrefrecorded beside it; a cycle stops at its second visit. - Evidence hygiene (SPEC §6.2).
audit-event.event_typeis two or more lower-case dotted segments,
with the reference writer's first segments reserved and a vendor name for anyone else;summary
andreasoningrefuse any control character; adetailsstring value is at most 200 characters;
actor.runtime_agent(optional, the closed vocabulary) is added whileactor.runtimestays open,
because the real journal spells it eleven ways;schema_versionismajor.minoron every schema
(the lease schemas widen from the literal1.0);provenance.specis 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 markedx-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 vectortool-args-droppedshows 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 customx-stabilitykeyword, and a schema only this repository can
compile is not portable. Python'srelikewise 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 bynpm test).conformance/spec-clauses.mjsextracts 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-majorschema_version, a
runtime_agentoutside the vocabulary, a model withoutfamily, anallowordenymissing 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 lockand checked current bynpm test), and CI runs it through Bowtie against
go-jsonschema,rust-jsonschema,python-jsonschema,java-json-schema,
dotnet-jsonschema-netandjs-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'sjsonschema metaschemaandlint(six style rules excluded by name, each with
its reason in the workflow). Two orphancomponentSourcedefinitions the typed hook and MCP
shapes had left behind are removed, and the marketplace's emptyrelevance.signalsschema
gained a description, both found by that lint. - Every property says what it promises. All 664 declared properties carry a
$commentof
stability: stableorstability: development(onlyallow.tool_argsis development), with
; deprecated: <replacement>for retiring a field.conformance/metaschema.jsonholds that and
the other conventions (dialect,$id, title, description, noformat) andnpm testvalidates
every schema against it. Lock format 5 recordsstability,deprecatedand the names an
allOfif/thenmakes required (the lease record's per-event requirements were invisible to
earlier formats); the guard reportsSTABILITY_LOWEREDandCONDITIONAL_REQUIRED_ADDEDas
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 underdocs/proposals/before any semantic change, how a
change lands and how a release is cut. A repository-ownedDCOcheck 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...
1.1.0
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.jsonis now lock format 3). The lock
records, per property path,pattern, theallOf[].not.patternset,minLength,maxLength,
minimum,maximum,additionalPropertiesand the number ofanyOfbranches, and per schema
whether its root is open or closed; enum values keep their JSON type instead of being
stringified.check-additive.mjsreports named findings (PATTERN_TIGHTENED,
BOUND_TIGHTENED,CONTENT_MODEL_CLOSED,UNION_CHANGEDbeside the four it already knew) and
says plainly when a baseline predates the format and those four cannot be compared. A
--baseline-refbaseline 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
propertyNamesrefusals, 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, innpm test):
every schema compiles under Ajv strict mode; the granted manifest inlined in
agent-lease.schema.jsonis 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'scdfContract.schemaSetVersionmatches the package version. -
A regex-portability job.
tooling/regex-portabilitycompiles everypatternin every schema
with Go'sregexp(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 (includingsrc/*.tsnot containing
src/a.ts), every refusal of §5.2,unknownaccepted forexternaland refused fornative, an
escaping path refusing the whole manifest, and a root manifest narrowed against a root policy.
The vectors were generated from the reference implementation'snarrowManifestand committed. An
adapter suppliesnarrow(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…anddddd…fordeclared_hashandsignature, 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 offeringhashandverifyis
checked for matchingdeclared_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.jsonnaming 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'sworkspace_id
shape had started rejecting first for the wrong reason; both now carry a digest-shaped
workspace_idso they fail only for the reason their.reasonstates. 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@marketplaceor object),settings,
userConfig(strict options:type,title,descriptionrequired;required,default,
options,multiple,sensitive,min,max),types,channels(strict),commandsas an
object map ofsource-or-contententries,hooks/mcpServers/lspServersas path, inline or a
mixed array (with.mcpb,.dxtandhttps://bundles), strictlspServersentries with
commandandextensionToLanguagerequired,outputStyles,workflows, top-levelthemes
(deprecated, still loaded) andexperimental(themes,monitorsas strict entries,evals);
marketplaceforceRemoveDeletedPlugins, entryrelevanceanddependenciesand every
manifest field an entry may carry;commandsourcetimeout(1 to 600) andmode(copyor
link);archivesha256in either case; and the if/then rule thatheadersHelperrequires
"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-codeat a pinned commit, author emails
removed). Four invalid fixtures pin the strict shapes. Thelocalsource form and plugin-level
categorystay 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; thelocalsource
form; plugin-levelcategory) 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. Everyintegerfield is bounded at 2^53 − 1 because
RFC 8785 presumes I-JSON; the tenBOUND_TIGHTENEDfindings are allow-listed with a fixture. -
The privacy properties are enforced by shape, not prose.
request_hash,prompt_hash,
steering_hashandcontent_hashrequire a 64-character lower-case hex digest;detailson the
audit event and the CDI signal refuses bypropertyNamesany 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;summaryis capped at 300 characters andreasoningat 500, the
writer's own caps. Before this a raw prompt inrequest_hashvalidated, 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_idis 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 ...