@kici-dev/compiler 0.1.27 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/dist/auth/headless-detect.d.ts +14 -5
  2. package/dist/auth/headless-detect.js +50 -9
  3. package/dist/cli.js +120 -11
  4. package/dist/commands/compile.js +28 -4
  5. package/dist/commands/diagnostics.js +14 -2
  6. package/dist/commands/doctor.d.ts +64 -0
  7. package/dist/commands/doctor.js +413 -0
  8. package/dist/commands/endpoints.js +6 -0
  9. package/dist/commands/fixture.js +13 -0
  10. package/dist/commands/hook.js +3 -2
  11. package/dist/commands/index.d.ts +8 -0
  12. package/dist/commands/index.js +5 -1
  13. package/dist/commands/init-host-os.d.ts +34 -0
  14. package/dist/commands/init-host-os.js +44 -0
  15. package/dist/commands/init.d.ts +4 -0
  16. package/dist/commands/init.js +159 -36
  17. package/dist/commands/local.d.ts +28 -2
  18. package/dist/commands/local.js +74 -5
  19. package/dist/commands/login.js +1 -1
  20. package/dist/commands/notifications.d.ts +61 -0
  21. package/dist/commands/notifications.js +274 -0
  22. package/dist/commands/preview.js +8 -1
  23. package/dist/commands/run-routed.js +10 -1
  24. package/dist/commands/run.js +41 -12
  25. package/dist/commands/runs/artifacts/download.d.ts +24 -0
  26. package/dist/commands/runs/artifacts/download.js +154 -0
  27. package/dist/commands/runs/artifacts/list.d.ts +5 -0
  28. package/dist/commands/runs/artifacts/list.js +47 -0
  29. package/dist/commands/runs/cancel.js +2 -2
  30. package/dist/commands/runs/list.js +8 -2
  31. package/dist/commands/verify-attestation.d.ts +3 -1
  32. package/dist/commands/verify-attestation.js +18 -7
  33. package/dist/errors/formatter.d.ts +14 -1
  34. package/dist/errors/formatter.js +13 -3
  35. package/dist/errors/index.d.ts +2 -1
  36. package/dist/errors/index.js +3 -2
  37. package/dist/errors/source-location.d.ts +20 -0
  38. package/dist/errors/source-location.js +45 -0
  39. package/dist/execution/executor.js +25 -5
  40. package/dist/fixtures/defaults/index.js +2 -1
  41. package/dist/llm-context/llms-architecture.txt +48 -19
  42. package/dist/llm-context/llms-cli.txt +2461 -1582
  43. package/dist/llm-context/llms-features.txt +178 -106
  44. package/dist/llm-context/llms-full.txt +6124 -4423
  45. package/dist/llm-context/llms-getting-started.txt +306 -42
  46. package/dist/llm-context/llms-patterns.txt +13 -7
  47. package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
  48. package/dist/llm-context/llms-sdk.txt +591 -2152
  49. package/dist/llm-context/llms.txt +30 -16
  50. package/dist/local-plane/orchestrator-process.d.ts +12 -7
  51. package/dist/local-plane/orchestrator-process.js +28 -14
  52. package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
  53. package/dist/local-plane/os-mismatch-hint.js +34 -0
  54. package/dist/local-plane/plane-liveness.d.ts +68 -0
  55. package/dist/local-plane/plane-liveness.js +145 -0
  56. package/dist/local-plane/plane-manager.d.ts +60 -18
  57. package/dist/local-plane/plane-manager.js +210 -70
  58. package/dist/local-plane/plane-trigger.d.ts +13 -4
  59. package/dist/local-plane/plane-trigger.js +27 -14
  60. package/dist/local-plane/platform-attach.js +1 -5
  61. package/dist/local-plane/port-holder.d.ts +78 -0
  62. package/dist/local-plane/port-holder.js +227 -0
  63. package/dist/local-plane/postgres.d.ts +15 -3
  64. package/dist/local-plane/postgres.js +34 -12
  65. package/dist/local-plane/run-follow.d.ts +20 -0
  66. package/dist/local-plane/run-follow.js +51 -4
  67. package/dist/local-plane/scaler-config.d.ts +28 -14
  68. package/dist/local-plane/scaler-config.js +59 -41
  69. package/dist/local-plane/source-provider.js +4 -10
  70. package/dist/lockfile/generator.d.ts +14 -1
  71. package/dist/lockfile/generator.js +99 -33
  72. package/dist/lockfile/index.d.ts +3 -1
  73. package/dist/lockfile/index.js +3 -2
  74. package/dist/lockfile/purity-diagnostics.d.ts +31 -0
  75. package/dist/lockfile/purity-diagnostics.js +52 -0
  76. package/dist/postinstall.js +2 -1
  77. package/dist/remote/artifact-extract.d.ts +8 -0
  78. package/dist/remote/artifact-extract.js +58 -0
  79. package/dist/remote/config.d.ts +21 -3
  80. package/dist/remote/config.js +24 -5
  81. package/dist/remote/dashboard-client.d.ts +39 -2
  82. package/dist/remote/dashboard-client.js +47 -4
  83. package/dist/remote/fs-case.d.ts +21 -0
  84. package/dist/remote/fs-case.js +45 -0
  85. package/dist/remote/history.d.ts +9 -3
  86. package/dist/remote/history.js +79 -10
  87. package/dist/remote/local-repo-identity.d.ts +1 -1
  88. package/dist/remote/local-repo-identity.js +2 -1
  89. package/dist/remote/notifications-client.d.ts +149 -0
  90. package/dist/remote/notifications-client.js +103 -0
  91. package/dist/remote/oauth.d.ts +13 -0
  92. package/dist/remote/oauth.js +31 -5
  93. package/dist/remote/output/summary.js +76 -14
  94. package/dist/remote/render.js +30 -9
  95. package/dist/remote/uploader.js +5 -4
  96. package/dist/templates/agents-md.d.ts +1 -1
  97. package/dist/templates/agents-md.js +6 -6
  98. package/dist/templates/index.d.ts +1 -1
  99. package/dist/templates/index.js +2 -2
  100. package/dist/templates/package-json.d.ts +15 -0
  101. package/dist/templates/package-json.js +26 -5
  102. package/dist/test-runner/dry-run.d.ts +2 -1
  103. package/dist/test-runner/dry-run.js +18 -1
  104. package/dist/test-runner/event-types.d.ts +2 -0
  105. package/dist/test-runner/event-types.js +3 -0
  106. package/dist/test-runner/job-executor.d.ts +1 -0
  107. package/dist/test-runner/job-executor.js +81 -43
  108. package/dist/test-runner/output-formatter.d.ts +5 -2
  109. package/dist/test-runner/output-formatter.js +6 -4
  110. package/dist/test-runner/payload-builder.d.ts +7 -1
  111. package/dist/test-runner/payload-builder.js +11 -2
  112. package/dist/test-runner/rule-evaluator.d.ts +7 -3
  113. package/dist/test-runner/rule-evaluator.js +13 -12
  114. package/dist/test-runner/step-context.d.ts +23 -5
  115. package/dist/test-runner/step-context.js +28 -118
  116. package/dist/types.d.ts +21 -2
  117. package/dist/types.js +4 -2
  118. package/dist/validation/index.d.ts +2 -0
  119. package/dist/validation/index.js +2 -1
  120. package/dist/validation/typecheck.d.ts +28 -0
  121. package/dist/validation/typecheck.js +110 -0
  122. package/dist/validation/validator.d.ts +9 -4
  123. package/dist/validation/validator.js +38 -44
  124. package/package.json +12 -15
  125. package/sbom.spdx.json +1322 -1964
  126. package/dist/assets/api-TJJVHrjC.json +0 -118
  127. package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
  128. package/dist/assets/package-BpQF9kR8.json +0 -74
  129. package/dist/assets/package-Ceo2h27X.json +0 -89
  130. package/dist/assets/source_context-D0atuL28.json +0 -20
  131. package/dist/assets/type-BFqO8SCZ.json +0 -202
  132. package/dist/commands/cancel.d.ts +0 -22
  133. package/dist/commands/detect-package-manager.d.ts +0 -42
  134. package/dist/commands/held-run-resolve.d.ts +0 -50
  135. package/dist/commands/status.d.ts +0 -34
  136. package/dist/commands/test.d.ts +0 -88
  137. package/dist/errors/codes.d.ts +0 -25
  138. package/dist/local-executor/dag-scheduler.d.ts +0 -44
  139. package/dist/local-executor/index.d.ts +0 -23
  140. package/dist/local-executor/job-runner.d.ts +0 -47
  141. package/dist/local-executor/materializer.d.ts +0 -44
  142. package/dist/local-executor/output-streamer.d.ts +0 -31
  143. package/dist/local-executor/payload-generator.d.ts +0 -16
  144. package/dist/local-executor/picker.d.ts +0 -33
  145. package/dist/local-executor/runs-on-display.d.ts +0 -9
  146. package/dist/local-executor/secret-loader.d.ts +0 -18
  147. package/dist/local-executor/to-event-payload.d.ts +0 -16
  148. package/dist/local-executor/types.d.ts +0 -93
  149. package/dist/local-executor/workflow-lock.d.ts +0 -82
  150. package/dist/package-F7UXSDHW.json +0 -74
  151. package/dist/remote/client.d.ts +0 -210
  152. package/dist/remote/observer.d.ts +0 -81
  153. package/dist/test-runner/summary.d.ts +0 -7
