Cikit Data

Overview

cikit data stores structured information associated with CI pipelines and logical runs.

Typical values include:

  • test counts and failures

  • coverage metrics

  • lint statistics

  • build duration

  • warning and error counts

  • memory usage

  • other scalar or structured run metrics

cikit data supports local persistence, optional remote synchronization, and historical timeline queries.

It is separate from the artifact system. cikit data stores structured values; the artifact registry stores metadata for generated files and directories.

Architecture

The data stack consists of three main layers:

CiRunEnvironment
       |
       +-- RunContext
               |
               +-- CikitData
                       |
                       +-- local JSON cache
                       |
                       +-- optional CikitDataClient
                               |
                               +-- remote cikit Data API

CikitData provides the high-level local and remote data operations.

RunContext binds a CikitData instance to one logical run and one pipeline id.

CiRunEnvironment uses that run context and exposes the most common operations through the active CI environment.

Access Modes

CikitData supports three access modes.

Cache-only Mode

Only a local cache directory is configured:

from pathlib import Path

from cikit.data import build_cikit_data

data = build_cikit_data(
    project="my_project",
    cache_dir=Path("public/cikit_data"),
)

Run updates are written locally. Operations requiring historical server data, such as timeline queries, are unavailable.

Remote-only Mode

Only a remote API is configured:

from cikit.data import build_cikit_data

data = build_cikit_data(
    project="my_project",
    base_url="https://example.com/api",
    token="secret",
)

Run updates are sent to the remote service without creating local run cache files.

Hybrid Mode

A local cache and remote API are both configured:

from pathlib import Path

from cikit.data import build_cikit_data

data = build_cikit_data(
    project="my_project",
    cache_dir=Path("public/cikit_data"),
    base_url="https://example.com/api",
    token="secret",
)

Updates are written to the local cache first and then synchronized with the remote API.

The standard cikit environment always configures CIKIT_DATA_DIR and therefore provides local cache access. Remote access is enabled additionally when CIKIT_DATA_BASE_URL is configured.

Environment Configuration

The cikit environment provides the following settings used by the data layer:

Variable

Purpose

PROJECT_NAME

Logical project name used for cikit data addressing.

CIKIT_DATA_DIR

Local directory for cached run data and related local metadata.

CIKIT_DATA_BASE_URL

Optional base URL of the remote cikit Data API.

CIKIT_DATA_API_TOKEN

Optional authentication token for the remote API.

By default:

CIKIT_DATA_DIR=${PUBLIC_DIR}/cikit_data

CIKIT_DATA_BASE_URL and CIKIT_DATA_API_TOKEN are optional.

Project Addressing

Every remote request is associated with one project key. The key is derived from the logical project name and the optional tool.cikit.data_scope value from pyproject.toml.

Without a configured scope, the effective key is simply:

PROJECT_NAME

For example:

PROJECT_NAME=cikit
-> cikit

A scope can be configured as project metadata:

[project]
name = "cikit"

[tool.cikit]
data_scope = "svesch"

The resulting remote project key is then:

svesch/cikit

The same key is used during local execution and in every CI provider. Provider metadata does not participate in cikit data addressing.

Logical Runs

Data inside a project is grouped by logical run name.

Typical run names are:

build
unittests
systemtests
coverage
ruff
pylint
docs

The run name identifies one independent set of values.

For example:

build:
    build_time
    warnings
    errors

coverage:
    coverage
    line_rate

pylint:
    score
    violations

The same run name is used for both local cache data and remote run data.

RunContext

RunContext binds three values together:

  • a configured CikitData instance

  • a pipeline id

  • a logical run name

Conceptually:

CikitData
   +
pipeline_id
   +
run_name
   |
   v
RunContext

This removes the need to repeatedly pass the run name and pipeline id to every data operation.

Example:

from cikit.data import RunContext

context = RunContext(
    cikit_data,
    pipeline_id="12345",
    run_name="build",
)

context.set("build_time", 4.2)
context.update({
    "warnings": 0,
    "errors": 0,
})

CiRunEnvironment Integration

CiRunEnvironment creates a RunContext automatically for its logical run.

The run context is available as:

env.run

The logical run name is exposed as:

env.name

Common operations are wrapped directly by the environment:

env.data_set("build_time", 4.2)

env.data_update({
    "warnings": 0,
    "errors": 0,
})

data = env.data

timeline = env.timeline(
    ["build_time", "warnings"],
    limit=20,
)

This is the normal API used by cikit macros and project CI commands.

Updating Run Data

A single value can be stored through the run environment:

env.data_set("coverage", 92.5)

Multiple values can be merged in one operation:

env.data_update({
    "tests": 120,
    "failures": 0,
    "errors": 0,
})

The behavior depends on the configured access mode.

Local Cache

When a local cache is configured, new values are merged into the existing JSON data for that run.

Existing keys not included in the update remain unchanged.

Remote Backend

When a remote client is configured, only the values supplied by the current update are sent to the server.

A remote update requires a pipeline id.

Hybrid Updates

In hybrid mode, the local cache is updated before the remote request is made.

This means the local update may already have been persisted if the subsequent remote synchronization fails.

Local Cache Format

Local run data is stored as one JSON file per logical run below CIKIT_DATA_DIR.

Example:

