@kici-dev/compiler 0.1.23 → 0.1.24

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 (41) hide show
  1. package/dist/cli.js +15 -5
  2. package/dist/commands/index.d.ts +4 -2
  3. package/dist/commands/index.js +3 -2
  4. package/dist/commands/init.js +2 -2
  5. package/dist/commands/pat.d.ts +27 -0
  6. package/dist/commands/pat.js +76 -0
  7. package/dist/commands/preview.d.ts +88 -0
  8. package/dist/commands/{test.js → preview.js} +15 -14
  9. package/dist/commands/run.d.ts +11 -1
  10. package/dist/commands/run.js +34 -7
  11. package/dist/commands/verify-attestation.d.ts +4 -1
  12. package/dist/commands/verify-attestation.js +26 -10
  13. package/dist/generators/secrets-dts.js +2 -0
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.js +2 -2
  16. package/dist/llm-context/llms-architecture.txt +3 -3
  17. package/dist/llm-context/llms-cli.txt +149 -26
  18. package/dist/llm-context/llms-features.txt +107 -5
  19. package/dist/llm-context/llms-full.txt +390 -46
  20. package/dist/llm-context/llms-getting-started.txt +6 -6
  21. package/dist/llm-context/llms-sdk.txt +125 -6
  22. package/dist/llm-context/llms.txt +7 -5
  23. package/dist/local-executor/index.js +2 -1
  24. package/dist/local-executor/job-runner.js +3 -3
  25. package/dist/lockfile/generator.d.ts +10 -2
  26. package/dist/lockfile/generator.js +106 -52
  27. package/dist/remote/history.d.ts +1 -1
  28. package/dist/remote/history.js +1 -1
  29. package/dist/remote/local-repo-identity.d.ts +32 -0
  30. package/dist/remote/local-repo-identity.js +74 -0
  31. package/dist/remote/prod-defaults.d.ts +8 -0
  32. package/dist/remote/prod-defaults.js +9 -1
  33. package/dist/templates/agents-md.d.ts +1 -1
  34. package/dist/templates/agents-md.js +2 -2
  35. package/dist/templates/package-json.js +1 -1
  36. package/dist/test-runner/step-context.d.ts +1 -1
  37. package/dist/test-runner/step-context.js +2 -1
  38. package/dist/types.d.ts +33 -6
  39. package/dist/types.js +5 -1
  40. package/package.json +4 -7
  41. package/sbom.spdx.json +35 -35
@@ -26,7 +26,7 @@ Why the lock file must stay in sync with workflow source, how to commit both tog
26
26
 
27
27
  ### [CLI reference](https://docs.kici.dev/user/cli-reference/)
28
28
 
