Action contract

September 20, 2026 ยท View on GitHub

The action selects tasks and publishes outputs. Workflows own execution, secrets, runners and ordering. There is one source of task definitions: the required inline tasks input.

Compatibility and versions

The action follows Semantic Versioning. A major-version tag such as v0 floats to the latest stable release of that major version. A prerelease publishes its exact tag only and never moves a floating tag. Pin a release commit when an immutable reference is required. A major version boundary covers changes to the input or output contract, the report version, or the default value of mode.

The report version and canonical Jev model version evolve independently from the action version. Consumers that persist or analyze reports must validate the report version and upgrade their analyzer deliberately.

Inputs

GitHub passes strings. Validation and normalization precede Git or HTTP access, including manual PR resolution. Blank optional inputs use their defaults.

InputDefaultContract
tasksRequiredYAML table of task objects; no enclosing tasks: key
modeenforceenforce applies selection; shadow keeps every task
modeljev-1.13.0Canonical version expected in the response, jev-X.Y.Z
skip-below0.05Finite decimal in [0, 1]; strict exclusion threshold
api-base-urlhttps://api.typesafe.aiHTTPS Jev System One root
api-modelmodelProvider identifier, possibly an alias
api-keyEmptyProvider Bearer key; absent means no call and all tasks
allow-external-context'false'Explicit authorization to send diff and metadata
github-token${{ github.token }}GitHub token; ordinarily contents: read
tested-refmergemerge or head
pull-requestEmptyOpen PR number for workflow_dispatch; add pull-requests: read
force-all'false'All tasks, no Jev request
timeout-ms10000Integer from 1 to 2147483647; total evaluation deadline
max-diff-bytes65536Positive safe integer; complete UTF-8 diff limit

Boolean inputs accept only true and false; quote them in YAML. Integers use decimal integer syntax, without permissive suffix parsing. The threshold accepts decimal syntax, not NaN, infinity or suffixes. Zero is a valid threshold and prevents model-based exclusion.

There are no per-task providers, budgets or thresholds, no file path interpretation of tasks, and no configuration source precedence.

Tasks