@@ -1,86 +1,151 @@
1
1
  # KiCI Getting started
2
2
 
3
- This bundle covers: Install the SDK, write your first workflow, compile and test locally.
3
+ This bundle covers: Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow.
4
4
 
5
5
  ## User guide
6
6
 
7
7
  Source: https://docs.kici.dev/user/
8
8
 
9
- Documentation for workflow authors -- people writing CI/CD pipelines in TypeScript using the KiCI SDK and compiler. If you are defining workflows, running local tests, or learning the SDK API, start here.
9
+ KiCI runs your CI/CD on infrastructure you control -- your own orchestrator and agents clone the code and run every job, while the hosted platform relays webhooks and renders the dashboard without ever seeing your source or secrets. You author workflows in real, typed TypeScript and run them locally before you push, so the pipeline you test on your laptop is the pipeline that runs in production. This section is for the people writing those workflows.
10
10
 
11
- ## Pages
11
+ ## Start here
12
12
 
13
- ### [Getting started with KiCI](https://docs.kici.dev/user/getting-started/)
13
+ 1. **[Green run in ~5 minutes](https://docs.kici.dev/user/quickstart/)** -- stand up an orchestrator and agent (Docker / Podman or bare metal) and watch your first workflow go green.
14
+ 2. **[Getting started](https://docs.kici.dev/user/getting-started/)** -- install the SDK and compiler, write your first workflow, compile it to a lock file, and test it locally with simulated events.
15
+ 3. **[Why KiCI](https://docs.kici.dev/user/why-kici/)** -- the case for running CI on your own infrastructure with typed TypeScript workflows.
16
+ 4. **[GitHub App provider](https://docs.kici.dev/user/providers/github/)** -- connect your first source and route real pull-request and push events.
14
17
 
15
- Install the SDK and compiler, write your first workflow, compile it to a lock file, and test it locally with simulated events. Covers prerequisites (Node.js 24+, pnpm), the `kici init` command for scaffolding, and the relationship between workflows, the lock file, and the three-tier runtime.
18
+ ## What's in the user guide
16
19
 
17
- ### [SDK reference](https://docs.kici.dev/user/sdk-reference/)
20
+ - **Authoring** -- the [SDK reference](https://docs.kici.dev/user/sdk-reference/) covers every factory function, trigger, rule, and matrix option; [workflow patterns](https://docs.kici.dev/user/workflow-patterns/) show monorepo builds, conditional jobs, dynamic matrices, and scheduling; [how your workflow code executes](https://docs.kici.dev/user/execution-model/) maps compile, orchestrator, and agent time.
21
+ - **Running and testing** -- the [CLI reference](https://docs.kici.dev/user/cli-reference/) documents every command; the [testing guide](https://docs.kici.dev/user/testing-guide/) covers `kici run remote`, fixtures, and overlay mode; the [dashboard](https://docs.kici.dev/user/dashboard/) is the web UI for watching runs.
22
+ - **Wiring sources** -- the [GitHub App](https://docs.kici.dev/user/providers/github/) and [universal-git](https://docs.kici.dev/user/providers/universal-git/) providers connect your forge; [global workflows](https://docs.kici.dev/user/global-workflows/) run cross-repo.
23
+ - **Configuration and secrets** -- [contexts](https://docs.kici.dev/user/contexts/), [secrets](https://docs.kici.dev/user/secrets/), [dynamic values](https://docs.kici.dev/user/dynamic-values/), [concurrency groups](https://docs.kici.dev/user/concurrency/), [lifecycle hooks](https://docs.kici.dev/user/hooks/), and [environment variables](https://docs.kici.dev/user/env-vars/).
18
24
 
19
- Complete API reference for `@kici-dev/sdk`. Covers all factory functions (`workflow()`, `job()`, `step()`), trigger builders (`pr()`, `push()`), rule functions (`rule()`, `skip()`), matrix configuration (static arrays, static objects, dynamic functions, include/exclude), and the `StepContext` interface that steps receive at runtime.
25
+ The left sidebar is the full index for the user guide -- every page in curated reading order.
20
26
 
21
- ### [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/)
27
+ ---
28
+
29
+ ## How your workflow code executes
30
+
31
+ Source: https://docs.kici.dev/user/execution-model/
32
+
33
+ Your workflow is plain TypeScript, but different parts of it run at three distinct moments, on three different machines. Knowing which part runs where is the difference between a workflow that behaves and one that surprises you. This page is the map.
34
+
35
+ ## The three phases
36
+
37
+ | Phase | Where it runs | What runs | When |
38
+ | ---------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
39
+ | **Compile** | Your dev machine or CI (`kici compile`) | Load your workflow modules, validate the DAG, assign step IDs, analyze dynamic-value purity, emit `kici.lock.json` | Before anything is pushed |
40
+ | **Orchestrator** | Your orchestrator (no repo clone) | Match triggers against the lock, evaluate **pure** inline dynamic values in a sandboxed JavaScript VM, dispatch jobs | On each incoming event |
41
+ | **Agent** | An ephemeral agent (fresh clone per job) | Load the workflow module, evaluate job and step rules, run step bodies and hooks, run impure dynamic-value init jobs and `dynamicJob` generators (both forms) | After dispatch |
22
42
 
23
- Why the lock file must stay in sync with workflow source, how to commit both together, using pre-commit and CI to catch drift early, and the agent-side hash verification when compiling from source.
43
+ The lock file is the seam. Everything left of it is decided once at compile time and frozen into JSON; everything right of it reads that JSON. See [the lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/) and [the three-tier architecture](https://docs.kici.dev/architecture/overview/) for the wider picture.
24
44
 
25
- ### [CLI reference](https://docs.kici.dev/user/cli-reference/)
45
+ ## Compile time
26
46
 
27
- All CLI commands provided by `@kici-dev/compiler`: `kici compile` (with watch mode and check mode), `kici run` (local and remote execution), `kici preview` (event simulation with dry-run, filtering, debug output, and custom payloads), `kici login`/`logout`/`org` (authentication and org management), `kici diagnostics` (infrastructure tree) and `kici runs` (`list`/`show`/`logs`/`rerun`/`cancel` run management), `kici secrets` (secret listing), `kici types` (type generation), `kici fixture` (generate test payloads), `kici init` (interactive project scaffolding), `kici hook` (pre-commit hook installation), `kici endpoints` (webhook entrypoints), and `kici workflows` (workflow listing). Includes environment variables and exit codes.
47
+ `kici compile` loads your `.kici/workflows/*.ts`, validates dependencies (no cycles, no missing `needs`), assigns compile-time step IDs (unnamed steps become `step-1`, `step-2`, ), runs purity analysis over every dynamic-value function, and writes `kici.lock.json`.
28
48
 
29
- ### [Workflow patterns](https://docs.kici.dev/user/workflow-patterns/)
49
+ The compiler runs your module's **top-level code** to build the workflow object — but that execution's side effects and in-memory state do not travel. Only the resulting workflow structure (plus the serialized source of pure dynamic-value functions) lands in the lock. Anything your top-level code computes that isn't part of the returned workflow object simply doesn't exist past this point.
30
50
 
31
- Common patterns for building real-world CI/CD workflows. Includes examples for basic CI pipelines with job dependencies, monorepo path-based triggering, conditional jobs with rules, matrix builds across Node versions, dynamic jobs generated at runtime, Docker-based step execution, and parallel test splitting.
51
+ See [compile the workflow](https://docs.kici.dev/user/getting-started/#compile-the-workflow) for the command in context.
32
52
 
33
- ### [Dashboard](https://docs.kici.dev/user/dashboard/)
53
+ ## What serializes into the lock file
34
54
 
35
- Guide to the KiCI web dashboard. Covers navigation (sidebar, org switcher, mobile bottom tabs), run list (table columns, filters, pagination, empty states), run detail (resizable two-panel layout, job tree, step selection, metadata tabs), log viewer (ANSI color rendering, search, permalink, copy), settings page (tabbed layout), theme toggle, keyboard shortcuts, and error pages.
55
+ The lock is portable JSON. It carries:
36
56
 
37
- ### [Testing guide](https://docs.kici.dev/user/testing-guide/)
57
+ - Workflow and trigger metadata.
58
+ - The job and step DAG, with compile-time step IDs.
59
+ - Static values, verbatim.
60
+ - The **source text** of pure dynamic-value functions, as inline expressions.
38
61
 
39
- How to run and write tests for KiCI workflows, including remote test execution with `kici run remote`, fixture-based testing, and overlay mode for uncommitted changes.
62
+ It does **not** carry:
40
63
 
41
- ### [Contexts](https://docs.kici.dev/user/contexts/)
64
+ - Your module's runtime state or module-level variables.
65
+ - Closures over those variables.
66
+ - Live instances of modules you imported.
67
+ - Anything computed at top level that isn't part of the returned workflow object.
42
68
 
43
- Configure deployment environments (staging, production, review/\*) with variables, scoped secrets, and protection rules. Covers the SDK API (`environment`, `env`, `concurrencyGroup` on jobs), the 8-layer variable merge precedence, protection rules (branch restrictions, required reviewers, wait timers, concurrency), dashboard management, type generation, and migration from the legacy contexts system.
69
+ The consequence is blunt: if a value isn't in the lock, the orchestrator can't see it it has no copy of your repository.
44
70
 
45
- ### [Environment variables](https://docs.kici.dev/user/env-vars/)
71
+ ## Orchestrator time
46
72
 
47
- Reference for all `KICI_*` environment variables supported by the CLI. Covers authentication overrides (OIDC issuer, client ID, project ID), browser behavior (custom browser command, fixed callback port), development mode, and usage examples for CI/CD, alternate environments, and headless setups.
73
+ On each event the orchestrator matches triggers using only the lock — it never clones your repository. Pure dynamic `context`, `env`, and `concurrencyGroup` functions are evaluated here, as inline expressions in a sandboxed JavaScript VM (~0ms overhead), instead of dispatching a separate job to resolve them.
48
74
 
49
- ### [CLI authentication](https://docs.kici.dev/user/cli-auth/)
75
+ A runtime error in an inline expression fails the job immediately — there is no automatic fallback to the clone-and-evaluate path. The orchestrator does **not** run `dynamicJob` generator bodies itself: for the event-only (function) form it dispatches a dedicated dynamic-evaluation job to an agent at event time; the generator function then runs agent-side (see below).
50
76
 
51
- Authenticate the KiCI CLI with browser-based OAuth (default), device authorization flow (for headless environments), or API key paste (for CI/CD pipelines). Covers org management and PATs.
77
+ See [dynamic values](https://docs.kici.dev/user/dynamic-values/) for the exact rules that make a function pure or impure.
52
78
 
53
- ### [Event system](https://docs.kici.dev/user/events/)
79
+ ## Agent time
54
80
 
55
- Event model concepts: event types, the registration model, event matching, and circuit breaker protection. Understanding this distinction is key to working with non-git triggers like schedules, custom events, and generic webhooks.
81
+ After dispatch, each job runs in its own ephemeral agent sandbox: a shallow clone at the dispatch ref (or a source-tarball extract for non-build jobs), then the workflow module is loaded fresh TypeScript is transformed on import. On the agent, in order:
56
82
 
57
- ### [Lifecycle hooks](https://docs.kici.dev/user/hooks/)
83
+ 1. **Job-level rules** are evaluated. By this point the agent has already spawned and the source has already been restored, so a job that its rules skip has **still** paid for that spawn and clone; only its steps are avoided.
84
+ 2. **Step-level rules**, then each step's `run()` body and its hooks.
85
+ 3. **Impure** dynamic values are resolved here too, via an init job that clones and evaluates the function (~5–10s) before the real job runs.
86
+ 4. **`dynamicJob` generators run here — both forms.** The event-only (function) form runs in a dedicated evaluation job dispatched at event time; the result-aware (options) form is deferred until its declared `needs` complete, then run with the upstream outputs frozen as `ctx.needs`.
58
87
 
59
- SDK hook API for cancel, cleanup, success, failure, and step-level callbacks. Hooks run at specific points in the execution lifecycle to react to outcomes and perform cleanup.
88
+ See [job execution](https://docs.kici.dev/architecture/execution/job-execution/) and [hooks and rules](https://docs.kici.dev/user/hooks/) for the details.
60
89
 
61
- ### [Concurrency groups](https://docs.kici.dev/user/concurrency/)
90
+ ## What re-evaluates where
62
91
 
63
- Control parallel execution with auto-cancel and queue modes. Prevent multiple workflow runs from executing in parallel when they target the same resource.
92
+ | Construct | Runs on | When |
93
+ | ---------------------------- | ---------------- | ------------------------------- |
94
+ | Static value | Compile → lock | Never re-evaluated |
95
+ | Pure dynamic value | Orchestrator VM | Per event |
96
+ | Impure dynamic value | Agent init job | Per event |
97
+ | Job-level rules | Agent | After clone |
98
+ | Step-level rules | Agent | Per step |
99
+ | `dynamicJob` (function form) | Agent (eval job) | Dispatched at event time |
100
+ | `dynamicJob` (options form) | Agent | Deferred until `needs` complete |
101
+ | Step / job body + hooks | Agent | Per job |
64
102
 
65
- ### [Dynamic values](https://docs.kici.dev/user/dynamic-values/)
103
+ **Determinism note.** `ctx.event` and `ctx.needs` are frozen snapshots — captured once and replayed unchanged on any re-evaluation. A generator that derives its output from them is stable across re-evaluations; one that reads the wall clock (`Date.now()`) or a random source (`Math.random()`) is not.
66
104
 
67
- Compute `environment`, `env`, and `concurrencyGroup` at runtime based on the incoming event payload. Instead of hardcoding static strings, pass a function that receives the webhook event and returns the resolved value.
105
+ ## OutputProxy: how outputs flow
68
106
 
69
- ### [Secrets](https://docs.kici.dev/user/secrets/)
107
+ `step(...).result` and `job(...).result` return an `OutputProxy` — a lazy proxy that, at the type level, mirrors the shape of the step or job's declared outputs so that reading `result.foo` is type-checked, and at runtime defers each property read to a shared outputs map populated as the run progresses.
70
108
 
71
- Access encrypted secrets in workflow steps via the explicit secrets API. Secrets are never auto-injected into `process.env` -- you must explicitly request each secret by name.
109
+ ```typescript
110
+ import { workflow, job, step, z } from '@kici-dev/sdk';
111
+
112
+ const build = job('build', {
113
+ runsOn: 'default',
114
+ steps: [
115
+ step('compile', {
116
+ outputs: { artifact: z.string() },
117
+ run: async () => ({ artifact: 'app.tar.gz' }),
118
+ }),
119
+ step('publish', {
120
+ // `compile.result.artifact` is typed from the `outputs` schema above.
121
+ run: async ({ steps }) => {
122
+ await Promise.resolve(steps.compile.result.artifact);
123
+ },
124
+ }),
125
+ ],
126
+ });
72
127
 
73
- ### [GitHub App provider](https://docs.kici.dev/user/providers/github/)
128
+ export default workflow('build-and-publish', { jobs: [build] });
129
+ ```
74
130
 
75
- The flagship source. Covers creating the GitHub App on GitHub's side (permissions, webhook URL, private key), registering it with the orchestrator via `kici-admin source add github`, routing keys (`github:<appId>`), global-workflow policy, enriched Check runs on pull requests, private-key and webhook-secret rotation, and troubleshooting.
131
+ Outputs are typed across the job boundary too: reading `jobRef.result.…` or `ctx.jobOutputs(jobRef)` on a **job reference** threads the upstream job's inferred output shape through from any job, in a step body or a `run:` shorthand so a typo on an output field or a renamed step is a compile error. Typed `ctx.needs.jobRef.result.…` additionally works in a `run:` shorthand job (where the run function's `ctx` derives from the enclosing job's `needs` tuple). Name your steps and use the options form (`step('name', { run })`) to give a job a typed output shape, and pass references rather than string names — string-form `needs` stay loosely typed. See [output chaining](https://docs.kici.dev/user/sdk/core/#output-chaining) for the authoring rules.
76
132
 
77
- ### [Universal-git provider](https://docs.kici.dev/user/providers/universal-git/)
133
+ ## Common footguns
78
134
 
79
- Connect a non-GitHub-App forge (Forgejo, Gitea, Gogs, GitLab, plain GitHub) to KiCI via its webhook. Covers preset selection, PAT and SSH credential wiring, credential rotation, global workflow policy against `generic:<orgId>:<sourceId>` routing keys, and troubleshooting.
135
+ | Symptom | Why | Fix |
136
+ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
137
+ | A top-level `let seen = 0` (or a cache filled in job A) is empty in job B | Each job loads the workflow module fresh in its own agent process after its own clone — there is no shared memory between jobs | Pass data through step/job **outputs** (`OutputProxy` / `needs`), not module variables |
138
+ | `context: (event) => event.ref + SUFFIX`, where `SUFFIX` is a module constant, silently falls back to the slower init-job path | Purity analysis only allows the function's own params, locals, and a fixed safe-globals set — a free identifier makes it impure | Inline the constant, or accept the init-job path knowingly. See [pure functions](https://docs.kici.dev/user/dynamic-values/#pure-functions-inline-evaluation) |
139
+ | Fan-out job identities shift between re-evaluations | `ctx.event` / `ctx.needs` are frozen and replayed, but `Date.now()` / `Math.random()` are not | Derive job identity only from the frozen event/needs snapshot |
140
+ | A rule-skipped job still spawned an agent and cloned | Job-level rules evaluate agent-side, after dispatch and clone — not on the orchestrator | This is by design: rules can read true runtime context (`$`, `changedFiles`, `env`). See [step-level rules](https://docs.kici.dev/user/hooks/#step-level-rules) |
80
141
 
81
- ### [Global workflows](https://docs.kici.dev/user/global-workflows/)
142
+ ## See also
82
143
 
83
- Cross-repo workflows that let a single workflow repo define jobs which run on events from many source repos in the same org. Covers the mental model (workflow repo vs. source repo, authoring axis vs. source axis), SDK syntax for declaring globals via `repos:` patterns, the dashboard opt-in flow and per-setting semantics (master toggle, author allow-list, source deny-list, elevated-access list), the security model, and troubleshooting skipped dispatches.
144
+ - [Dynamic values](https://docs.kici.dev/user/dynamic-values/)
145
+ - [Hooks and rules](https://docs.kici.dev/user/hooks/)
146
+ - [Lock file and drift](https://docs.kici.dev/user/lock-file-and-drift/)
147
+ - [Job execution (architecture)](https://docs.kici.dev/architecture/execution/job-execution/)
148
+ - [SDK: rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/)
84
149
 
85
150
  ---
86
151
 
@@ -123,6 +188,12 @@ The package manager is detected from your repo's `packageManager` field, lockfil
123
188
  | `--skip-install` | Create files without installing dependencies |
124
189
  | `--package-manager <npm\|pnpm\|yarn>` | Force a package manager for the install step (default: auto) |
125
190
  | `--mjs` | JavaScript-only mode (no TypeScript, no deps) |
191
+ | `--workspace` | Integrate `.kici/` into the surrounding workspace |
192
+ | `--standalone` | Force a self-contained `.kici/` even inside a workspace |
193
+
194
+ ### Workspace integration
195
+
196
+ If you run `kici init` inside a pnpm, npm, or yarn workspace, it offers to **integrate** `.kici/` into that workspace instead of scaffolding a self-contained folder. In integrate mode there is no `.kici/package.json`: `@kici-dev/sdk` is added to your workspace-root `package.json`, and your workflows can `import` your other workspace packages directly — for example shared build or deploy utilities. Pass `--workspace` to opt in non-interactively, or `--standalone` to keep the self-contained layout. In CI the default is standalone. See the [`kici init` reference](https://docs.kici.dev/user/cli/account-and-org/#kici-init) for details.
126
197
 
127
198
  ### MJS mode
128
199
 
@@ -177,7 +248,7 @@ Create `.kici/workflows/ci.ts`:
177
248
  import { workflow, job, step, pr } from '@kici-dev/sdk';
178
249
 
179
250
  const lint = job('lint', {
180
- runsOn: 'linux',
251
+ runsOn: 'kici:os:linux',
181
252
  steps: [
182
253
  step('install', async ({ $ }) => {
183
254
  await $`pnpm install --frozen-lockfile`;
@@ -189,7 +260,7 @@ const lint = job('lint', {
189
260
  });
190
261
 
191
262
  const test = job('test', {
192
- runsOn: 'linux',
263
+ runsOn: 'kici:os:linux',
193
264
  needs: [lint],
194
265
  steps: [
195
266
  step('install', async ({ $ }) => {
@@ -213,7 +284,7 @@ This workflow:
213
284
  - Runs a `lint` job first
214
285
  - Runs a `test` job after lint succeeds (`needs: [lint]`)
215
286
 
216
- `runsOn` selects which agents may run a job. Every agent self-reports `kici:os:<platform>`, `kici:arch:<cpu>`, and `kici:host:<hostname>`, so `runsOn: 'kici:os:linux'` targets any connected Linux agent with zero configuration `kici init` scaffolds workflows with exactly that. Use a custom label such as `'linux'` or `'gpu'` (defined in your scaler's `labelSet`) to target a specific pool instead. See the [runsOn forms](https://docs.kici.dev/user/sdk/core/#runson-forms) reference for the full label model.
287
+ `runsOn` selects which agents may run a job. Every agent self-reports `kici:os:<platform>`, `kici:arch:<cpu>`, and `kici:host:<hostname>`, so `runsOn: 'kici:os:linux'` targets any connected Linux agent with zero configuration. `kici init` scaffolds workflows targeting your own host's OS label (`kici:os:linux`, `kici:os:macos`, or `kici:os:windows`) so your first local run dispatches on this machine. Use a custom label such as `'linux'` or `'gpu'` (defined in your scaler's `labelSet`) to target a specific pool instead. See the [runsOn forms](https://docs.kici.dev/user/sdk/core/#runson-forms) reference for the full label model.
217
288
 
218
289
  **Single-step shortcut.** If a job only has one step, pass `run` directly to `job()` instead of building a `steps: [step(...)]` array:
219
290
 
@@ -285,6 +356,8 @@ npx kici run pr:open --local
285
356
 
286
357
  This compiles, matches triggers, and runs all matched jobs on this machine — which joins as an ephemeral agent through the warm local dev plane — with DAG-based parallel scheduling.
287
358
 
359
+ If a run never appears or a webhook seems ignored, run `kici doctor` — it walks your login, organization, orchestrator connection, and compiled lock file, and tells you the exact command to fix the first broken step.
360
+
288
361
  ## Workflow dependencies
289
362
 
290
363
  KiCI workflows can use any npm package. Dependencies are declared in `.kici/package.json`, which `kici init` generates automatically.
@@ -441,6 +514,7 @@ The compiler watches `.kici/workflows/*.ts` and recompiles on every save.
441
514
  ## Next steps
442
515
 
443
516
  - **[5-minute quickstart](https://docs.kici.dev/user/quickstart/)** -- ready to run your workflow on real infrastructure? Stand up an orchestrator + agent (Docker / Podman or bare metal)
517
+ - **[How your workflow code executes](https://docs.kici.dev/user/execution-model/)** -- the mental model: which parts of your workflow run at compile time, on the orchestrator, and on the agent
444
518
  - **[SDK reference](https://docs.kici.dev/user/sdk-reference/)** -- complete API for workflows, jobs, steps, triggers, rules, and matrix
445
519
  - **[CLI reference](https://docs.kici.dev/user/cli-reference/)** -- all CLI commands with options and examples
446
520
  - **[Workflow patterns](https://docs.kici.dev/user/workflow-patterns/)** -- common patterns for real-world CI/CD workflows
@@ -472,6 +546,158 @@ The lock file approach means the orchestrator stays git-agnostic -- it only need
472
546
 
473
547
  ---
474
548
 
549
+ ## Migrating from GitHub Actions
550
+
551
+ Source: https://docs.kici.dev/user/migrating-from-github-actions/
552
+
553
+ ## Before you start
554
+
555
+ KiCI workflows are real, typed TypeScript in `.kici/workflows/*.ts` instead of YAML in `.github/workflows/*.yml`. The compiler validates them ahead of time, and at run time the agent clones your repository and executes them on your own infrastructure. This guide maps the GitHub Actions concepts you already know to their KiCI equivalents, translates one realistic workflow side-by-side, and lists honestly what has no equivalent yet.
556
+
557
+ Follow [getting started](https://docs.kici.dev/user/getting-started/) for the full setup. The mapping below assumes you have `@kici-dev/sdk` installed.
558
+
559
+ ## Concept mapping
560
+
561
+ | GitHub Actions | KiCI | Notes |
562
+ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
563
+ | Workflow file `.github/workflows/ci.yml` | TypeScript module `.kici/workflows/ci.ts` that default-exports `workflow('ci', {...})` | Workflows are modules built from `workflow()`, `job()`, and `step()` factories. |
564
+ | `on: pull_request:` | `on: pr({ target: 'main' })` | `pr()` is a trigger factory; `on` takes one trigger or an array of triggers. |
565
+ | `on: push: branches: [main]` | `on: push({ branches: ['main'] })` | `push()` matches by branch pattern. |
566
+ | `on: schedule: - cron:` | `on: schedule({ cron: '0 2 * * *', timezone: 'UTC' })` | `cron` is required; `timezone` defaults to `UTC`. In a cluster only the leader evaluates schedules. |
567
+ | `on: workflow_dispatch: inputs:` | `on: dispatch({ inputs: defineDispatchInputs({...}) })`, read typed via `inputs.from(ctx)` | Dispatch inputs are declared with Zod schemas and read back fully typed. |
568
+ | `jobs:` map keyed by name | `jobs: [job('lint', {...}), job('test', {...})]` | Jobs are an array of `job()` results. |
569
+ | `needs: [build]` (string references) | `needs: [buildJob]` (object references) | You reference the `job()` value directly; the compiler validates the dependency graph for cycles and missing references. |
570
+ | `runs-on: <hosted runner>` | `runsOn: 'kici:os:linux'` or a custom scaler label | Every agent self-reports labels such as `kici:os:linux`, `kici:arch:x64`, and `kici:host:<hostname>`. A GitHub-hosted runner label matches no KiCI agent. |
571
+ | `steps: - run: npm test` | `steps: [step('test', async ({ $ }) => { await $\`npm test\` })]` | The `$` in a step body is a zx shell; a single-step job can use the `run:` function shorthand instead of `steps:`. |
572
+ | `steps: - uses: actions/checkout@v4` | (nothing) | The agent clones the repository automatically before steps run, so there is no explicit checkout step. |
573
+ | `steps: - uses: actions/setup-node@v4` | Provision the toolchain in a step or from the agent image | KiCI has no `uses:`-style setup actions; the toolchain comes from the agent environment or an explicit step. |
574
+ | `${{ secrets.NPM_TOKEN }}` | `await ctx.secrets.get('NPM_TOKEN')`, or `ctx.secrets.expose('NPM_TOKEN')` to place it in the environment | Secrets are never auto-injected into `process.env`; access is always explicit. |
575
+ | `environment: production` (+ protection rules) | `context: 'production'` (or `contexts: ['staging', 'prod']`) | A context carries variables, bound secrets, and protection rules (branch restrictions, required reviewers, wait timers, concurrency limits, minimum trust). |
576
+ | `env:` (job or step level) | `env: { KEY: 'val' }` on a job | Job-level `env` accepts a static object or a `(event) => ({...})` function for dynamic values. |
577
+ | `strategy: matrix: node: [18, 20, 22]` | `matrix: ['18', '20', '22']` or `{ node: [...], os: [...] }` | A single-dimension array exposes `matrix.value`; an object expands all combinations and exposes each dimension by name. A dynamic function form is also supported. |
578
+ | `if: github.ref == 'refs/heads/main'` | Native TypeScript conditionals plus `rule()` / `skip()` and dynamic values | Conditions are real TypeScript branching; dynamic values are pure functions of the normalized event. |
579
+ | `concurrency: group: ...` | `concurrencyGroup: 'production-api'` (static) or a dynamic function | Set at the job level; a workflow-level concurrency group also exists. |
580
+ | `jobs.<id>.outputs` | Structured job and step outputs consumed downstream via `needs`; `ctx.setSecretOutput(key, value)` for encrypted outputs | Outputs pass values between jobs; secret outputs are encrypted. |
581
+ | `- uses: actions/cache@v4` | `cache` field on a job or step (declarative) or `ctx.cache.restore` / `ctx.cache.save` (imperative) | A keyed cache, immutable once written, org- and ref-scoped, backed by the orchestrator's object storage. |
582
+ | Reusable workflows / composite actions / marketplace `uses:` | `@kici-dev/action-*` building blocks or any npm package you `import` | Reusable logic is imported as functions, not referenced by `uses:`. |
583
+
584
+ ### Secrets are explicit
585
+
586
+ KiCI never copies secrets into `process.env` for you. A step reads a value with `ctx.secrets.get('KEY')` or injects it into the environment with `ctx.secrets.expose('KEY')`, and can mount a secret as a file with `ctx.secrets.mountFile(...)`. Every access is tracked. See [secrets](https://docs.kici.dev/user/secrets/).
587
+
588
+ ### Environments become contexts
589
+
590
+ A GitHub environment maps to a KiCI context bound at the job level with `context:` (or `contexts:` for several). A context carries variables, bound secrets, and protection rules — branch restrictions, required reviewers, wait timers, concurrency limits, and a minimum-trust gate. See [contexts](https://docs.kici.dev/user/contexts/).
591
+
592
+ ### `if:` becomes real TypeScript
593
+
594
+ There is no expression mini-language. Conditions are ordinary TypeScript, and values that depend on the event are pure functions of the normalized event object. See [dynamic values](https://docs.kici.dev/user/dynamic-values/).
595
+
596
+ ### Matrix
597
+
598
+ A single-dimension matrix is an array (`matrix: ['18', '20', '22']`) and exposes the current value as `matrix.value` in the step context. A multi-dimension matrix is an object and exposes each dimension by name. See [conditionals and matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/).
599
+
600
+ ### Caching
601
+
602
+ Declare a `cache` on a job or step, or drive it imperatively with `ctx.cache.restore(spec)` and `ctx.cache.save(spec)`. Cache entries are keyed and immutable once written. See [caching](https://docs.kici.dev/user/sdk/caching/).
603
+
604
+ ## A real workflow, translated
605
+
606
+ Here is a pull-request CI workflow that runs tests across a Node version matrix and uploads coverage using a secret.
607
+
608
+ The GitHub Actions version:
609
+
610
+ ```yaml
611
+ name: ci
612
+ on:
613
+ pull_request:
614
+ branches: [main]
615
+ jobs:
616
+ test:
617
+ # kici-lint-allow-github-runner: GitHub-hosted runner shown for contrast
618
+ runs-on: ubuntu-latest
619
+ strategy:
620
+ matrix:
621
+ node: ['18', '20', '22']
622
+ steps:
623
+ - uses: actions/checkout@v4
624
+ - uses: actions/setup-node@v4
625
+ with:
626
+ node-version: ${{ matrix.node }}
627
+ - run: npm ci
628
+ - run: npm test
629
+ - run: npx codecov
630
+ env:
631
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
632
+ ```
633
+
634
+ The KiCI translation:
635
+
636
+ ```typescript
637
+ import { workflow, job, step, pr } from '@kici-dev/sdk';
638
+
639
+ const test = job('test', {
640
+ runsOn: 'kici:os:linux',
641
+ matrix: ['18', '20', '22'],
642
+ context: 'ci',
643
+ steps: [
644
+ step('install', async ({ $ }) => {
645
+ // The agent already cloned the repo — no checkout step needed.
646
+ await $`npm ci`;
647
+ }),
648
+ step('test', async ({ $, matrix }) => {
649
+ // matrix.value is the Node version for this cell ('18' | '20' | '22').
650
+ // Select it however your agent provisions toolchains, e.g. a version manager.
651
+ await $`nvm use ${matrix!.value}`;
652
+ await $`npm test`;
653
+ }),
654
+ step('coverage', async (ctx) => {
655
+ await ctx.secrets.expose('CODECOV_TOKEN');
656
+ await ctx.$`npx codecov`;
657
+ }),
658
+ ],
659
+ });
660
+
661
+ export default workflow('ci', {
662
+ on: pr({ target: 'main' }),
663
+ jobs: [test],
664
+ });
665
+ ```
666
+
667
+ What changed and why:
668
+
669
+ - `on: pull_request` → `on: pr({ target: 'main' })`.
670
+
671
+ <!-- kici-lint-allow-github-runner: contrasts GitHub's ubuntu-latest with the KiCI auto-label -->
672
+
673
+ - `runs-on: ubuntu-latest` → `runsOn: 'kici:os:linux'`, an auto-label every Linux agent reports; a GitHub hosted-runner label would match no agent. See [runsOn forms](https://docs.kici.dev/user/sdk/core/#runson-forms).
674
+ - `actions/checkout` → removed; the agent clones the repository before steps run.
675
+ - `actions/setup-node` with `matrix.node` → `matrix: ['18', '20', '22']`, with the current value available as `matrix.value` in the step context. KiCI has no built-in setup-node, so toolchain selection is a step or agent-image concern. See [conditionals and matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/).
676
+ - `${{ secrets.CODECOV_TOKEN }}` → `ctx.secrets.expose('CODECOV_TOKEN')`, explicit and never auto-injected. See [secrets](https://docs.kici.dev/user/secrets/).
677
+ - `environment` → `context: 'ci'`. See [contexts](https://docs.kici.dev/user/contexts/).
678
+
679
+ The `nvm use` line is illustrative — KiCI does not install a Node version for you; use whatever your agent image or step provides.
680
+
681
+ ## What has no equivalent yet
682
+
683
+ **File artifacts between jobs.** KiCI has no first-class store for uploading a build directory as a named artifact and downloading it in a later job or from the run UI. It does have structured job and step outputs (and secret outputs) for passing _values_, and a keyed [cache](https://docs.kici.dev/user/sdk/caching/) for reusing files across runs. For build outputs you need to hand between jobs, use the cache or an external object store.
684
+
685
+ **A community action marketplace.** GitHub Actions has thousands of third-party marketplace actions addressable by `uses: owner/repo@ref`. KiCI's reusable blocks are the published `@kici-dev/action-*` packages plus any npm package you import — there is no marketplace of community-contributed actions.
686
+
687
+ **`uses:`-style step references.** KiCI steps are TypeScript functions, so you call reusable logic as imported library functions rather than referencing a composite or container action. This is a model shift rather than a missing feature, but a drop-in `uses:` translation does not exist.
688
+
689
+ **Provider breadth.** KiCI is GitHub-first. Other git hosts are reachable through the universal-git and local-file providers, but the richest event coverage is for GitHub. See [the GitHub provider](https://docs.kici.dev/user/providers/github/).
690
+
691
+ ## Next steps
692
+
693
+ - [Getting started](https://docs.kici.dev/user/getting-started/)
694
+ - [SDK reference — runsOn forms](https://docs.kici.dev/user/sdk/core/#runson-forms)
695
+ - [Conditionals and matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/)
696
+ - [Secrets](https://docs.kici.dev/user/secrets/) and [contexts](https://docs.kici.dev/user/contexts/)
697
+ - [CLI reference](https://docs.kici.dev/user/cli-reference/)
698
+
699
+ ---
700
+
475
701
  ## 5-minute quickstart
476
702
 
477
703
  Source: https://docs.kici.dev/user/quickstart/
@@ -509,3 +735,41 @@ If you're not sure, pick Docker / Podman.
509
735
  Both quickstarts deploy a real orchestrator + agent. If you only want to write a workflow and dry-run it on your laptop with no infrastructure, [Getting started](https://docs.kici.dev/user/getting-started/) covers `kici preview` and `kici run <event> --local` instead.
510
736
 
511
737
  ---
738
+
739
+ ## Why KiCI
740
+
741
+ Source: https://docs.kici.dev/user/why-kici/
742
+
743
+ Most CI platforms ask you to hand your source, your secrets, and your build machines to someone else. KiCI is built the other way around: your code runs on infrastructure you control, and you describe the pipeline in real TypeScript you can run before you push. This page is the short argument for that trade -- what it buys you, and what it costs.
744
+
745
+ ## Your infrastructure runs the code
746
+
747
+ KiCI is a three-tier relay. The hosted platform is a thin webhook router: it verifies the incoming webhook signature and relays the event to your orchestrator over a WebSocket. Your **orchestrator** decides what to run and dispatches jobs to your **agents**, which clone the repository, execute the steps, and stream logs back. The orchestrator and agents run on machines you own.
748
+
749
+ Because of that split, the hosted platform sees only the envelope, never the payload:
750
+
751
+ - **What it receives:** the webhook event, run metadata (workflow and job names, statuses, timings), aggregate operational metrics, and log lines while you are streaming them to the dashboard.
752
+ - **What it never sees:** your source, your secrets, your artifacts, or your signing material. Those stay on your orchestrator and agents. Log content is relayed only in transit for the live dashboard view -- the platform never stores it.
753
+
754
+ The hosted platform is operated by KiCI; you do not run your own. What you run is the orchestrator and the agents, and that is where every byte of your code and every secret lives. For the field-level breakdown of what does and does not leave your infrastructure, see [Data residency](https://docs.kici.dev/operator/data-residency/), and for the honest security posture of self-hosting the agents, see [Is self-hosting the agents a security risk?](https://docs.kici.dev/operator/security/self-hosting-security/).
755
+
756
+ ## Workflows are TypeScript
757
+
758
+ A KiCI workflow is a TypeScript program, not a YAML document. Jobs, steps, triggers, and matrices are typed values you compose with the full language -- loops, conditionals, functions, `async`/`await`, and your editor's autocompletion and type checking. Invalid pipelines fail at compile time, in your editor, instead of failing on the tenth push.
759
+
760
+ The same TypeScript runs everywhere. `kici run --local` executes your workflow on your own machine against a simulated event, and that is the same execution model the agents use in production. Your local run is the production pipeline, so you debug a green run before it ever reaches a source event. This also makes workflows something an AI coding agent can author, type-check, and run before it opens a pull request.
761
+
762
+ ## The honest trade
763
+
764
+ Running your own infrastructure is not free. You operate the orchestrator: a Docker / Podman or bare-metal service you stand up (in minutes for the quickstart topology), keep patched, back up, and upgrade. In exchange, you own your data, your egress, and your isolation model -- no third party executes your code or holds your secrets. KiCI is pre-1.0, so pin versions for production. See [Deploying the orchestrator](https://docs.kici.dev/operator/orchestrator/getting-started/) for what running it involves.
765
+
766
+ ## Compared to specific tools
767
+
768
+ If you are weighing KiCI against a specific incumbent -- GitHub Actions, GitLab CI, CircleCI, Jenkins, Buildkite, and others -- the point-by-point comparisons live on the marketing site, kept current with sourced references. Start with the [GitHub Actions comparison](https://kici.dev/compare/github-actions), or browse [all comparisons](https://kici.dev/compare).
769
+
770
+ ## Next steps
771
+
772
+ - **[Green run in ~5 minutes](https://docs.kici.dev/user/quickstart/)** -- stand up an orchestrator and agent and watch a workflow go green.
773
+ - **[Getting started](https://docs.kici.dev/user/getting-started/)** -- write your first workflow and test it locally.
774
+
775
+ ---
@@ -224,7 +224,7 @@ npx kici compile # regenerate .kici/kici.lock.json
224
224
  npx kici run dispatch --local
225
225
  ```
226
226
 
227
- `kici run dispatch --local` compiles the workflow, matches triggers against a simulated `dispatch` event, and executes the matched jobs on this machine — which joins as an ephemeral agent through the warm local dev plane — with DAG-based scheduling. No webhook, no GitHub, no deployed orchestrator involved. See [`kici run <event> --local`](https://docs.kici.dev/user/cli-reference/#kici-run-event---local) for options like `--env`, `--in-place`, and `--offline`.
227
+ `kici run dispatch --local` compiles the workflow, matches triggers against a simulated `dispatch` event, and executes the matched jobs on this machine — which joins as an ephemeral agent through the warm local dev plane — with DAG-based scheduling. No webhook, no GitHub, no deployed orchestrator involved. See [`kici run <event> --local`](https://docs.kici.dev/user/cli/runs-and-approvals/#kici-run-event---local) for options like `--env`, `--in-place`, and `--offline`.
228
228
 
229
229
  ### Unfiltered vs typed `dispatch()`
230
230
 
@@ -351,7 +351,7 @@ With a single-dimension matrix, the current value is available as `matrix.value`
351
351
 
352
352
  ### Multi-dimensional matrix
353
353
 
354
- Use an object to define multiple dimensions. KiCI expands all combinations (capped at 256):
354
+ Use an object to define multiple dimensions. KiCI expands all combinations (capped at 256, and each combination must be unique — a repeated value fails the job rather than running it twice):
355
355
 
356
356
  ```typescript
357
357
  const test = job('test', {
@@ -402,7 +402,9 @@ const test = job('test', {
402
402
  });
403
403
  ```
404
404
 
405
- Exclude is applied first (removes matching combinations), then include adds additional entries.
405
+ Exclude is applied first (removes matching combinations), then include adds additional entries. An
406
+ include entry's values appear in the child job name ordered by dimension name, whichever order you
407
+ write the keys in — see [Include and exclude](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/#include-and-exclude).
406
408
 
407
409
  ### Dynamic matrix
408
410
 
@@ -590,9 +592,7 @@ post-restart job until the host completes a reboot cycle, then dispatches it.
590
592
  import { workflow, job, step, restartHost, waitForHostAlive } from '@kici-dev/sdk';
591
593
 
592
594
  export default workflow('patch-and-verify', {
593
- on: [
594
- /* ... */
595
- ],
595
+ on: [/* ... */],
596
596
  jobs: [
597
597
  // Restart job: apply updates, then reboot. restartHost() MUST be the last step.
598
598
  job('patch', {
@@ -727,6 +727,12 @@ export const postDeploy = workflow('post-deploy', {
727
727
 
728
728
  `workflowComplete()` / `jobComplete()` start a **separate** workflow run that reacts to the prior one finishing, gated on its status. They are the right tool when a _different_ workflow should respond. When you instead need to add more jobs to the **same** run based on what a job just produced — fanning out follow-up work from a prior job's outputs — use a result-aware generator (next section), not a completion-event chain.
729
729
 
730
+ #### Failure notifier (any source → any destination)
731
+
732
+ Because the orchestrator auto-emits `workflow_complete` for **every** run, a single workflow with `on: workflowComplete({ status: ['failed'] })` (no `source` filter) becomes an org-wide failure notifier: every failed workflow, whatever repo or provider triggered it, dispatches this one workflow. The runnable example `examples/workflows/failed-workflow-slack-notifier.ts` builds exactly that — it maps the failed run's repo (`ctx.sourceRepo?.identifier`) through an inline `repo → { channel, tag }` table and posts to Slack with `fetch`, guarding the real POST behind `ctx.isTestRun` so a test run never messages a live channel. Swap the `fetch` body for Discord, Teams, PagerDuty, or a plain HTTP endpoint to change the destination — no external package required.
733
+
734
+ The one failure class this pattern cannot catch is a **dead orchestrator**: a workflow can only run while the orchestrator that would dispatch it is alive, so if the orchestrator itself is gone, nothing dispatches the notifier. Watching for an orchestrator that has stopped reporting is the managed notification plane's job, not a workflow's.
735
+
730
736
  ### Same-run discovery → fan-out
731
737
 
732
738
  A result-aware [`dynamicJob(group, { needs, generate })`](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/#dynamicjob--result-aware-generation) is deferred until its declared upstreams complete, then runs with their frozen outputs as `ctx.needs` — so a discovery job can emit a list at runtime and the generator fans out one follow-up job per item, all in the same run:
@@ -1261,7 +1267,7 @@ export const testSuite = workflow('test-suite', {
1261
1267
  await $`pnpm test`;
1262
1268
  }),
1263
1269
  step('emit-results', async (ctx) => {
1264
- await ctx.emit(testsPassedEvent.name, {
1270
+ await ctx.emit(testsPassedEvent, {
1265
1271
  branch: 'main',
1266
1272
  commit: 'abc123',
1267
1273
  testCount: 142,