Contextfulv0.23.0

Docs / Specification

contract assurance · 80-formal.md · 81-engineering.md · 82-consumer.md · 83-gate.md · guide · card

The gate and the release tag

How contextful-ci checks a change and cuts a release: typed subcommands, the gate’s stages in run order, its parts, folds and shards, its build jobs and footprint budgets, the target ledger the evaluate stage reads, and the signed release tag with the local packaging that follows it. Each operation keeps its assurance clause ids.

automate

Typed subcommands over compiled binaries, and the boundary shell keeps.

gate

Stage order, secrets of record, the crate-graph, row-token, egress and dependency rules, the formal stage, build jobs, free disk, footprint budgets and surface checks.

affected parts

stages --affected maps a diff to the parts it reaches.

  • affected-partstestcontextful-ci stages --parts --base <rev> --affected lists, in run order, the parts of the selected tier the diff from the merge base of <rev> and HEAD reaches, the required tier always including preflight.
    WhyA-assurance#pull-request-runs
  • affected-everytestA change to Cargo.toml, Cargo.lock, a toolchain file, anything under crates/, tools/ or .github/, or any path no other affected clause maps reaches every part of either tier.
    WhyA-assurance#pull-request-runs
  • affected-spectestA change under spec/ or references/ reaches test-first validation, one under spec/ the foundation workspace part, and each clause whose statement or spec/pins.toml entry changed the parts running its pinned test or theorem.
    WhyA-assurance#pull-request-runs
  • affected-spec-inputstestA change to spec/01-topology.md or spec/capabilities.toml reaches crate-graph, to spec/adr/A-assurance.md the formal stage, and to spec/terms/assurance.toml every budget part.
    Whybecause those stages read the file directly, apart from any clause pin
  • affected-removedtestA clause the merge base’s lock holds and HEAD’s lacks reaches the evaluate stage.
    Whybecause the evaluate stage refuses a ledger entry naming a clause the lock lacks
  • affected-unplacedtestAn unreadable spec lock or pins file, or a changed clause pinned outside every workspace package, reaches every required part.
    Whybecause a pin the listing cannot place runs on a part the listing cannot name
  • affected-surfacestestA change under apps/ or packages/ reaches the TypeScript surfaces, acceptance and crate-graph parts.
    WhyA-assurance#pull-request-runs
  • affected-formaltestA change under formal/ other than a lean-toolchain file reaches the formal and evaluate stages, the foundation, CLI and CLI differential workspace parts, and every feature part.
    WhyA-assurance#pull-request-runs

dependency rules

The crate-graph stage holds the dependency graph and runtime source to their rules.

  • crate-graphtestThe crate-graph stage holds run-path crates to reaching read-path crates through the three crossing crates alone; another edge raises CrateGraphViolation, naming both crates.
    WhyP5
  • locked-resolvetestThe crate-graph stage resolves every graph --locked against the committed Cargo.lock, so a lock behind its manifests, or one rewritten before the stage starts, fails the stage before any rule runs; the stage never rewrites it.
    Whybecause a rule held over a graph the lock file does not record passes a build that resolves another
  • row-tokentestEvery row-returning function of the store crate takes the enforcement token type, whose constructor is private to the enforcement module; a row path outside a registered relation raises EnforceUnmediatedPath, naming the function.
    WhyP5
  • dependency-denytestThe crate-graph stage runs pinned cargo-deny check bans over each profile’s resolved graph, denying every crate a profile-wide dependency refusal names; a hit raises that refusal’s error, naming the profile and the path.
    WhyA-assurance#profile-graphs-answer
  • deny-profile-graphtestA profile’s graph is the binary resolved with default features off and the profile’s feature on, across every target, excluding development dependencies; the stage prints each profile the binary declares no feature for.
    WhyA-assurance#profile-graphs-answer
  • deny-listtestdeny.toml holds one [bans] entry per crate or feature a profile-wide dependency refusal names, its reason that refusal’s clause id; an entry missing or extra raises DenyListDrift, naming it.
    Whybecause a refusal whose crate the list omits passes every profile graph linking it
  • unconfigured-egresstestAn outbound call reachable under the default configuration — usage ping, license check, update probe — raises UnconfiguredEgress in the crate-graph stage, naming the call site.
    Whybecause some deployments run with no external reach, and a call nobody configured moves data outside every grant, zone and audit record
  • interpolated-claimtestA source lint over the runtime crates finding a subject claim formatted into SQL text raises EnforceInterpolatedSubjectClaim, naming the file and line.
    WhyP1

