@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
@@ -2,6 +2,99 @@
2
2
 
3
3
  This bundle covers: Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift.
4
4
 
5
+ ## Drive KiCI from your coding agent
6
+
7
+ Source: https://docs.kici.dev/user/ai-agents/
8
+
9
+ KiCI ships a hosted **MCP server** so a coding agent (Claude Code, or any MCP
10
+ client) can drive your CI directly: trigger runs, read a structured result,
11
+ fetch the failing step's logs, cancel, and re-run — all under your own identity,
12
+ org-scoped, and audited. There are no per-tool tokens to configure: point the
13
+ agent at one URL with one credential and it's done.
14
+
15
+ The MCP exposes only what you can already do yourself through the `kici` CLI and
16
+ the dashboard. It is not a new privileged surface — every tool maps to an
17
+ existing user-facing operation and is gated by the same permissions your role
18
+ grants.
19
+
20
+ ## 1. Mint an agent token
21
+
22
+ The MCP accepts **only** an agent-kind personal access token (PAT). Mint one with
23
+ the `kici` CLI (log in first with `kici login`):
24
+
25
+ ```bash
26
+ kici pat create --agent --name "claude-code"
27
+ ```
28
+
29
+ The `--name` value is the **agent label**. It is recorded on every action the
30
+ agent takes, so your audit log shows exactly which agent did what (and on whose
31
+ behalf). The token is printed once — save it now; it cannot be retrieved later.
32
+
33
+ An agent PAT inherits your permissions unchanged — it carries provenance, not
34
+ extra authority. Powerful operator capabilities (secret rotation, agent and peer
35
+ management, draining) are intentionally **not** exposed here.
36
+
37
+ ## 2. Point your coding agent at the MCP server
38
+
39
+ Configure your MCP client with the KiCI MCP endpoint and the agent PAT as a
40
+ Bearer credential. The endpoint is the hosted Platform URL plus `/api/v1/mcp`.
41
+
42
+ For Claude Code, add a remote MCP server whose URL is your KiCI Platform's
43
+ `/api/v1/mcp` and whose `Authorization` header is `Bearer <your-agent-pat>`.
44
+
45
+ That's the entire setup. The agent can now call the tools below.
46
+
47
+ ## 3. What the agent can do
48
+
49
+ **Read**
50
+
51
+ - `list_runs` — recent runs in your organization.
52
+ - `get_run` — the structured, provenance-tagged result of a run: the typed job
53
+ graph, per-step statuses and exit codes, durations, and a derived failure
54
+ category.
55
+ - `get_step_logs` — the log lines for a specific step.
56
+ - `list_workflows` — your registered workflows.
57
+
58
+ **Drive**
59
+
60
+ - `trigger_run` — run a registered workflow ("run now").
61
+ - `rerun_run` — re-run a completed run.
62
+ - `cancel_run` — cancel an in-progress run.
63
+
64
+ If you belong to a single organization, the org is resolved automatically. If
65
+ you belong to several, pass an `orgId` argument to any tool.
66
+
67
+ ## 4. Why the structured result is agent-safe
68
+
69
+ `get_run` and `get_step_logs` return a machine-first shape designed for an agent
70
+ to reason over without being misled by repository content. Every field that
71
+ comes from your repo, a contributor, or a process's output — workflow and job
72
+ names, refs, error messages, log lines, job outputs — is wrapped in an
73
+ `{ untrusted: true, value: … }` envelope. KiCI-generated values (ids, statuses,
74
+ exit codes, durations, the derived failure category) are left plain. An agent can
75
+ keep user-controlled content out of its instruction channel by refusing to act
76
+ on anything tagged `untrusted`.
77
+
78
+ Secret values are never returned — only the names of the secret keys a step
79
+ accessed.
80
+
81
+ ## 5. The audit guarantee
82
+
83
+ Because the MCP accepts only an agent-kind PAT, **every action that flows through
84
+ it is agent-attributed by construction** — there is no path that produces an
85
+ untagged, human-looking action. Each read and each drive operation is recorded in
86
+ your orchestrator's access log under your identity plus the agent label, so you
87
+ always have a complete trail of what your agent did.
88
+
89
+ Inspect that trail with `kici-admin access-log list --json` (or
90
+ `kici-admin access-log show <id>` for one entry). An agent-attributed row keeps
91
+ `actor_type` as `user` and `actor_id` as your own identity — the agent provenance
92
+ rides in the row's actor metadata as `agentLabel` (the `--name` you minted the
93
+ PAT with) and `agentPatId` (the token that acted). The label is also stored in a
94
+ dedicated `agent_label` column on every such row.
95
+
96
+ ---
97
+
5
98
  ## CLI authentication