tasks: |
  integration:
    description: Verifies HTTP authentication and PostgreSQL persistence.
    jobs:
      - workflow: .github/workflows/ci.yml
        job: integration
    context_files: [tests/integration/setup.ts]
    force_paths: [migrations/**]
  lint:
    description: Verifies TypeScript ESLint rules.
    always: true
FieldContract
descriptionRequired nonblank string describing verification scope
jobsOptional nonempty list of workflow/job references
context_filesOptional list of repository-relative paths
alwaysBoolean, default false; forces execution
force_pathsOptional list of positive globs; a match forces execution

A job reference has required workflow and optional job. Omitting job includes every job of that workflow. No other reference fields are accepted. Metadata includes job and step names, commands, action references and their declared inputs, relevant package scripts and explicitly requested context files. A description alone is complete; optional metadata enriches the same evaluation pipeline.

IDs match ^[A-Za-z_][A-Za-z0-9_-]{0,63}$ and are unique ignoring case. Standard output names and dangerous JavaScript property names are reserved by the schema. Job names such as plan and ci-contract are not reserved task IDs.

Unknown fields, duplicate YAML keys, YAML aliases, blank descriptions and incorrect types are rejected. A string is not a task object. Paths must be relative, without absolute paths, .. traversal or NUL characters. Globs are positive must-run rules using the action's existing minimatch semantics; absence of a match does not authorize skipping. This is not the full paths-filter syntax.

Missing or blank tasks fails. An explicit {} is valid and yields no selected tasks and no Jev requests.

Provider contract

The SDK appends /v1/systemone to api-base-url, retaining a path prefix. The endpoint must implement Jev System One; an OpenAI-style chat endpoint cannot be substituted.

HTTPS is required. Embedded credentials, query strings, fragments, spaces, control characters and backslashes are rejected before network access. api-model has 1โ€“128 ASCII characters from letters, digits, ., _, :, /, -, starting with a letter or digit. The provider response must identify the exact canonical model, even when an alias was requested.

There are no redirects, retries or automatic alternate providers. Evaluation uses at most three concurrent requests; each is bounded by ten seconds and the remaining total deadline. Explicit settings prevent SDK environment variables from changing the provider or enabling request/response body logging.

Events, provenance and selection

Automatic PR analysis uses verified immutable Git objects. tested-ref: merge tests the verified event merge commit; head uses the verified PR head and merge-base. Consumer jobs should check out tested-sha.

Task definitions are inputs of the executed workflow, not data reread from the base branch. Requested metadata is read at the PR base commit; manual PR evaluation uses the operator-selected workflow revision. The action cannot reconstruct a task removed from its inputs.

A workflow_dispatch with pull-request evaluates that open PR using the same pipeline. Other events keep every task without a Jev request. This includes push, schedule and merge groups; semantic selection on those events is not provided.

An optional task is excluded only when all required observations are complete and their scores are strictly below skip-below. Equality retains the task. always, matching task paths and workflow changes impose execution. Workflow needs remains the only execution dependency mechanism: keep prerequisites mandatory where necessary.

Forks, missing key or consent, and force-all bypass Jev and keep all tasks. Unusable diffs, deadlines and incompatible or incomplete responses retain tasks conservatively. A requested but unavailable metadata reference retains the affected task; omitting references deliberately is valid. Shadow mode keeps all effective outputs true while recording proposals.

Invalid inputs fail without a plan, even with force-all. Unexpected internal failures remain failures. Error diagnostics name a known field and fixed constraint, never supplied values or raw YAML/provider errors.

Outputs

All values are strings. JSON structures use stable task identifier order.

OutputValue
<task-id>Exact true or false, effective decision
runJSON object of task booleans
selectedJSON array of selected IDs
matrixJSON object {"include":[{"task":"unit"}]}
has-tasksExact true or false
statusplanned, bypassed or fallback
tested-shaImmutable tested commit
report-pathRunner-local JSON report path

Named and aggregate outputs agree. Shadow, bypass and fallback retain all declared tasks. For {}, run is {}, selected is [], matrix has an empty include, and has-tasks is false.

Use steps.select.outputs.unit == 'true' within a job, or forward it through job outputs for needs.selection.outputs.unit == 'true'. Check has-tasks before matrix expansion. Keep existing CI failure gates; the action does not make a skipped consumer job prove that planning succeeded. Static and matrix examples include advanced final gates.

Report v5

The report is persisted and validated before outputs are published. The strict schema is authoritative; the current analyzer accepts only v5.

GroupFields
Versionversion: 5
Commitsbase_sha, head_sha, tested_sha, tested_ref, diff_base_sha
Metadatametadata_sha, job_metadata
Definitionselection_hash, skip_below
Diffdiff_hash, diff_bytes, changed_path_count
Executionmode, status, durations_ms, usage
Modelmodel.requested, model.expected, model.returned
Decisionstasks
Observationsobservation, observation_error

metadata_sha is the reference revision for metadata even when no task requests it. selection_hash is SHA-256 of canonical JSON { model, skip_below, tasks } after normalization and defaults: recursively sorted object keys, preserved array order. YAML formatting does not change it.

Each task decision contains proposed_run, run and reasons. Scores live in observations by group, not in a synthetic global task probability. Observations include groups, requests, scores, errors, models, usages and durations. Reports omit descriptions, file bodies, diffs, keys and provider error bodies.

The summary leads with mode, status, actual decisions, shadow proposals and fallback reasons. Detailed observations are secondary; reports and artifacts are optional for normal use.

Release and development

Examples target v0.1.0; publication is separate from implementation. Pin a published release commit when immutable references are needed. This breaking API requires inline tasks; choose mode: shadow explicitly for observation.

Use Node.js 24, npm run build, npm run check and npm run eval:replay. Example consistency checks live in this repository's tests, not in scripts installed by consumers.