29
- All CLI commands provided by `@kici-dev/compiler`: `kici compile` (with watch mode and check mode), `kici run` (local and remote execution), `kici test` (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.
29
+ 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.
30
30
 
31
31
  ### [Workflow patterns](https://docs.kici.dev/user/workflow-patterns/)
32
32
 
@@ -249,10 +249,10 @@ The lock file (`kici.lock.json`) is a JSON representation of your workflow that
249
249
 
250
250
  ## Preview trigger matching
251
251
 
252
- Use `kici test` to preview which workflows match a trigger event (dry-run, no execution):
252
+ Use `kici preview` to preview which workflows match a trigger event (dry-run, no execution):
253
253
 
254
254
  ```bash
255
- npx kici test pr:open
255
+ npx kici preview pr:open
256
256
  ```
257
257
 
258
258
  Expected output (simplified):
@@ -418,14 +418,14 @@ Or add the flag to your root `package.json`:
418
418
 
419
419
  ## Authoring KiCI workflows with LLM coding agents
420
420
 
421
- KiCI is LLM-ready by design. Because workflows are real, typed TypeScript, coding agents reason over the SDK's `.d.ts` signatures instead of guessing a bespoke YAML DSL — and they verify their own pipelines with the same `kici test` and `kici run local` loop you use, so there's no push-to-find-out round-trip. First-class agent context ships in the box, so an agent is briefed the moment it opens the project.
421
+ KiCI is LLM-ready by design. Because workflows are real, typed TypeScript, coding agents reason over the SDK's `.d.ts` signatures instead of guessing a bespoke YAML DSL — and they verify their own pipelines with the same `kici preview` and `kici run local` loop you use, so there's no push-to-find-out round-trip. First-class agent context ships in the box, so an agent is briefed the moment it opens the project.
422
422
 
423
423
  KiCI ships first-class context for LLM coding agents (Claude Code, Cursor, Aider, etc.). When you scaffold a project with `kici init`, the CLI writes `.kici/AGENTS.md`, a one-page briefing that tells the agent:
424
424
 
425
425
  - where the SDK type declarations live (`node_modules/@kici-dev/sdk/dist/index.d.ts`)
426
426
  - the five canonical authoring patterns with runnable examples
427
427
  - the anti-patterns that catch agents off-guard (no YAML, no `/dist/...` imports, no top-level `await`)
428
- - the local commands the agent should drive (`kici compile --check`, `kici test`, `kici run local`, `kici docs llm`)
428
+ - the local commands the agent should drive (`kici compile --check`, `kici preview`, `kici run local`, `kici docs llm`)
429
429
 
430
430
  If you don't want the file, pass `--no-agents-md` to `kici init`, or delete the file afterwards — KiCI never reads it at runtime.
431
431
 
@@ -516,7 +516,7 @@ If you're not sure, pick Docker / Podman.
516
516
 
517
517
  ## Looking for the laptop-only path?
518
518
 
519
- 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 test` and `kici run local` instead.
519
+ 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 local` instead.
520
520
 
521
521
  ---
522
522
 
@@ -2650,7 +2650,7 @@ const deploy = job('deploy', {
2650
2650
 
2651
2651
  **Important:** `needs` must be declared explicitly. Output chaining does not auto-infer dependencies -- you must list job dependencies in `needs` even if you access their outputs via `.result`.
2652
2652
 
2653
- Cross-job output chaining works in both local test mode (`kici test`) and remote pipeline execution. The orchestrator's needs-aware dispatch scheduler guarantees upstream jobs reach a terminal state before downstream jobs dispatch, and upstream outputs are transported to the downstream agent sandbox via the `upstreamJobOutputs` field on `job.dispatch`. See [needs-scheduler](https://docs.kici.dev/architecture/execution/needs-scheduler/) for the full dispatch semantics.
2653
+ Cross-job output chaining works in both local execution (`kici run local`) and remote pipeline execution. The orchestrator's needs-aware dispatch scheduler guarantees upstream jobs reach a terminal state before downstream jobs dispatch, and upstream outputs are transported to the downstream agent sandbox via the `upstreamJobOutputs` field on `job.dispatch`. See [needs-scheduler](https://docs.kici.dev/architecture/execution/needs-scheduler/) for the full dispatch semantics.
2654
2654
 
2655
2655
  ### Job dependencies (`needs`)
2656
2656
 
@@ -3316,6 +3316,88 @@ The caller never has to branch on outcome — `result.result` is always a `Bucke
3316
3316
 
3317
3317
  ---
3318
3318
 
3319
+ ## SDK reference: parallel
3320
+
3321
+ Source: https://docs.kici.dev/user/sdk/parallel/
3322
+
3323
+ `parallel([...steps], opts?)` runs a group of independent steps **concurrently**
3324
+ within one job, behind a join barrier: execution continues past the group only
3325
+ once every child has settled. Each child is its own observable step — it gets its
3326
+ own logs, status, timing, and retry — instead of being hidden inside one step's
3327
+ `Promise.all`.
3328
+
3329
+ ```ts
3330
+ import { workflow, job, step, parallel, push } from '@kici-dev/sdk';
3331
+
3332
+ export default workflow('ci', {
3333
+ on: push(),
3334
+ jobs: [
3335
+ job('checks', {
3336
+ runsOn: 'kici:os:linux',
3337
+ steps: [
3338
+ checkout,
3339
+ // lint, typecheck, and the unit tests have no ordering between them,
3340
+ // so they run together — the job's wall-clock is the slowest child,
3341
+ // not the sum of all three.
3342
+ parallel([lint, typecheck, unitTests], { failFast: true }),
3343
+ deploy,
3344
+ ],
3345
+ }),
3346
+ ],
3347
+ });
3348
+ ```
3349
+
3350
+ `parallel(...)` returns a `ParallelGroup` that sits in the ordinary flat
3351
+ `steps: [...]` array — there is no new `job` field. A group's children are
3352
+ **sequential steps only**; groups cannot be nested.
3353
+
3354
+ ## Options
3355
+
3356
+ `parallel(steps, opts?)` accepts:
3357
+
3358
+ - **`failFast?: boolean`** — default `true`. When a child fails, the in-flight
3359
+ siblings are cancelled immediately and the job fails. With `failFast: false`
3360
+ every child runs to completion first, then the job fails if any child failed.
3361
+ - **`maxParallel?: number`** — default unlimited. Caps how many children run at
3362
+ once; children waiting for a slot report a `pending` status until they launch.
3363
+ - **`name?: string`** — a label for the group's dashboard band.
3364
+
3365
+ A child marked `continueOnError: true` never trips fail-fast and never fails the
3366
+ job — it still shows a `failed` status badge, but the group treats it as
3367
+ non-fatal.
3368
+
3369
+ ## Statuses
3370
+
3371
+ Parallel steps introduce two step statuses:
3372
+
3373
+ - **`pending`** — a child queued behind `maxParallel`, not yet launched.
3374
+ - **`cancelled`** — a sibling aborted by fail-fast. A cancelled step is **not** a
3375
+ failure: only the child that actually failed fails the job; the cancelled
3376
+ siblings render in gray (distinct from the red failing step) on the dashboard.
3377
+
3378
+ Children may also complete **out of order** — the fastest child finishes first
3379
+ regardless of array position. A later sequential step can read a parallel child's
3380
+ `.result` after the barrier; children within a group cannot read each other's
3381
+ results (there is no ordering inside the group).
3382
+
3383
+ ## Scope: nests inside job-level fan-out
3384
+
3385
+ `parallel()`'s `failFast` / `maxParallel` are **step-group** scopes — they govern
3386
+ only the steps inside the group. They are a different layer from the **job-level**
3387
+ `failFast` / `maxParallel` on a matrix / `runsOnAll` fan-out, which govern how a
3388
+ job's child _jobs_ spread across the matrix or host roster. A `parallel()` group
3389
+ inside a fan-out job nests its concurrency inside each fan-out child.
3390
+
3391
+ ## Local vs remote execution
3392
+
3393
+ Run remotely (the orchestrator + agent), parallel children execute concurrently
3394
+ and each surfaces as its own dashboard step. `kici run local` executes the same
3395
+ children in array order in its single-process model — the results are identical,
3396
+ only the wall-clock and the live fail-fast cancellation differ. Use a remote run
3397
+ to observe the concurrent timeline.
3398
+
3399
+ ---
3400
+
3319
3401
  ## SDK reference: rules, matrix, dynamic jobs
3320
3402
 
3321
3403
  Source: https://docs.kici.dev/user/sdk/rules-matrix-dynamic/
@@ -4214,7 +4296,7 @@ interface StepContext<TInputs = Record<string, unknown>> {
4214
4296
  rawPayload?: Record<string, unknown>;
4215
4297
  /** Which git provider triggered this workflow (e.g. 'github', 'gitlab') */
4216
4298
  provider?: string;
4217
- /** Whether this execution was triggered by `kici test` (remote test run) */
4299
+ /** Whether this execution was triggered by `kici run remote` (developer-initiated remote run) */
4218
4300
  isTestRun: boolean;
4219
4301
  /** The resolved deployment environment name for this job (undefined without environment) */
4220
4302
  environment?: string;
@@ -4611,7 +4693,7 @@ Tools that require a file path on disk (sops `SOPS_AGE_KEY_FILE`, kubectl `KUBEC
4611
4693
 
4612
4694
  ### Local test mode secrets
4613
4695
 
4614
- When running `kici test`, you can provide secrets locally without an orchestrator.
4696
+ When running `kici preview`, you can provide secrets locally without an orchestrator.
4615
4697
 
4616
4698
  #### .kici/.secrets file
4617
4699
 
@@ -4639,10 +4721,10 @@ Override or supplement file-based secrets with CLI flags:
4639
4721
 
4640
4722
  ```bash
4641
4723
  # Inject flat secrets (repeatable)
4642
- kici test push --secret DEPLOY_TOKEN=my-token --secret API_KEY=my-key
4724
+ kici preview push --secret DEPLOY_TOKEN=my-token --secret API_KEY=my-key
4643
4725
 
4644
4726
  # Inject context-scoped secrets (repeatable)
4645
- kici test push --context production.DB_PASSWORD=prod-secret --context npm-publish.NPM_TOKEN=abc123
4727
+ kici preview push --context production.DB_PASSWORD=prod-secret --context npm-publish.NPM_TOKEN=abc123
4646
4728
  ```
4647
4729
 
4648
4730
  **Precedence:** CLI flags override `.kici/.secrets` file values. Context secrets are auto-flattened into `ctx.secrets` using the same merge logic as production (last context wins).
@@ -5197,6 +5279,7 @@ interface ScheduleConfigInput {
5197
5279
  cron: string; // Required: cron expression (5-field)
5198
5280
  timezone?: string; // Timezone for cron evaluation (default: 'UTC')
5199
5281
  description?: string; // Human-readable description of the schedule
5282
+ inputs?: DispatchInputsMap; // Optional: defaults-only typed inputs (see below)
5200
5283
  }
5201
5284
  ```
5202
5285
 
@@ -5207,6 +5290,41 @@ schedule({ cron: '0 9 * * 1', timezone: 'America/New_York' }); // Monday 9am ET
5207
5290
  schedule({ cron: '*/15 * * * *', description: 'health check every 15 min' });
5208
5291
  ```
5209
5292
 
5293
+ #### Schedule inputs (defaults-only)
5294
+
5295
+ A `schedule()` trigger may declare typed `inputs`. A cron or dashboard
5296
+ "run now" fire carries **no operator-supplied values**, so each input resolves
5297
+ from its declared **default** and is exposed to steps and rules as
5298
+ `ctx.dispatchInputs` — the same surface as [typed dispatch inputs](https://docs.kici.dev/user/sdk/triggers/#typed-dispatch-inputs).
5299
+
5300
+ Because there is no operator to supply a value, every schedule input must
5301
+ declare a `.default()` **or** be `.optional()`. An input that is neither is
5302
+ rejected at `kici compile` time.
5303
+
5304
+ ```typescript
5305
+ import { workflow, job, schedule, z } from '@kici-dev/sdk';
5306
+
5307
+ export default workflow('nightly', {
5308
+ on: schedule({
5309
+ cron: '0 3 * * *',
5310
+ inputs: { mode: z.enum(['full', 'quick']).default('full') },
5311
+ }),
5312
+ jobs: [
5313
+ job('build', {
5314
+ runsOn: 'default',
5315
+ run: async (ctx) => {
5316
+ ctx.log(`mode = ${ctx.dispatchInputs.mode}`); // "full" on every fire
5317
+ },
5318
+ }),
5319
+ ],
5320
+ });
5321
+ ```
5322
+
5323
+ You can also share a typed handle via `defineDispatchInputs(...)` and read it
5324
+ back with `.from(ctx)`, exactly as with `dispatch()`. The allowed input types
5325
+ are the same closed subset documented under
5326
+ [typed dispatch inputs](https://docs.kici.dev/user/sdk/triggers/#typed-dispatch-inputs).
5327
+
5210
5328
  ### lifecycle()
5211
5329
 
5212
5330
  Create a lifecycle trigger for cross-workflow orchestration events. Returns a frozen `LifecycleTriggerConfig`.
@@ -5535,7 +5653,7 @@ If `check()` throws while polling, the error is logged and polling continues —
5535
5653
 
5536
5654
  Source: https://docs.kici.dev/user/sdk-reference/
5537
5655
 
5538
- Reference documentation for `@kici-dev/sdk`. The reference is split across five pages by topic.
5656
+ Reference documentation for `@kici-dev/sdk`. The reference is split across the per-topic pages below.
5539
5657
 
5540
5658
  | Page | Covers |
5541
5659
  | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -5547,6 +5665,7 @@ Reference documentation for `@kici-dev/sdk`. The reference is split across five
5547
5665
  | [Runtime](https://docs.kici.dev/user/sdk/runtime/) | Types index, `StepContext`, secrets, and fixtures. |
5548
5666
  | [Idempotent helpers](https://docs.kici.dev/user/sdk/idempotent/) | `idempotent()`, `idempotentStep()`, and the check-mode-aware `checkStep()` — check / apply pattern with typed results on both the skipped and applied branches. |
5549
5667
  | [Wait-for helpers](https://docs.kici.dev/user/sdk/wait-for/) | `waitFor()` and `waitForStep()` — poll a condition on an interval, run an optional success action, recover gracefully on timeout. |
5668
+ | [Parallel steps](https://docs.kici.dev/user/sdk/parallel/) | `parallel()` — run independent steps concurrently within one job behind a join barrier, each as its own observable step, with `failFast` and `maxParallel` controls. |
5550
5669
 
5551
5670
  The `@kici-dev/sdk` package re-exports the entire surface from a single entry point. Pick what you need:
5552
5671
 
@@ -5569,6 +5688,99 @@ For the complete list of every named export (factory functions, triggers, rules,
5569
5688
 
5570
5689
  # CLI and authoring
5571
5690
 
5691
+ ## Drive KiCI from your coding agent
5692
+
5693
+ Source: https://docs.kici.dev/user/ai-agents/
5694
+
5695
+ KiCI ships a hosted **MCP server** so a coding agent (Claude Code, or any MCP
5696
+ client) can drive your CI directly: trigger runs, read a structured result,
5697
+ fetch the failing step's logs, cancel, and re-run — all under your own identity,
5698
+ org-scoped, and audited. There are no per-tool tokens to configure: point the
5699
+ agent at one URL with one credential and it's done.
5700
+
5701
+ The MCP exposes only what you can already do yourself through the `kici` CLI and
5702
+ the dashboard. It is not a new privileged surface — every tool maps to an
5703
+ existing user-facing operation and is gated by the same permissions your role
5704
+ grants.
5705
+
5706
+ ## 1. Mint an agent token
5707
+
5708
+ The MCP accepts **only** an agent-kind personal access token (PAT). Mint one with
5709
+ the `kici` CLI (log in first with `kici login`):
5710
+
5711
+ ```bash
5712
+ kici pat create --agent --name "claude-code"
5713
+ ```
5714
+
5715
+ The `--name` value is the **agent label**. It is recorded on every action the
5716
+ agent takes, so your audit log shows exactly which agent did what (and on whose
5717
+ behalf). The token is printed once — save it now; it cannot be retrieved later.
5718
+
5719
+ An agent PAT inherits your permissions unchanged — it carries provenance, not
5720
+ extra authority. Powerful operator capabilities (secret rotation, agent and peer
5721
+ management, draining) are intentionally **not** exposed here.
5722
+
5723
+ ## 2. Point your coding agent at the MCP server
5724
+
5725
+ Configure your MCP client with the KiCI MCP endpoint and the agent PAT as a
5726
+ Bearer credential. The endpoint is the hosted Platform URL plus `/api/v1/mcp`.
5727
+
5728
+ For Claude Code, add a remote MCP server whose URL is your KiCI Platform's
5729
+ `/api/v1/mcp` and whose `Authorization` header is `Bearer <your-agent-pat>`.
5730
+
5731
+ That's the entire setup. The agent can now call the tools below.
5732
+
5733
+ ## 3. What the agent can do
5734
+
5735
+ **Read**
5736
+
5737
+ - `list_runs` — recent runs in your organization.
5738
+ - `get_run` — the structured, provenance-tagged result of a run: the typed job
5739
+ graph, per-step statuses and exit codes, durations, and a derived failure
5740
+ category.
5741
+ - `get_step_logs` — the log lines for a specific step.
5742
+ - `list_workflows` — your registered workflows.
5743
+
5744
+ **Drive**
5745
+
5746
+ - `trigger_run` — run a registered workflow ("run now").
5747
+ - `rerun_run` — re-run a completed run.
5748
+ - `cancel_run` — cancel an in-progress run.
5749
+
5750
+ If you belong to a single organization, the org is resolved automatically. If
5751
+ you belong to several, pass an `orgId` argument to any tool.
5752
+
5753
+ ## 4. Why the structured result is agent-safe
5754
+
5755
+ `get_run` and `get_step_logs` return a machine-first shape designed for an agent
5756
+ to reason over without being misled by repository content. Every field that
5757
+ comes from your repo, a contributor, or a process's output — workflow and job
5758
+ names, refs, error messages, log lines, job outputs — is wrapped in an
5759
+ `{ untrusted: true, value: … }` envelope. KiCI-generated values (ids, statuses,
5760
+ exit codes, durations, the derived failure category) are left plain. An agent can
5761
+ keep user-controlled content out of its instruction channel by refusing to act
5762
+ on anything tagged `untrusted`.
5763
+
5764
+ Secret values are never returned — only the names of the secret keys a step
5765
+ accessed.
5766
+
5767
+ ## 5. The audit guarantee
5768
+
5769
+ Because the MCP accepts only an agent-kind PAT, **every action that flows through
5770
+ it is agent-attributed by construction** — there is no path that produces an
5771
+ untagged, human-looking action. Each read and each drive operation is recorded in
5772
+ your orchestrator's access log under your identity plus the agent label, so you
5773
+ always have a complete trail of what your agent did.
5774
+
5775
+ Inspect that trail with `kici-admin access-log list --json` (or
5776
+ `kici-admin access-log show <id>` for one entry). An agent-attributed row keeps
5777
+ `actor_type` as `user` and `actor_id` as your own identity — the agent provenance
5778
+ rides in the row's actor metadata as `agentLabel` (the `--name` you minted the
5779
+ PAT with) and `agentPatId` (the token that acted). The label is also stored in a
5780
+ dedicated `agent_label` column on every such row.
5781
+
5782
+ ---
5783
+
5572
5784
  ## CLI authentication
5573
5785
 
5574
5786
  Source: https://docs.kici.dev/user/cli-auth/
@@ -5851,7 +6063,7 @@ Run commands with `npx kici` or add scripts to your `package.json`:
5851
6063
  {
5852
6064
  "scripts": {
5853
6065
  "kici:compile": "kici compile",
5854
- "kici:test": "kici test"
6066
+ "kici:preview": "kici preview"
5855
6067
  }
5856
6068
  }
5857
6069
  ```
@@ -5946,6 +6158,8 @@ kici run local [event] [options]
5946
6158
  | `--kici-dir <path>` | `.kici` | Path to .kici directory |
5947
6159
  | `--in-place` | `false` | Run against the real working directory instead of an isolated tmp checkout (see "Execution isolation" below) |
5948
6160
  | `--keep` | `false` | Always retain the isolated tmp checkout (default: keep only on failure) |
6161
+ | `--check` | `false` | Run in [check mode](https://docs.kici.dev/user/idempotent-steps/): each step reports drift instead of applying; the run still exits 0 |
6162
+ | `--fail-on-drift` | `false` | In check mode, exit non-zero if any step reports drift (no effect without `--check`) |
5949
6163
 
5950
6164
  **Interactive workflow selection (`--pick` / `-p`):**
5951
6165
 
@@ -6087,6 +6301,10 @@ kici run remote [fixture] [options]
6087
6301
  | `--target-allow-empty` | `false` | A `--target` that narrows a `runsOnAll` job to zero hosts skips it instead of failing |
6088
6302
  | `--debug` | `false` | Verbose internals |
6089
6303
  | `--kici-dir <path>` | `.kici` | Path to .kici directory |
6304
+ | `--routing-key <key>` | none | Override the routing key for this run (advanced; selecting the org normally suffices — see [How the run is routed](https://docs.kici.dev/user/cli-reference/#how-the-run-is-routed)) |
6305
+ | `--check` | `false` | Run in [check mode](https://docs.kici.dev/user/idempotent-steps/): each step reports drift instead of applying |
6306
+ | `--fail-on-drift` | `false` | In check mode, fail the run if any step reports drift (no effect without `--check`) |
6307
+ | `--approve-all, --yes` | `false` | Auto-approve every [approval gate](https://docs.kici.dev/user/approvals/) this run holds on (run-scoped; eligibility still enforced) |
6090
6308
 
6091
6309
  **Examples:**
6092
6310
 
@@ -6252,12 +6470,12 @@ kici orchestrators use us-east
6252
6470
  kici orchestrators use us-east --org xyz789ghi012
6253
6471
  ```
6254
6472
 
6255
- ### kici test
6473
+ ### kici preview
6256
6474
 
6257
6475
  Preview which workflows match a trigger event (dry-run, no execution). Useful for verifying trigger configurations during development.
6258
6476
 
6259
6477
  ```bash
6260
- kici test [event] [options]
6478
+ kici preview [event] [options]
6261
6479
  ```
6262
6480
 
6263
6481
  **Arguments:**
@@ -6284,19 +6502,19 @@ kici test [event] [options]
6284
6502
 
6285
6503
  ```bash
6286
6504
  # Preview which workflows match a push event
6287
- kici test push
6505
+ kici preview push
6288
6506
 
6289
6507
  # Preview PR trigger matching
6290
- kici test pr:open
6508
+ kici preview pr:open
6291
6509
 
6292
6510
  # Preview with branch override
6293
- kici test push --branch develop
6511
+ kici preview push --branch develop
6294
6512
 
6295
6513
  # Filter to specific workflow
6296
- kici test push --workflow ci
6514
+ kici preview push --workflow ci
6297
6515
 
6298
6516
  # Simulate changed files for path-filtered triggers
6299
- kici test push --files src/index.ts --files README.md
6517
+ kici preview push --files src/index.ts --files README.md
6300
6518
  ```
6301
6519
 
6302
6520
  **Exit codes:**
@@ -6306,7 +6524,7 @@ kici test push --files src/index.ts --files README.md
6306
6524
  | 0 | Preview completed (including zero matches) |
6307
6525
  | 1 | Error |
6308
6526
 
6309
- **Migration from old `kici test <fixture>`:** If you were using `kici test <fixture-name>` for remote fixture execution, use `kici run remote <fixture-name>` instead. For local workflow execution, use `kici run local <event>`.
6527
+ **Migration from the old `test` command:** The dry-run preview command was renamed from `test` to `preview`. If you were using the old `test` command with a fixture name for remote fixture execution, use `kici run remote <fixture-name>` instead. For local workflow execution, use `kici run local <event>`.
6310
6528
 
6311
6529
  ### kici login
6312
6530
 
@@ -6683,6 +6901,27 @@ Only key names are shown — secret values are never returned over this endpoint
6683
6901
 
6684
6902
  **Prerequisites:** authenticate via `kici login` and select an active organization with `kici org use <name>`.
6685
6903
 
6904
+ ### kici pat create
6905
+
6906
+ Mint a personal access token under your own identity. Pass `--agent` to mint an
6907
+ **agent-kind** PAT — the credential a coding agent points the KiCI MCP server at.
6908
+
6909
+ ```bash
6910
+ kici pat create --agent --name "claude-code"
6911
+ ```
6912
+
6913
+ - `--agent` marks the token as agent-kind. An agent PAT inherits your
6914
+ permissions unchanged (it carries provenance, not extra authority) and is the
6915
+ **only** credential the MCP server accepts.
6916
+ - `--name <label>` sets the token name. For an agent PAT this is the **agent
6917
+ label** recorded on every action the agent takes — required with `--agent`.
6918
+ - `--expires-in-days <n>` overrides the default expiry.
6919
+
6920
+ The token is printed once — save it immediately; it cannot be retrieved later.
6921
+ See [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/) for the full setup.
6922
+
6923
+ **Prerequisites:** authenticate via `kici login` first.
6924
+
6686
6925
  ### kici types
6687
6926
 
6688
6927
  Generate TypeScript declaration files from orchestrator environment metadata. The generated `.d.ts` file augments the SDK's `KnownSecretKeys` and `EnvironmentSecrets` interfaces, providing compile-time autocomplete and type checking for secret key names.
@@ -7001,7 +7240,7 @@ kici admin drain-worker --url http://worker-2.internal:10143
7001
7240
  Verify a KiCI build-provenance attestation bundle offline. A bundle is the signed package a workflow step produces via `ctx.attestProvenance(...)`: a DSSE-wrapped SLSA in-toto statement, the ephemeral public key that signed it, and the KiCI identity token that anchors the build context. For the end-to-end attest → verify → view journey, see the [build provenance guide](https://docs.kici.dev/user/provenance/). Verification establishes the full chain — the identity token verifies against the trusted issuer's JWKS, the DSSE signature verifies against the bundled key, and the statement's build context must match the token's claims (a mismatch is a hard failure). When an `[artifact]` is given, its SHA-256 digest is also matched against the attestation subject.
7002
7241
 
7003
7242
  ```bash
7004
- kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-file> [options]
7243
+ kici verify-attestation [artifact] --bundle <path-or-url> [--trust-root <url-or-file>] [options]
7005
7244
  ```
7006
7245
 
7007
7246
  **Arguments:**
@@ -7012,14 +7251,14 @@ kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-f
7012
7251
 
7013
7252
  **Options:**
7014
7253
 
7015
- | Option | Required | Description |
7016
- | ---------------------------- | -------- | ----------------------------------------------------------------------------------------- |
7017
- | `--bundle <path-or-url>` | yes | Path or `http(s)` URL to the attestation bundle JSON. |
7018
- | `--trust-root <url-or-file>` | yes | Trusted issuer (see below). The token issuer is pinned to it, never taken from the token. |
7019
- | `--audience <aud>` | no | Expected token audience (defaults to the KiCI provenance audience). |
7020
- | `--json` | no | Print the structured verification result as JSON instead of human-readable output. |
7254
+ | Option | Required | Description |
7255
+ | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
7256
+ | `--bundle <path-or-url>` | yes | Path or `http(s)` URL to the attestation bundle JSON. |
7257
+ | `--trust-root <url-or-file>` | no | Trusted issuer (see below). Defaults to the hosted KiCI platform. The token issuer is pinned to it, never taken from the token. |
7258
+ | `--audience <aud>` | no | Expected token audience (defaults to the KiCI provenance audience). |
7259
+ | `--json` | no | Print the structured verification result as JSON instead of human-readable output. |
7021
7260
 
7022
- **Trust root:** the verifier never trusts the issuer named inside the token you supply the trusted issuer out-of-band via `--trust-root`, in one of two forms:
7261
+ **Trust root:** `--trust-root` defaults to the hosted KiCI platform's provenance issuer — the same platform you `kici login` against (see [Which trust root do I use?](https://docs.kici.dev/user/provenance/#which-trust-root-do-i-use)), so the common case needs no flag. The verifier never trusts the issuer named inside the token; supplying it out-of-band is what prevents a forged bundle from self-attesting. To override the default, pass `--trust-root` in one of two forms:
7023
7262
 
7024
7263
  - **Online — an HTTPS issuer URL.** The verifier fetches `<url>/.well-known/openid-configuration`, reads its `issuer` and `jwks_uri`, and fetches the JWKS. The token's `iss` is pinned to the discovery document's `issuer`.
7025
7264
  - **Offline — a self-contained trust-root file.** A local JSON file with the issuer and JWKS inlined, so no network access is needed (air-gapped verification):
@@ -7038,7 +7277,10 @@ kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-f
7038
7277
  **Examples:**
7039
7278
 
7040
7279
  ```bash
7041
- # Online: verify a bundle against a deployed issuer, digest-checking the artifact
7280
+ # Default: verify against the hosted KiCI platform (no --trust-root needed)
7281
+ kici verify-attestation ./dist/app.tgz --bundle ./app.tgz.kici.json
7282
+
7283
+ # Override: verify a bundle against a specific issuer, digest-checking the artifact
7042
7284
  kici verify-attestation ./dist/app.tgz \
7043
7285
  --bundle ./app.tgz.kici.json \
7044
7286
  --trust-root https://platform.example/issuer
@@ -7055,10 +7297,10 @@ kici verify-attestation --bundle ./app.tgz.kici.json \
7055
7297
 
7056
7298
  **Exit codes:**
7057
7299
 
7058
- | Code | Meaning |
7059
- | ---- | ----------------------------------------------------------------------------------- |
7060
- | 0 | Verified — signature, identity, build context (and digest, if checked) all pass |
7061
- | 1 | Not verified, or an error (missing flag, unreadable bundle, unreachable trust root) |
7300
+ | Code | Meaning |
7301
+ | ---- | ----------------------------------------------------------------------------------------- |
7302
+ | 0 | Verified — signature, identity, build context (and digest, if checked) all pass |
7303
+ | 1 | Not verified, or an error (missing `--bundle`, unreadable bundle, unreachable trust root) |
7062
7304
 
7063
7305
  ## Workflow discovery
7064
7306
 
@@ -7100,7 +7342,7 @@ All commands follow a consistent exit code convention:
7100
7342
 
7101
7343
  ## Debug output
7102
7344
 
7103
- Use `--debug` (on `kici run local`, `kici run remote`, `kici test`) or `--verbose` (on `kici compile`) for detailed output:
7345
+ Use `--debug` (on `kici run local`, `kici run remote`, `kici preview`) or `--verbose` (on `kici compile`) for detailed output:
7104
7346
 
7105
7347
  ```bash
7106
7348
  # Shows trigger matching, rule evaluation, decision traces
@@ -7110,7 +7352,7 @@ kici run local push --debug
7110
7352
  kici compile --verbose
7111
7353
 
7112
7354
  # Shows trigger matching preview
7113
- kici test pr:open --debug
7355
+ kici preview pr:open --debug
7114
7356
  ```
7115
7357
 
7116
7358
  Set `KICI_DEBUG=true` for additional internal debug output across all commands.
@@ -7391,7 +7633,7 @@ The lock file (`kici.lock.json`) is a JSON file with the following top-level fie
7391
7633
 
7392
7634
  | Field | Description |
7393
7635
  | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7394
- | `schemaVersion` | Lock file schema version (currently 20). Incremented on breaking format changes. |
7636
+ | `schemaVersion` | Lock file schema version (currently 29). Incremented on breaking format changes. |
7395
7637
  | `source` | Reference to the source file and export (e.g., `{ file: “.kici/workflows/ci.ts”, export: “#default” }`). |
7396
7638
  | `contentHash` | SHA-256 of the serialized lock file content (excluding itself). Changes when any workflow, trigger, or job changes. |
7397
7639
  | `lockfileHash` | SHA-256 of the detected package manager's lockfile, used as the dependency cache key. The lockfile is `.kici/package-lock.json` for npm, or the repo-root `pnpm-lock.yaml` / `yarn.lock` for a pnpm/yarn workspace; the hash input is prefixed with the manager name so a manager change is a guaranteed cache miss. Omitted when no lockfile exists. |
@@ -7514,7 +7756,7 @@ Test your workflows remotely against the full CI pipeline from your local machin
7514
7756
  - Give test runs test-scoped secrets — your local secret files and `--env` values (uploaded encrypted) plus any environment flagged `allowLocalExecution: true` — while production environments stay unreachable
7515
7757
  - Detect test mode in workflow code via `ctx.isTestRun`
7516
7758
 
7517
- The command is remote-only -- all execution happens on the orchestrator and agent. For local-only trigger matching previews, use `kici test <event>`.
7759
+ The command is remote-only -- all execution happens on the orchestrator and agent. For local-only trigger matching previews, use `kici preview <event>`.
7518
7760
 
7519
7761
  :::note[Orchestrator prerequisite: cache storage]
7520
7762
  `kici run remote` uploads your working-tree overlay to the orchestrator's **cache storage** via a pre-signed URL, and the agent fetches it from there (see [Repo state transfer](https://docs.kici.dev/user/testing-guide/#repo-state-transfer)). The target orchestrator must therefore have cache storage enabled (`KICI_STORAGE_TYPE` = `s3` or `filesystem`).
@@ -8754,6 +8996,36 @@ job('deploy-review', {
8754
8996
 
8755
8997
  A pure function like the one above (see [Dynamic values](https://docs.kici.dev/user/dynamic-values/)) is evaluated inline at dispatch with no init-job overhead. Dynamic environments that match a glob pattern (e.g., `review/*`) inherit the pattern's configuration, variables, and protection rules.
8756
8998
 
8999
+ ### Multiple environments per job
9000
+
9001
+ A job can bind more than one environment with `environments`, an ordered array. This lets a single job draw secrets and variables from several environments at once — for example a shared `staging` environment plus a `my-testing` environment that carries test-only variables:
9002
+
9003
+ ```typescript
9004
+ job('deploy', {
9005
+ runsOn: 'default',
9006
+ environments: ['staging', 'my-testing'],
9007
+ steps: [
9008
+ step('deploy', async (ctx) => {
9009
+ // ctx.secrets and ctx.env carry the merged set from both environments
9010
+ const dbUrl = await ctx.secrets.get('DB_URL');
9011
+ }),
9012
+ ],
9013
+ });
9014
+ ```
9015
+
9016
+ - `environment` (singular) and `environments` (array) are mutually exclusive — setting both is a compile error. `environment: 'staging'` is exactly equivalent to `environments: ['staging']`.
9017
+ - Each array entry is a static name or a function of the event, resolved per element exactly like a single dynamic environment.
9018
+
9019
+ **Merge order — last wins.** All bound environments are resolved on every dispatch (webhook, scheduled, and test runs alike) and merged in array order. When the same secret or variable key is defined in more than one environment, the later entry in the array wins. With `environments: ['staging', 'my-testing']`, a key defined in both resolves to `my-testing`'s value; keys defined in only one are preserved. The longest-scope-path-wins rule still applies _within_ each environment.
9020
+
9021
+ **Protection rules combine all-must-pass.** A job must satisfy **every** bound environment's gates — adding an environment can never loosen access. Branch restrictions, trigger-type filters, and repo patterns must pass for all environments; the minimum trust tier is the most restrictive across them; required reviewers are the union of all environments' reviewers; the wait timer is the longest; and the hold expiry is the shortest. If a run is gated out, the rejection names which environment and which rule rejected it (visible via `kici status` and the run's rejection reason), so a mutually-exclusive set of rules surfaces as a clear failure rather than a silent perpetual rejection.
9022
+
9023
+ **Skip-on-test.** On a test or local run (`kici run remote`, `kici run local`), any bound environment that disallows local execution is skipped — its variables and secrets are omitted from the merge and its gates are not evaluated. This makes the test-only-variables pattern work: with `environments: ['staging', 'my-testing']` where only `my-testing` allows local execution, a test run resolves just `my-testing`'s variables. If every bound environment disallows test runs, the job runs with no environment variables and a clear warning.
9024
+
9025
+ **Unconfigured environments contribute nothing at dispatch.** At dispatch time a bound environment name with no matching configured environment (and no matching glob environment) simply adds no variables, secrets, or protection rules — the job still runs, exactly as a single dynamic environment resolving to an as-yet-unconfigured name does today.
9026
+
9027
+ **Registration rejects a provably-unsatisfiable binding.** When a workflow is registered, KiCI statically checks every multi-environment binding: a bound environment that does not exist, a disabled one, or two environments with mutually-exclusive fixed branch / trigger-type / repository restrictions (no value can satisfy both) makes the binding provably unsatisfiable, and the registration is rejected with a precise message naming the job, the environments, and the rule — for example `unsatisfiable environment binding: job 'deploy' binds environments [staging, my-testing] with mutually exclusive branch restrictions (no value satisfies all bound environments)`. Bindings whose restrictions use globs are undecidable at registration and fall through to the dispatch-time gate check instead.
9028
+
8757
9029
  ### Job-level environment variables
8758
9030
 
8759
9031
  The `env` property on a job provides static or dynamic environment variables:
@@ -8793,7 +9065,7 @@ job('deploy', {
8793
9065
  });
8794
9066
  ```
8795
9067
 
8796
- If no `concurrencyGroup` is specified, the environment name is used as the default concurrency group.
9068
+ If no `concurrencyGroup` is specified, the environment name is used as the default concurrency group. For a job bound to multiple environments, the default is the **first** bound environment's name.
8797
9069
 
8798
9070
  ### Step context
8799
9071
 
@@ -8934,6 +9206,12 @@ Each environment has four tabs:
8934
9206
 
8935
9207
  4. **History** -- view filtered runs targeting this environment.
8936
9208
 
9209
+ ### Bound environments on runs
9210
+
9211
+ A job's bound deployment environments are shown as chips on the run detail page (in the job metadata panel) in the order the job declared them, and the distinct set across a run's jobs appears as compact chips on the run list. For a multi-environment job the chips read left-to-right in merge order — later environments override earlier ones on key collisions. A `(dynamic)` chip marks an environment whose name is computed at runtime; it resolves to the real name once the run starts. A job that binds a single environment shows one chip; a job that binds none shows no chip.
9212
+
9213
+ If a multi-environment binding is gated out, the run's failure banner names which environment and which rule rejected it (the same all-must-pass detail surfaced by `kici status`).
9214
+
8937
9215
  ### Secrets management
8938
9216
 
8939
9217
  Secrets are individual encrypted values organized by scope paths (e.g., `aws/prod`, `databases/postgres`). Scopes are bound to environments via bindings:
@@ -9957,11 +10235,34 @@ statement's build context must match the token's identity claims (a mismatch is
9957
10235
  a hard failure).
9958
10236
 
9959
10237
  ```bash
9960
- kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-file>
10238
+ kici verify-attestation [artifact] --bundle <path-or-url> [--trust-root <url-or-file>]
9961
10239
  ```
9962
10240
 
9963
- You supply the trusted issuer out-of-band via `--trust-root` the verifier
9964
- never trusts the issuer named inside the token. There are two forms:
10241
+ ### Which trust root do I use?
10242
+
10243
+ The trust root is the **KiCI platform's provenance issuer** — the same hosted
10244
+ KiCI platform you `kici login` against. KiCI attestations are issued by, and
10245
+ verified against, that one issuer; there are no competing "roots" to choose
10246
+ between. So the answer to "shouldn't I just use KiCI as the trust root?" is yes
10247
+ — and that's the **default**: omit `--trust-root` and the verifier checks the
10248
+ bundle against the hosted KiCI platform automatically. You only pass
10249
+ `--trust-root` to verify against a different environment or, more commonly, an
10250
+ offline `{ issuer, jwks }` file for air-gapped checks.
10251
+
10252
+ ### Why you supply it out-of-band
10253
+
10254
+ Given there's a single issuer, why pass it at all instead of letting the
10255
+ verifier read it from the token? Because the issuer named **inside** a token
10256
+ cannot be trusted: a forged bundle could carry a token that names
10257
+ `iss: https://attacker.example` _and_ bundle a key set that "verifies" it,
10258
+ making the whole signature chain circular and self-attesting. The verifier
10259
+ therefore pins to an issuer you supply out-of-band and checks the token against
10260
+ _that_ — the bundle is verified against a key set you trust, not one it shipped
10261
+ with. Naming the trust root is a security requirement, not a multiple-choice
10262
+ question.
10263
+
10264
+ To override the default, supply the trusted issuer via `--trust-root`, in one of
10265
+ two forms:
9965
10266
 
9966
10267
  - **Online — an HTTPS issuer URL.** The verifier fetches
9967
10268
  `<url>/.well-known/openid-configuration`, reads its `issuer` and `jwks_uri`,
@@ -9989,7 +10290,10 @@ when it does not (or on an error such as a missing flag or unreachable trust
9989
10290
  root).
9990
10291
 
9991
10292
  ```bash
9992
- # Verify a bundle against a deployed issuer, digest-checking the artifact:
10293
+ # Default: verify against the hosted KiCI platform (no --trust-root needed):
10294
+ kici verify-attestation ./dist/app.tgz --bundle ./app.tgz.kici.json
10295
+
10296
+ # Override the trust root to verify against a specific issuer:
9993
10297
  kici verify-attestation ./dist/app.tgz \
9994
10298
  --bundle ./app.tgz.kici.json \
9995
10299
  --trust-root https://platform.example/issuer
@@ -10028,6 +10332,44 @@ attestations shows an empty state.
10028
10332
 
10029
10333
  <!-- /help:run-attestations -->
10030
10334
 
10335
+ ## Browsing attestations across runs
10336
+
10337
+ The **Attestations** page (in the org sidebar) lists every build-provenance
10338
+ attestation your organization has produced — not just one run's. It is the
10339
+ supply-chain audit surface: look up "who built `sha256:…`?" by digest, or browse
10340
+ and filter every attestation across all runs.
10341
+
10342
+ <!-- help:attestations-list#browsing-attestations-across-runs -->
10343
+
10344
+ The **Attestations** page lists every build-provenance attestation your organization has produced.
10345
+
10346
+ - **Search** by artifact digest (exact `sha256:…`) or name to trace a specific artifact.
10347
+ - **Filter** by verification status, repository, workflow, job, or date.
10348
+ - Each row's **status badge** is the verdict KiCI recorded when the attestation was produced (`verified`, `failed`, `unverifiable`, or `pending`).
10349
+
10350
+ Open a row for the parsed provenance statement and a live re-verification.
10351
+
10352
+ <!-- /help:attestations-list -->
10353
+
10354
+ The status badge here is the **server-side verdict**, computed once when the
10355
+ attestation was recorded (verify-at-ingest) — so the list stays fast at any
10356
+ size. `verified` means the signature, build identity, and build context all
10357
+ checked out against the provenance issuer; `failed` means verification ran and
10358
+ the bundle did not pass; `unverifiable` means no verdict could be computed (no
10359
+ provenance issuer configured, or its keys could not be read — not a forgery
10360
+ signal); `pending` means the verdict has not been computed yet.
10361
+
10362
+ Opening a row leads to the **attestation detail page**:
10363
+
10364
+ <!-- help:attestation-detail#browsing-attestations-across-runs -->
10365
+
10366
+ This page shows the parsed provenance for one attestation.
10367
+
10368
+ - **Builder identity, source, and build type** come from the signed SLSA statement.
10369
+ - The **stored badge** is the verdict recorded at build time; **Re-verify** runs the check live in your browser against the current signing keys.
10370
+ - **Download** exports the signed bundle for offline verification with `kici verify-attestation`.
10371
+ <!-- /help:attestation-detail -->
10372
+
10031
10373
  ## See also
10032
10374
 
10033
10375
  - [SDK runtime reference](https://docs.kici.dev/user/sdk/runtime/) — the `ctx.attestProvenance` and
@@ -10049,6 +10391,8 @@ Secrets are managed per-environment in the orchestrator (see [operator docs](htt
10049
10391
 
10050
10392
  This design prevents accidental secret leakage through child processes, log output, or error messages. Only secrets you explicitly request are loaded into memory.
10051
10393
 
10394
+ A job can bind several environments with `environments: ['staging', 'my-testing']`; the secret keys from all bound environments are merged in array order, with a later environment's value winning on a key collision. See [Multiple environments per job](https://docs.kici.dev/user/environments/#multiple-environments-per-job).
10395
+
10052
10396
  ## Where secret values come from
10053
10397
 
10054
10398
  Secret values are written either through the dashboard or through `kici-admin` running against the orchestrator. The orchestrator operator decides — per organization — which surface accepts secret writes. From the workflow author's perspective, the resolution path at run time is identical either way; the difference is where you (or your ops team) **enter** the value.
@@ -11520,7 +11864,7 @@ Source: https://docs.kici.dev/architecture/data-flows/
11520
11864
 
11521
11865
  This document describes the key data flows through the KiCI architecture: webhook delivery, job execution, developer-initiated remote runs, dependency caching, re-run and cancel, trace ID propagation, internal event routing, and generic webhook ingestion.
11522
11866
 
11523
- > **Lock file schema version:** The lock file uses schema version 21, which adds the `CheckMode` / `CheckStepOutcome` enums for check-mode step execution on top of v20's `LabelMatcher` (exact/regex) selectors for `runsOn`/`runsOnAll`/`excludeLabels`, v19's `maxParallel`/`failFast` fan-out concurrency, v18's `runsOnAll` host fan-out predicate and `onUnreachable` policy, v17's typed init presets (`mise` / `{ mise }`) and `auto` detection, v16's normalized approval config, v15's per-job init config, v14's declarative cache specs, v11's `LockInlineValue` for pure function inline evaluation, v10's simplified negative patterns (! prefix in repos/paths arrays), v9's global workflow repos matching, and v8's runsOn polymorphic type support.
11867
+ > **Lock file schema version:** The lock file uses schema version 29. The orchestrator rejects any fetched lock whose `schemaVersion` does not exactly match the engine version it was built against, so a stale lock must be recompiled with `kici compile` and pushed again after any SDK upgrade that bumps the schema.
11524
11868
 
11525
11869
  ## Webhook delivery flow
11526
11870
 
@@ -11726,7 +12070,7 @@ Dep cache misses alone do **not** trigger a build job. Deps are platform-specifi
11726
12070
 
11727
12071
  ### Cross-source / no-contentHash workflows
11728
12072
 
11729
- - **Lock files without `contentHash`** (schema v1) skip the source cache entirely; agents compile from source. Regenerate lock files with `kici compile` to enable caching. The current lock file schema version is 21.
12073
+ - **Lock files without `contentHash`** (schema v1) skip the source cache entirely; agents compile from source. Regenerate lock files with `kici compile` to enable caching. The current lock file schema version is 29.
11730
12074
  - **Cross-source / global-workflow dispatch** (a workflow registered against source A fired by a webhook on source B) bypasses both caches. The registration's lock file entry still carries `contentHash`, but the cross-source path always clone-and-installs — the eval temp dir doesn't ship `@kici-dev/sdk`. The execution agent still verifies `contentHash` against the cloned source for drift detection.
11731
12075
 
11732
12076
  ### Build deduplication
@@ -12294,7 +12638,7 @@ The compiler processes the workflow definition:
12294
12638
 
12295
12639
  ### Execution time (local test runner)
12296
12640
 
12297
- When `kici test` runs a workflow:
12641
+ When `kici run local` runs a workflow:
12298
12642
 
12299
12643
  1. **SDK module resolution:** The runner resolves `setStepOutputsMap` / `setJobOutputsMap` from the same `@kici-dev/sdk` module instance that the workflow uses (ensures the proxy reads from the same map)
12300
12644
  2. **Map injection:** Fresh `OutputsMap` and `StepRefMap` are created and injected via `setStepOutputsMap()` / `setStepRefMap()` before each job