footprint

Build resources and each profile’s artifact size stay within their bounds.

unsettled: Which workload, cadence and drift bound does the idle-resident soak run under, given that a multi-day soak fits no per-change gate? owner: build affects: assurance.gate

unsettled: Which cache hit rate holds test-first and workspace under the per-stage wall clock once the Cargo.lock-keyed build cache serves the gate container (issue 31)? owner: build affects: assurance.gate

measure

The target ledger: each tracked target, the clause it serves, how it is measured and its record per commit.

Scenarios

release

The release tag’s version, the refusals guarding it, the signed annotated tag contextful-ci tag creates, and the local packaging the operator runs from it.

  • versiontesttestA release tag is v0.<minor>.<patch>: <minor> counts the milestones computing closed in the tagged commit’s spec/status.md plus each store format major and each manifest format past 1, and <patch> counts the existing tags v0.<minor>.*.
    WhyA-assurance#store-format-major
  • store-formattestA commit’s store format is the FormatVersion::CURRENT constant in its crates/contextful-core/src/store/lay_out.rs, and 1.0 where that constant is absent; a constant that does not parse raises TagStoreFormatUnread.
    WhyA-assurance#store-format-major
  • manifest-formattestA commit’s manifest format is the MANIFEST_FORMAT constant in its crates/contextful-core/src/store/sync.rs, and 1 where that constant is absent; a constant that does not parse as an integer raises TagManifestFormatUnread.
    WhyA-assurance#store-format-major
  • annotatedtestcontextful-ci tag creates a signed annotated tag on HEAD naming the closed milestones once every refusal of this operation clears, and never moves or replaces an existing tag.
    WhyA-assurance#version-tag-names
  • dirty-treetestA working tree or index differing from HEAD raises TagTreeDirty.
    Whybecause the gate then judges files the tagged commit does not hold
  • off-branchtestA HEAD unreachable from the default branch, origin/HEAD unless --branch names another, raises TagOffDefaultBranch.
    WhyA-assurance#version-tag-names
  • version-regressedtestA computed version below the highest existing v<major>.<minor>.<patch> tag raises TagVersionRegressed, naming both.
    WhyA-assurance#store-format-major
  • format-break-patchtesttestA computed <minor> equal to the highest existing tag’s, while HEAD’s store format major or manifest format differs from that tag’s, raises TagFormatBreakPatch, naming both formats.
    WhyA-assurance#store-format-major
  • format-notestesttestA tag whose store format major exceeds the highest existing tag’s carries the message line Store format <old> → <new>: upgrade every reader, then every writer together., and one whose manifest format exceeds it the same line opening Manifest format.
    Whybecause an older reader refuses a manifest of the newer major, and an older writer refuses the table once a newer writer commits
  • workspace-versiontestA [workspace.package] version in HEAD’s Cargo.toml that is absent or differs from the computed version raises TagWorkspaceVersionMismatch, naming both.
    WhyA-assurance#version-tag-names
  • gate-failedtestOnce the other refusals clear, every gate stage runs against HEAD with HEAD~1 as base; a failing stage raises TagGateFailed, naming the stage’s refusal, and no tag is created.
    WhyA-assurance#version-tag-names
  • release-tag-versiontestcontextful-ci release over a HEAD carrying a v* tag, none of which equals v plus the [workspace.package] version, raises ReleaseTagVersionMismatch, naming both, before any build.
    Whybecause a tag cut without contextful-ci tag otherwise ships binaries reporting another version

Scenarios

deploy-apps

The one GitHub Actions workflow, its trigger, permission and credential shape, and contextful-ci deploy cloudflare, the command it and an operator run to ship one Cloudflare app.