Skip to content

1.2.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 06:05
· 27 commits to main since this release
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
    Backward compatibility and Security sections and a status lifecycle.
  • Every release frozen at its own URL, and an index. pages.yml now lays out /<version>/
    for every v1.* tag beside the /1.x/ alias, byte for byte as tagged and never rewritten, and
    runs on the tag push so the frozen copy appears with the release; release.yml checks it serves
    the tagged bytes (patiently, and reported rather than failed while the deploy is still landing).
    schemas/index.json, written by npm run lock and validated by npm test, lists every schema
    with file, $id, title, dialect and fileMatch; it is served beside the schemas. A
    SchemaStore catalogue entry is proposed for **/.cdf/config.yaml, pointing at the served
    config-core schema.

npm: https://www.npmjs.com/package/@cognitive-delivery/contract/v/1.2.0
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.0.
Schemas: https://cognitive-delivery.github.io/contract/1.x/