Skip to content

ci: chant run results as a status source for ci tick, with signed run statements (#4044) - #4062

Merged
lex00 merged 1 commit into
mainfrom
feat/4044-run-status-source
Oct 17, 2026
Merged

lex00 merged 1 commit into
mainfrom
feat/4044-run-status-source

Conversation

@lex00

@lex00 lex00 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Closes #4044

ci tick can take chant's own Op runs as its status source, counting only runs whose record a runner key signed. This gives GitLab, Forgejo and any forge without check runs green and revoked tags (the #4021 decision).

  • Source: ci.green.source: "checks" | "runs" in the declaration, or --source checks|runs on ci tick and ci list (the flag wins). checks, GitHub check runs, stays the default. With runs the tick needs no forge token.
  • Phases: with runs, a phase's patterns match Op names. A run counts for the commit in its record's gitSha; the latest run of each Op on a commit wins by end time. ok passes, fail fails, and a run stopped at a gate or decision point leaves the phase undecided.
  • Signing a run: chant run <op> --key <pem> or CHANT_RUNNER_KEY signs the record as the run settles, with predicate https://intentius.io/chant/op-run/v1 over the commit, id, Op, env, status and a SHA-256 of the record. The key must be under runners in .chant/trust.json at HEAD with the runner role. A bad --key stops the run before it starts; a bad CHANT_RUNNER_KEY warns and records the run unsigned. Signing code loads only when a key is given, so level-0 projects still load no workspace code.
  • The tick checks each run against trust.json at the branch tip. A run without a valid statement is ignored and reported, and neither greens nor revokes. With runs it fetches chant/lifecycle first (never forced, stopping if diverged), and always needs a runner key except under --dry-run. Its tags are signed as in trust: signed green tags and run statements, a role for who may cut a release, signed release records #4031, with source: "runs" and each run's opRun (id, env, signer, statement).
  • ci list --source runs judges undecided commits from the local ledger, refuses --forge, and adds opRun and ignoredRuns.
  • cli/ci.mdx has a "Chant runs as the status source" section; the --forge gitlab|forgejo refusal now points at --source runs.

Level-0 output changes

On the exceptions page and #2525: chant run <op> given a key adds statement to its ledger record and --json; a bad CHANT_RUNNER_KEY warns; help gains run --key and --source; the --forge gitlab|forgejo refusal text changed; ci list --json gains opRun and ignoredRuns under runs. No golden moved.

Acceptance

  • A test tags a commit from a signed local Op run (ci-green-runs.test.ts "a commit is tagged green from signed local Op runs, and the tag names and carries them": real Ops through runOpLocally, temp repo, throwaway key, no forge; it verifies the tag and run statements and that a second tick changes nothing).
  • A test refuses an unsigned run ("an unsigned run is refused: it neither greens nor revokes a commit, and is reported"). The file also covers a tampered record, an unlisted key, a signed failing run that revokes, and --source precedence.

Checks

  • npx tsx scripts/typecheck.ts: no regressions (26 baselined)
  • npx tsx scripts/check-lexicons.ts: no untracked tier-1 failures
  • ci-green tests, read-contract, level-0 goldens, boundary roster, main, local executor, run ledger: 336 passed with GITHUB_SHA, GITHUB_RUN_ID and CI set, after rebasing on main
  • Full vitest: only the 3 known local op/factory.e2e.test.ts failures
  • lint-docs-sentences (no regressions), lint-prompts, check-docs-diataxis, ./scripts/build-docs.sh: pass
  • The real repository's chant/lifecycle (4275740), its 100 ci/* tags and .git/chant/ were unchanged by the test runs.

Limits: the tick reads the run ledger of the directory it runs in, not other members' _members/<m>/ runs; ci workflow still writes only a GitHub Actions file; readers verify a tag's own signature and don't re-verify the run statements inside it.

…a runner key (#4044)

ci.green.source (or --source) runs reads Op runs from the run ledger on
chant/lifecycle in place of forge check runs. A phase's patterns match Op
names, and a run counts for its gitSha only when its record carries an
op-run/v1 statement signed by a runner key holding the runner role at base.
chant run <op> --key or CHANT_RUNNER_KEY signs the record as it settles.
Tags made from runs are signed and name each run's id, env, signer and
statement. GitLab and Forgejo are covered through chant runs. (#4044)
@lex00
lex00 force-pushed the feat/4044-run-status-source branch from 7d681ba to bc96748 Compare October 17, 2026 04:12
@lex00
lex00 merged commit ecf09cd into main Oct 17, 2026
11 checks passed
@lex00
lex00 deleted the feat/4044-run-status-source branch October 17, 2026 04:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: chant run results as a status source for ci tick, with signed statements

1 participant