GitHub checks architecture
Module: packages/orchestrator/src/reporting/
This document describes the enriched GitHub Check run system that provides real-time step progress, failure details with log context, and source location annotations.
Overview
Section titled “Overview”KiCI creates GitHub Check runs at trigger match time and updates them throughout job execution. Each workflow gets two types of check runs:
- Workflow check run (
kici/{workflowName}) — overall pass/fail for the workflow - Job check runs (
kici/{workflowName}/job/{jobName}) — per-job detail with step progress - Build check run (
kici/{workflowName}/setup) — optional, for dependency installation and compilation
Data flow
Section titled “Data flow”Agent Orchestrator GitHub Checks API | | | |--- step.status (running) ---->| | | |--- updateStepProgress() --------->| | | (immediate in_progress) | | | | |--- log.chunk (lines) -------->| | | |--- stepLogBuffer.addLines() ----->| | | (ANSI stripped, last 20) | | | | |--- step.status (success) ---->| | | |--- updateStepProgress() --------->| | | (debounced 5s) | | | | |--- step.status (failed) ----->| | | |--- updateStepProgress() --------->| | | (debounced 5s) | | | | |--- job.status (failed) ------>| | | |--- updateJobStatus() ----------->| | | cancel debounce timer | | | buildCheckRunSummary() | | | buildAnnotations() | | | (completed, failure) |Components
Section titled “Components”StepLogBuffer (step-log-buffer.ts)
Section titled “StepLogBuffer (step-log-buffer.ts)”Per-step ring buffer that retains the last 20 ANSI-stripped log lines for each step. Lines are keyed by {runId}:{jobId}:{stepIndex} composite key. Used by the summary builder to include log context in failure details.
- Input:
addLines(key, rawLines)— strips ANSI codes, evicts oldest lines when buffer exceeds 20 - Output:
getLastLines(key)— returns{ lines, totalCount }for truncation indicators - Cleanup:
cleanup(runId)— removes all entries for a completed run
CheckRunReporter (check-run-reporter.ts)
Section titled “CheckRunReporter (check-run-reporter.ts)”Central coordinator for all GitHub Checks API interactions. Manages:
- Check run creation (queued) at trigger match time
- Live step progress (in_progress) during execution with debouncing
- Completion updates (completed) with enriched summaries and annotations
- Cleanup of in-memory state when runs are pruned
Key methods:
| Method | When Called | GitHub Status |
|---|---|---|
setPending() | Trigger match | queued |
updateStepProgress() | Step starts/completes | in_progress |
updateJobStatus() | Job reaches terminal state | completed |
updateWorkflowStatus() | All jobs complete | completed |
cleanupRun() | Run pruned from memory | (cleanup only) |
Summary/annotation builders (check-run-summary.ts)
Section titled “Summary/annotation builders (check-run-summary.ts)”Pure functions with no side effects:
-
buildCheckRunSummary()— Produces markdown with step table, failure details, log context, and trace footer. Uses progressive truncation (20 -> 10 -> 5 -> 0 log lines) to stay under the 65535 byte GitHub API limit. -
buildAnnotations()— Creates GitHub-compatible annotation objects linking failures to source locations in.kici/workflows/*.ts. Capped at 50 per API request. -
buildProgressText()— Produces checklist-style live progress with emoji prefixes for running/completed/pending states.
SourceLocationStore (app.ts)
Section titled “SourceLocationStore (app.ts)”In-memory cache mapping workflowName:jobName to source location arrays (file, line, column). Populated during webhook processing when lock files contain sourceLocation data on steps. Used by CheckRunReporter to generate annotations.
Debounce strategy
Section titled “Debounce strategy”Live step progress updates are debounced to prevent GitHub API rate limiting:
- First
runningstep: Immediatein_progressupdate (transitions check run fromqueued) - Subsequent updates: Scheduled with 5-second debounce timer. If a timer is already pending, no new timer is created — the pending timer picks up the latest state when it fires.
- Completion: Cancels any pending debounce timer immediately. Completion always takes priority over progress updates to prevent race conditions.
This results in a maximum of 1 API call per 5 seconds per job check run during execution, plus the initial transition and final completion.
Source location flow
Section titled “Source location flow”Step source locations enable GitHub Check annotations that link failures directly to the step() call in the workflow source file (.kici/workflows/*.ts):
- SDK:
step()factory captures call-site file, line, and column viaError.captureStackTrace - Compiler: Writes
sourceLocationto lock fileLockStepwith git-root-relative paths - Orchestrator: Extracts source locations from lock file during webhook processing, stores in
SourceLocationStore - Annotations: On job failure,
buildAnnotations()creates annotation objects with file paths and line numbers
Users must recompile their workflows (schema version 2+) to get source location data in their lock files.
Details URL contract
Section titled “Details URL contract”Every check run carries a details_url pointing at a dashboard route the reader can click straight from the GitHub UI. The URL uses an opaque public alias for the org, not the canonical internal org identifier:
https://<dashboard-host>/r/orgs/oal_<12-char>/runs/<run-uuid>oal_<12-char>is a server-generated random alias (16 chars total, ~71 bits of entropy) stored alongside every org. It is never derived from the org’s display name, and never the same string as the canonical org ID, which carries a differentorg_prefix and is only used inside the authenticated system.- The
/r/orgs/:alias/*resolver authenticates the visitor, verifies org membership, and redirects to the canonical dashboard run-detail page. Unauthenticated visitors hit the OIDC login flow and land at the run detail after sign-in. Unknown aliases and non-members get the same uniform 404 — the resolver does not double as an org existence oracle. - The orchestrator caches its org’s public alias on connect and emits the alias-bearing
details_urlon everychecks.create()andchecks.update(). If the orchestrator was deployed without a dashboard URL configured (independent mode without operator config),details_urlis omitted and the GitHub check still works without a “Details” link.
The split means a public repository can host a KiCI workflow without leaking the org’s canonical identifier to anyone reading the repo’s check runs.
Summary format
Section titled “Summary format”Failed job
Section titled “Failed job”**Job 'ci/test' failed** (2/3 steps passed)
| Step | Status | Duration || ------------ | ----------------- | -------- || Install deps | checkmark success | 1.2s || Build | checkmark success | 3.4s || Run tests | cross failed | 5.6s |
### cross Run tests
**Error:** Process exited with code 1
... (showing last 20 of 142 lines)\`\`\`FAIL src/auth.test.tsExpected: trueReceived: false\`\`\`
Exit code: 1
Trace: abc-123 | Run: def-456Successful job
Section titled “Successful job”**Job 'ci/test' passed** (3/3 steps passed)
| Step | Status | Duration || ------------ | ----------------- | -------- || Install deps | checkmark success | 1.2s || Build | checkmark success | 3.4s || Run tests | checkmark success | 5.6s |
**Total duration:** 10.2s
Trace: abc-123 | Run: def-456Live progress (in_progress)
Section titled “Live progress (in_progress)”checkmark Install deps (1.2s)checkmark Build (3.4s)hourglass Run tests...circle Deploy
Trace: abc-123 | Run: def-456Size limits
Section titled “Size limits”- Summary: 65535 bytes (GitHub API limit). Progressive truncation ensures compliance.
- Annotations: 50 per API request. If more annotations exist, the remaining count is mentioned in the summary.
Cleanup
Section titled “Cleanup”When the ExecutionTracker prunes a completed run from memory (after 5 minutes), the onRunPruned callback triggers:
StepLogBuffer.cleanup(runId)— removes all log entries for the runCheckRunReporter.cleanupRun(runId)— removes step progress entries and cancels pending debounce timers
This prevents memory leaks from accumulating log lines and progress state for completed runs.