canon-kit

Spec discipline for agent-authored components: one canonical spec per component, deltas as short-lived amendment files, and a star topology across the prose surfaces — every fact has exactly one owning surface, and every other surface cites it rather than restating it. Gates hold the copy-shaped failure modes; the irreducibly semantic judgment stays a review tripwire, not a gate.

Why: when a coding agent authors the specs, design rationale gets re-derived under build pressure unless it is captured up front, and a parallel copy of any gated fact is an un-gateable second source that drifts silently. The remedy is a lifecycle (amendments authored up front, merged and deleted at build) plus gates over the mechanically-decidable copy failures — a doubled Definition-of-Done, a banned-heading code dump, a fenced block that verbatim-copies a source file, a glossary definition restated on another surface, a feature task with no amendment. See SPEC.md for the full contracts.

This file ships in the installer payload; what that payload withholds, and where this page’s SPEC.md links then resolve, is gate-sdk/SPEC.md §Consumer payload.

Install

Vendor the kit beside gate-sdk (required); the queue-facing gate reads the tag syntax queue-kit defines.

  1. Register the gates — add to your gates.list, each where its surface exists:

    check-amendment-queue        # needs a task queue
    check-amendment-update-target # every update target cites a delta the amendment defines
    check-amendment-retired-spelling # declared retired spellings survive only where the roster names
    check-spec-dod-singleton     # needs canonical specs
    check-spec-derivable-section # needs canonical specs
    check-spec-embedded-source   # needs canonical specs + tracked source
    check-surface-duplication    # needs a glossary (exits 2 without one)
    check-comment-tier           # classifies comments on shell/config sources
    check-spec-pointer           # resolves spec:/contract: pointer targets
    check-todo-task-liveness     # resolves TODO(task:) slugs against the queue
    check-docs-cmd               # invoked paths + kit knobs in docs resolve
    check-docs-restatement-parity # needs restating pages (their code tokens held to the README beside each)
    check-docs-page-repeat       # needs declared pages (no link target or long sentence stated twice on one)
    check-docs-page-length       # needs declared pages and a bound (no page past the word bound)
    check-citation-link          # needs declared pages (every section citation links its section)
    check-fence-command-head     # every shell-fence command starts with a word that can run
    check-fence-run              # every shell fence marked runnable runs and exits as declared
    check-fence-paste-unit       # needs declared pages (every code block is one paste)
    check-knob-citation          # knob values stated only in the owning SPEC
    check-knob-default-coupling  # source knob defaults agree with the owning SPEC
    check-manifest-count         # no bare cardinal over a governed collection
    check-measured-claim         # needs a measured-claim oracle (marked claims re-measured)
    check-unmarked-claim         # needs a claim-class roster (a declared class carries a marker)
    check-manifest-temporal      # no temporal narration in manifest prose
    check-pendency-contradiction  # no file calls one code span both pending and landed
    check-provenance-seam        # no publisher provenance in a kit SPEC or a declared seam surface
    check-tracking-claim         # a path's tracking status in prose agrees with git
    check-task-path-claim        # needs task lists (a ticked task names only paths that exist)
    check-task-label-resolution  # needs task lists and label families (a cited label resolves beside it)
    check-prose-enum             # partial enumeration of a governed set
    check-md-refs                # internal markdown links resolve
    check-md-unwrapped           # needs an unwrapped markdown set (no paragraph broken across lines)
    check-install-claim          # one declared primary install transport, led with everywhere
    check-payload-claim          # one declared payload-disclosure class, asserted nowhere else
    check-spec-fence-balance     # even fence counts, parsers never desync
    check-docs-link-convention   # needs a docs site (cite-downward links)
    check-prose-tells            # needs prose surfaces (mechanical AI-prose tells)
    check-prose-bounds           # needs governed prose (sentence, unit and repeated-phrase bounds)
    check-deprecation-task       # needs a deprecation-marker vocabulary
    

    They resolve through gate-sdk’s registry path (your gates dir first, then each kit’s checks/), and the pre-commit hook runs the precommit-tier ones their # graph: manifests trigger, read at commit. --install-hooks on the gate binary GATE_SDK_NATIVE_BIN names places both hooks in a clone once, and a gate registered later is reached with no re-install.

  2. Adopt the amendment lifecycle — copy templates/SPEC-amendment.md when a designed-but-unimplemented change needs a home. Name it after the feature (SPEC-<feature>.md), place it in the owning component’s directory (a governance ruling with no component lives at the repo root), and pair it with a queue entry tagged [spec: SPEC-<feature>.md]. Merge it into the canonical spec and delete it when the work completes.

  3. Optional — reshape the config: copy templates/canon-config.knobs into your gates dir and override the manifest set, the spec/amendment names, section set, the Definition-of-Done mode (exactly-one / at-most-one), the banned-heading set and density budget, the embedded-source language map, or the glossary surface list. Defaults are this repo’s layout.

Use

The lifecycle is prose discipline the gates enforce; there is no runtime tool. Author a spec so it (1) owns semantics, (2) names every contract and couples it to code by a gate, and (3) never copies a structural definition — reference the code instead. Author amendments up front; merge and delete them at build.

Test

Run this arm with the gate binary GATE_SDK_NATIVE_BIN names, where init places it. In PowerShell, spell the line ./scripts/checkwright-gates <arm> at the repository root, with .exe on native Windows:

gates="${GATE_SDK_NATIVE_BIN:-./scripts/checkwright-gates}"
"$gates" --run-gate-tests canon-kit/gate-tests canon-kit/checks

License

Licensed under Apache-2.0, with the text in LICENSE beside this file in an installed copy and at the repository root in the source tree.