6
99
 
7
100
  Source: https://docs.kici.dev/user/cli-auth/
@@ -284,7 +377,7 @@ Run commands with `npx kici` or add scripts to your `package.json`:
284
377
  {
285
378
  "scripts": {
286
379
  "kici:compile": "kici compile",
287
- "kici:test": "kici test"
380
+ "kici:preview": "kici preview"
288
381
  }
289
382
  }
290
383
  ```
@@ -379,6 +472,8 @@ kici run local [event] [options]
379
472
  | `--kici-dir <path>` | `.kici` | Path to .kici directory |
380
473
  | `--in-place` | `false` | Run against the real working directory instead of an isolated tmp checkout (see "Execution isolation" below) |
381
474
  | `--keep` | `false` | Always retain the isolated tmp checkout (default: keep only on failure) |
475
+ | `--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 |
476
+ | `--fail-on-drift` | `false` | In check mode, exit non-zero if any step reports drift (no effect without `--check`) |
382
477
 
383
478
  **Interactive workflow selection (`--pick` / `-p`):**
384
479
 
@@ -520,6 +615,10 @@ kici run remote [fixture] [options]
520
615
  | `--target-allow-empty` | `false` | A `--target` that narrows a `runsOnAll` job to zero hosts skips it instead of failing |
521
616
  | `--debug` | `false` | Verbose internals |
522
617
  | `--kici-dir <path>` | `.kici` | Path to .kici directory |
618
+ | `--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)) |
619
+ | `--check` | `false` | Run in [check mode](https://docs.kici.dev/user/idempotent-steps/): each step reports drift instead of applying |
620
+ | `--fail-on-drift` | `false` | In check mode, fail the run if any step reports drift (no effect without `--check`) |
621
+ | `--approve-all, --yes` | `false` | Auto-approve every [approval gate](https://docs.kici.dev/user/approvals/) this run holds on (run-scoped; eligibility still enforced) |
523
622
 
524
623
  **Examples:**
525
624
 
@@ -685,12 +784,12 @@ kici orchestrators use us-east
685
784
  kici orchestrators use us-east --org xyz789ghi012
686
785
  ```
687
786
 
688
- ### kici test
787
+ ### kici preview
689
788
 
690
789
  Preview which workflows match a trigger event (dry-run, no execution). Useful for verifying trigger configurations during development.
691
790
 
692
791
  ```bash
693
- kici test [event] [options]
792
+ kici preview [event] [options]
694
793
  ```
695
794
 
696
795
  **Arguments:**
@@ -717,19 +816,19 @@ kici test [event] [options]
717
816
 
718
817
  ```bash
719
818
  # Preview which workflows match a push event
720
- kici test push
819
+ kici preview push
721
820
 
722
821
  # Preview PR trigger matching
723
- kici test pr:open
822
+ kici preview pr:open
724
823
 
725
824
  # Preview with branch override
726
- kici test push --branch develop
825
+ kici preview push --branch develop
727
826
 
728
827
  # Filter to specific workflow
729
- kici test push --workflow ci
828
+ kici preview push --workflow ci
730
829
 
731
830
  # Simulate changed files for path-filtered triggers
732
- kici test push --files src/index.ts --files README.md
831
+ kici preview push --files src/index.ts --files README.md
733
832
  ```
734
833
 
735
834
  **Exit codes:**
@@ -739,7 +838,7 @@ kici test push --files src/index.ts --files README.md
739
838
  | 0 | Preview completed (including zero matches) |
740
839
  | 1 | Error |
741
840
 
742
- **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>`.
841
+ **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>`.
743
842
 
744
843
  ### kici login
745
844
 
@@ -1116,6 +1215,27 @@ Only key names are shown — secret values are never returned over this endpoint
1116
1215
 
1117
1216
  **Prerequisites:** authenticate via `kici login` and select an active organization with `kici org use <name>`.
1118
1217
 
1218
+ ### kici pat create
1219
+
1220
+ Mint a personal access token under your own identity. Pass `--agent` to mint an
1221
+ **agent-kind** PAT — the credential a coding agent points the KiCI MCP server at.
1222
+
1223
+ ```bash
1224
+ kici pat create --agent --name "claude-code"
1225
+ ```
1226
+
1227
+ - `--agent` marks the token as agent-kind. An agent PAT inherits your
1228
+ permissions unchanged (it carries provenance, not extra authority) and is the
1229
+ **only** credential the MCP server accepts.
1230
+ - `--name <label>` sets the token name. For an agent PAT this is the **agent
1231
+ label** recorded on every action the agent takes — required with `--agent`.
1232
+ - `--expires-in-days <n>` overrides the default expiry.
1233
+
1234
+ The token is printed once — save it immediately; it cannot be retrieved later.
1235
+ See [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/) for the full setup.
1236
+
1237
+ **Prerequisites:** authenticate via `kici login` first.
1238
+
1119
1239
  ### kici types