public/
`-- cikit_data/
    |-- build.json
    |-- coverage.json
    |-- pylint.json
    `-- unittests.json

A run file contains the merged values of that run.

Example build.json:

{
    "build_time": 4.2,
    "warnings": 0,
    "errors": 0
}

Repeated updates merge new values into the existing file.

Reading Current Run Data

The current run’s locally cached data is exposed by CiRunEnvironment:

data = env.data

Internally this uses RunContext.get_data().

get_data():

  • reads only the local cache

  • does not query the remote API

  • returns a shallow dictionary copy

  • returns an empty dictionary when the run file does not exist

  • requires a configured local cache directory

Reading a Latest Value

CikitData.latest() and RunContext.latest() resolve one value using a local-first strategy.

Example:

value = env.run.latest("build_time")

Resolution order:

  1. Read the local run cache when configured.

  2. Return the cached value when the requested key exists.

  3. Otherwise query the remote API when configured.

  4. Raise an error if neither source can provide the value.

Only the resolved value is returned, not the complete remote response.

Timeline Queries

Timeline queries provide historical values across multiple pipeline entries.

Unlike latest(), a timeline query always uses the remote API. The local cache contains only the current locally stored run state and is not used as a historical data source.

Through CiRunEnvironment:

from cikit.data import RefKey

timeline = env.timeline(
    ["build_time", "warnings"],
    ref_key=RefKey.INDEX_DESC,
    limit=20,
)

The result has a normalized structure:

{
    "refs": [-2, -1, 0],
    "series": {
        "build_time": [4.8, 4.5, 4.2],
        "warnings": [1, 2, 0],
    },
}

Each series contains one value for every reference. Timeline entries are returned from oldest to newest. With RefKey.INDEX_DESC, the newest entry is 0, the previous entry is -1, and so on.

Missing values are represented as None after decoding the remote JSON null value.

Remote key prefixes are removed before the high-level result is returned, so the keys in series match the run-local keys requested by the caller.

Timeline queries require remote access. Without a configured remote client, they raise a recoverable cikit error.

Reference Keys

RefKey selects which pipeline property is used as the timeline reference.

Available values are:

Reference

Value

RefKey.ID

id

RefKey.INDEX_DESC

index_desc

RefKey.TIMESTAMP

timestamp

RefKey.COMMIT_HASH

commit_hash

RefKey.SHORT_COMMIT_HASH

short_commit_hash

RefKey.PIPELINE_ID

pipeline_id

The low-level CikitData and RunContext timeline APIs default to RefKey.TIMESTAMP.

CiRunEnvironment.timeline() defaults to RefKey.INDEX_DESC, which is well suited to compact reports and charts where the newest entry is represented relative to previous entries.

Timeline chart generation is described in Timeline Charts.

Remote Key Format

The remote API stores run values below the runs namespace.

The high-level API converts a run and local key into:

runs.<run>.<key>

Examples:

runs.build.build_time
runs.coverage.coverage
runs.pylint.score

Callers using CikitData or RunContext normally work with the shorter run-local key:

env.run.latest("build_time")

The fully qualified key is an implementation detail of communication with the remote API.

Pipeline Entries

The remote backend groups run updates below pipeline entries.

A pipeline entry contains provider and revision context and is created through:

cikit_data.create(
    pipeline_id="12345",
    ci_provider="GITLAB",
    commit_hash="abcdef123456",
)

In cache-only mode, create() performs no remote operation and returns an empty dictionary.

Project CI initialization uses the active environment to provide the pipeline id, provider, and commit hash to this operation.

Low-Level Remote Client

CikitDataClient implements the HTTP communication used by CikitData.

The high-level API should normally be preferred, but the client defines the remote behavior.

It uses:

  • a shared requests.Session

  • retries for temporary server-side failures

  • three retry attempts

  • retry status codes 500, 502, 503, and 504

  • a connection timeout of 5 seconds

  • a read timeout of 30 seconds

The client exposes operations for:

  • pipeline creation

  • run updates

  • latest-value queries

  • timeline queries

Authentication is sent through the X-Auth-Token header when a token is configured.

Cikit Data and Artifacts

cikit data and artifacts are independent mechanisms.

cikit data stores structured values:

build_time = 4.2
warnings = 0
coverage = 92.5

The artifact registry stores metadata for persistent files and directories:

firmware.bin -> BUILD
build.log -> LOG
chart_build.svg -> TIMELINE

Both mechanisms are associated with logical runs, but they serve different purposes. Cached run data and artifact registry metadata are stored below CIKIT_DATA_DIR. The artifact files and directories themselves are stored below PUBLIC_DIR.

In particular:

  • cikit data does not store artifact metadata as run values

  • timeline data is historical cikit data, not a list of TIMELINE artifacts

  • TIMELINE artifacts are rendered SVG files registered through the artifact mechanism

  • reports may combine structured cikit data with registered artifacts

For artifact storage and lookup semantics, see Artifacts.

Error Behavior

Configuration and invalid data operations generally raise CiToolsError.

Remote synchronization failures are reported through cikit synchronization errors derived from the common cikit error handling.

Some unavailable operations are intentionally recoverable. For example:

  • timeline access without a remote client

  • reading cached run data without a configured cache directory

A hybrid update writes the local cache before contacting the remote API. Therefore a remote failure does not imply that the local update was rolled back.

See Also