1120
1240
 
1121
1241
  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.
@@ -1434,7 +1554,7 @@ kici admin drain-worker --url http://worker-2.internal:10143
1434
1554
  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.
1435
1555
 
1436
1556
  ```bash
1437
- kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-file> [options]
1557
+ kici verify-attestation [artifact] --bundle <path-or-url> [--trust-root <url-or-file>] [options]
1438
1558
  ```
1439
1559
 
1440
1560
  **Arguments:**
@@ -1445,14 +1565,14 @@ kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-f
1445
1565
 
1446
1566
  **Options:**
1447
1567
 
1448
- | Option | Required | Description |
1449
- | ---------------------------- | -------- | ----------------------------------------------------------------------------------------- |
1450
- | `--bundle <path-or-url>` | yes | Path or `http(s)` URL to the attestation bundle JSON. |
1451
- | `--trust-root <url-or-file>` | yes | Trusted issuer (see below). The token issuer is pinned to it, never taken from the token. |
1452
- | `--audience <aud>` | no | Expected token audience (defaults to the KiCI provenance audience). |
1453
- | `--json` | no | Print the structured verification result as JSON instead of human-readable output. |
1568
+ | Option | Required | Description |
1569
+ | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
1570
+ | `--bundle <path-or-url>` | yes | Path or `http(s)` URL to the attestation bundle JSON. |
1571
+ | `--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. |
1572
+ | `--audience <aud>` | no | Expected token audience (defaults to the KiCI provenance audience). |
1573
+ | `--json` | no | Print the structured verification result as JSON instead of human-readable output. |
1454
1574
 
1455
- **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:
1575
+ **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:
1456
1576
 
1457
1577
  - **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`.
1458
1578
  - **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):
@@ -1471,7 +1591,10 @@ kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-f
1471
1591
  **Examples:**
1472
1592
 
1473
1593
  ```bash
1474
- # Online: verify a bundle against a deployed issuer, digest-checking the artifact
1594
+ # Default: verify against the hosted KiCI platform (no --trust-root needed)
1595
+ kici verify-attestation ./dist/app.tgz --bundle ./app.tgz.kici.json
1596
+
1597
+ # Override: verify a bundle against a specific issuer, digest-checking the artifact
1475
1598
  kici verify-attestation ./dist/app.tgz \
1476
1599
  --bundle ./app.tgz.kici.json \
1477
1600
  --trust-root https://platform.example/issuer
@@ -1488,10 +1611,10 @@ kici verify-attestation --bundle ./app.tgz.kici.json \
1488
1611
 
1489
1612
  **Exit codes:**
1490
1613
 
1491
- | Code | Meaning |
1492
- | ---- | ----------------------------------------------------------------------------------- |
1493
- | 0 | Verified — signature, identity, build context (and digest, if checked) all pass |
1494
- | 1 | Not verified, or an error (missing flag, unreadable bundle, unreachable trust root) |
1614
+ | Code | Meaning |
1615
+ | ---- | ----------------------------------------------------------------------------------------- |
1616
+ | 0 | Verified — signature, identity, build context (and digest, if checked) all pass |
1617
+ | 1 | Not verified, or an error (missing `--bundle`, unreadable bundle, unreachable trust root) |
1495
1618
 
1496
1619
  ## Workflow discovery
1497
1620
 
@@ -1533,7 +1656,7 @@ All commands follow a consistent exit code convention:
1533
1656
 
1534
1657
  ## Debug output
1535
1658
 
1536
- Use `--debug` (on `kici run local`, `kici run remote`, `kici test`) or `--verbose` (on `kici compile`) for detailed output:
1659
+ Use `--debug` (on `kici run local`, `kici run remote`, `kici preview`) or `--verbose` (on `kici compile`) for detailed output:
1537
1660
 
1538
1661
  ```bash
1539
1662
  # Shows trigger matching, rule evaluation, decision traces
@@ -1543,7 +1666,7 @@ kici run local push --debug
1543
1666
  kici compile --verbose
1544
1667
 
1545
1668
  # Shows trigger matching preview
1546
- kici test pr:open --debug
1669
+ kici preview pr:open --debug
1547
1670
  ```
1548
1671
 
1549
1672
  Set `KICI_DEBUG=true` for additional internal debug output across all commands.
@@ -1824,7 +1947,7 @@ The lock file (`kici.lock.json`) is a JSON file with the following top-level fie
1824
1947
 
1825
1948
  | Field | Description |
1826
1949
  | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1827
- | `schemaVersion` | Lock file schema version (currently 20). Incremented on breaking format changes. |
1950
+ | `schemaVersion` | Lock file schema version (currently 29). Incremented on breaking format changes. |
1828
1951
  | `source` | Reference to the source file and export (e.g., `{ file: “.kici/workflows/ci.ts”, export: “#default” }`). |
1829
1952
  | `contentHash` | SHA-256 of the serialized lock file content (excluding itself). Changes when any workflow, trigger, or job changes. |
1830
1953
  | `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. |
@@ -1947,7 +2070,7 @@ Test your workflows remotely against the full CI pipeline from your local machin
1947
2070
  - 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
1948
2071
  - Detect test mode in workflow code via `ctx.isTestRun`
1949
2072
 
1950
- The command is remote-only -- all execution happens on the orchestrator and agent. For local-only trigger matching previews, use `kici test <event>`.
2073
+ The command is remote-only -- all execution happens on the orchestrator and agent. For local-only trigger matching previews, use `kici preview <event>`.
1951
2074
 
1952
2075
  :::note[Orchestrator prerequisite: cache storage]
1953
2076
  `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`).
@@ -802,6 +802,36 @@ job('deploy-review', {
802
802
 
803
803
  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.
804
804
 
805
+ ### Multiple environments per job
806
+
807
+ 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:
808
+
809
+ ```typescript
810
+ job('deploy', {
811
+ runsOn: 'default',
812
+ environments: ['staging', 'my-testing'],
813
+ steps: [
814
+ step('deploy', async (ctx) => {
815
+ // ctx.secrets and ctx.env carry the merged set from both environments
816
+ const dbUrl = await ctx.secrets.get('DB_URL');
817
+ }),
818
+ ],
819
+ });
820
+ ```
821
+
822
+ - `environment` (singular) and `environments` (array) are mutually exclusive — setting both is a compile error. `environment: 'staging'` is exactly equivalent to `environments: ['staging']`.
823
+ - Each array entry is a static name or a function of the event, resolved per element exactly like a single dynamic environment.
824
+
825
+ **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.
826
+
827
+ **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.
828
+
829
+ **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.
830
+
831
+ **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.
832
+
833
+ **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.
834
+
805
835
  ### Job-level environment variables
806
836
 
807
837
  The `env` property on a job provides static or dynamic environment variables:
@@ -841,7 +871,7 @@ job('deploy', {
841
871
  });
842
872
  ```
843
873
 
844
- If no `concurrencyGroup` is specified, the environment name is used as the default concurrency group.
874
+ 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.
845
875
 
846
876
  ### Step context
847
877
 
@@ -982,6 +1012,12 @@ Each environment has four tabs:
982
1012
 
983
1013
  4. **History** -- view filtered runs targeting this environment.
984
1014
 
1015
+ ### Bound environments on runs
1016
+
1017
+ 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.
1018
+
1019
+ 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`).
1020
+
985
1021
  ### Secrets management
986
1022
 
987
1023
  Secrets are individual encrypted values organized by scope paths (e.g., `aws/prod`, `databases/postgres`). Scopes are bound to environments via bindings:
@@ -2005,11 +2041,34 @@ statement's build context must match the token's identity claims (a mismatch is
2005
2041
  a hard failure).
2006
2042
 
2007
2043
  ```bash
2008
- kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-file>
2044
+ kici verify-attestation [artifact] --bundle <path-or-url> [--trust-root <url-or-file>]
2009
2045
  ```
2010
2046
 
2011
- You supply the trusted issuer out-of-band via `--trust-root` the verifier
2012
- never trusts the issuer named inside the token. There are two forms:
2047
+ ### Which trust root do I use?
2048
+
2049
+ The trust root is the **KiCI platform's provenance issuer** — the same hosted
2050
+ KiCI platform you `kici login` against. KiCI attestations are issued by, and
2051
+ verified against, that one issuer; there are no competing "roots" to choose
2052
+ between. So the answer to "shouldn't I just use KiCI as the trust root?" is yes
2053
+ — and that's the **default**: omit `--trust-root` and the verifier checks the
2054
+ bundle against the hosted KiCI platform automatically. You only pass
2055
+ `--trust-root` to verify against a different environment or, more commonly, an
2056
+ offline `{ issuer, jwks }` file for air-gapped checks.
2057
+
2058
+ ### Why you supply it out-of-band
2059
+
2060
+ Given there's a single issuer, why pass it at all instead of letting the
2061
+ verifier read it from the token? Because the issuer named **inside** a token
2062
+ cannot be trusted: a forged bundle could carry a token that names
2063
+ `iss: https://attacker.example` _and_ bundle a key set that "verifies" it,
2064
+ making the whole signature chain circular and self-attesting. The verifier
2065
+ therefore pins to an issuer you supply out-of-band and checks the token against
2066
+ _that_ — the bundle is verified against a key set you trust, not one it shipped
2067
+ with. Naming the trust root is a security requirement, not a multiple-choice
2068
+ question.
2069
+
2070
+ To override the default, supply the trusted issuer via `--trust-root`, in one of
2071
+ two forms:
2013
2072
 
2014
2073
  - **Online — an HTTPS issuer URL.** The verifier fetches
2015
2074
  `<url>/.well-known/openid-configuration`, reads its `issuer` and `jwks_uri`,
@@ -2037,7 +2096,10 @@ when it does not (or on an error such as a missing flag or unreachable trust
2037
2096
  root).
2038
2097
 
2039
2098
  ```bash
2040
- # Verify a bundle against a deployed issuer, digest-checking the artifact:
2099
+ # Default: verify against the hosted KiCI platform (no --trust-root needed):
2100
+ kici verify-attestation ./dist/app.tgz --bundle ./app.tgz.kici.json
2101
+
2102
+ # Override the trust root to verify against a specific issuer:
2041
2103
  kici verify-attestation ./dist/app.tgz \
2042
2104
  --bundle ./app.tgz.kici.json \
2043
2105
  --trust-root https://platform.example/issuer
@@ -2076,6 +2138,44 @@ attestations shows an empty state.
2076
2138
 
2077
2139
  <!-- /help:run-attestations -->
2078
2140
 
2141
+ ## Browsing attestations across runs
2142
+
2143
+ The **Attestations** page (in the org sidebar) lists every build-provenance
2144
+ attestation your organization has produced — not just one run's. It is the
2145
+ supply-chain audit surface: look up "who built `sha256:…`?" by digest, or browse
2146
+ and filter every attestation across all runs.
2147
+
2148
+ <!-- help:attestations-list#browsing-attestations-across-runs -->
2149
+
2150
+ The **Attestations** page lists every build-provenance attestation your organization has produced.
2151
+
2152
+ - **Search** by artifact digest (exact `sha256:…`) or name to trace a specific artifact.
2153
+ - **Filter** by verification status, repository, workflow, job, or date.
2154
+ - Each row's **status badge** is the verdict KiCI recorded when the attestation was produced (`verified`, `failed`, `unverifiable`, or `pending`).
2155
+
2156
+ Open a row for the parsed provenance statement and a live re-verification.
2157
+
2158
+ <!-- /help:attestations-list -->
2159
+
2160
+ The status badge here is the **server-side verdict**, computed once when the
2161
+ attestation was recorded (verify-at-ingest) — so the list stays fast at any
2162
+ size. `verified` means the signature, build identity, and build context all
2163
+ checked out against the provenance issuer; `failed` means verification ran and
2164
+ the bundle did not pass; `unverifiable` means no verdict could be computed (no
2165
+ provenance issuer configured, or its keys could not be read — not a forgery
2166
+ signal); `pending` means the verdict has not been computed yet.
2167
+
2168
+ Opening a row leads to the **attestation detail page**:
2169
+
2170
+ <!-- help:attestation-detail#browsing-attestations-across-runs -->
2171
+
2172
+ This page shows the parsed provenance for one attestation.
2173
+
2174
+ - **Builder identity, source, and build type** come from the signed SLSA statement.
2175
+ - The **stored badge** is the verdict recorded at build time; **Re-verify** runs the check live in your browser against the current signing keys.
2176
+ - **Download** exports the signed bundle for offline verification with `kici verify-attestation`.
2177
+ <!-- /help:attestation-detail -->
2178
+
2079
2179
  ## See also
2080
2180
 
2081
2181
  - [SDK runtime reference](https://docs.kici.dev/user/sdk/runtime/) — the `ctx.attestProvenance` and
@@ -2097,6 +2197,8 @@ Secrets are managed per-environment in the orchestrator (see [operator docs](htt
2097
2197
 
2098
2198
  This design prevents accidental secret leakage through child processes, log output, or error messages. Only secrets you explicitly request are loaded into memory.
2099
2199
 
2200
+ 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).
2201
+
2100
2202
  ## Where secret values come from
2101
2203
 
2102
2204
  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.