@kici-dev/compiler 0.6.0 → 0.7.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 (45) hide show
  1. package/dist/cli.js +10 -2
  2. package/dist/commands/compile.js +5 -1
  3. package/dist/commands/doctor.js +8 -2
  4. package/dist/commands/feedback.d.ts +53 -0
  5. package/dist/commands/feedback.js +142 -0
  6. package/dist/commands/index.d.ts +2 -0
  7. package/dist/commands/index.js +2 -1
  8. package/dist/commands/init.d.ts +9 -0
  9. package/dist/commands/init.js +77 -12
  10. package/dist/commands/preview.js +1 -1
  11. package/dist/commands/report/identity.d.ts +11 -0
  12. package/dist/commands/report/identity.js +7 -2
  13. package/dist/commands/run-routed.js +1 -0
  14. package/dist/commands/types.d.ts +6 -1
  15. package/dist/commands/types.js +2 -1
  16. package/dist/execution/executor.js +7 -1
  17. package/dist/llm-context/llms-architecture.txt +72 -86
  18. package/dist/llm-context/llms-cli-remote.txt +2380 -0
  19. package/dist/llm-context/llms-cli.txt +348 -2615
  20. package/dist/llm-context/llms-features-execution.txt +80 -23
  21. package/dist/llm-context/llms-features.txt +137 -6
  22. package/dist/llm-context/llms-full.txt +2582 -2025
  23. package/dist/llm-context/llms-getting-started.txt +152 -5
  24. package/dist/llm-context/llms-patterns.txt +81 -5
  25. package/dist/llm-context/llms-providers.txt +6 -2
  26. package/dist/llm-context/llms-sdk-runtime.txt +22 -18
  27. package/dist/llm-context/llms-sdk.txt +47 -7
  28. package/dist/llm-context/llms.txt +20 -13
  29. package/dist/local-plane/orchestrator-process.d.ts +0 -8
  30. package/dist/local-plane/orchestrator-process.js +3 -14
  31. package/dist/local-plane/plane-manager.js +2 -2
  32. package/dist/lockfile/generator.js +25 -9
  33. package/dist/lockfile/hasher.d.ts +5 -13
  34. package/dist/lockfile/hasher.js +1 -15
  35. package/dist/lockfile/workspace-siblings.d.ts +46 -0
  36. package/dist/lockfile/workspace-siblings.js +197 -0
  37. package/dist/templates/package-json.js +1 -1
  38. package/dist/test-runner/job-executor.js +1 -1
  39. package/dist/test-runner/rule-evaluator.js +1 -1
  40. package/dist/types.d.ts +6 -1
  41. package/package.json +7 -9
  42. package/sbom.spdx.json +123 -123
  43. package/dist/postinstall.d.ts +0 -9
  44. package/dist/postinstall.js +0 -62
  45. package/hack/postinstall.mjs +0 -105
@@ -46,7 +46,7 @@ The lock file is the seam. Everything left of it is decided once at compile time
46
46
 
47
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`, …), and writes `kici.lock.json`.
48
48
 
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 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.
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 lands in the lock. Anything your top-level code computes that isn't part of the returned workflow object doesn't exist past this point.
50
50
 
51
51
  See [compile the workflow](https://docs.kici.dev/user/getting-started/#compile-the-workflow) for the command in context.
52
52
 
@@ -202,7 +202,7 @@ kici runs logs <run-id>
202
202
  When you cannot resolve it yourself, gather a diagnostic bundle:
203
203
 
204
204
  ```bash
205
- kici report --run <run-id> --message "what you expected, and what happened"
205
+ kici report --run <run-id>
206
206
  ```
207
207
 
208
208
  The command writes a ZIP and prints its path and `sha256`. It sends nothing.
@@ -243,6 +243,10 @@ Reports contain your data, so there is no public tracker for them. Add
243
243
  kici report --run <run-id> --upload --message "matrix job hangs on macOS"
244
244
  ```
245
245
 
246
+ A defect in KiCI itself is different: if you can reproduce it without your own
247
+ data — the docs promise something the tool does not do — it belongs in the
248
+ public tracker instead. See [Reporting a discrepancy](https://docs.kici.dev/user/reporting-discrepancies/).
249
+
246
250
  The command prints a reference id. Quote it in any conversation about the
247
251
  problem. The bundle goes straight from your machine to KiCI storage over a
248
252
  one-time upload link — it never passes through the dashboard.
@@ -271,7 +275,7 @@ someone who has left.
271
275
  ## Reporting a security issue
272
276
 
273
277
  Do not use `kici report` for a suspected vulnerability. Follow the disclosure
274
- process in [SECURITY.md](https://github.com/kici-dev/kici/blob/master/SECURITY.md)
278
+ process in [SECURITY.md](https://github.com/kici-dev/kici-public/blob/main/SECURITY.md)
275
279
  instead.
276
280
 
277
281
  ## See also
@@ -305,7 +309,7 @@ npx kici init
305
309
  This will:
306
310
 
307
311
  1. Create `.kici/` directory with `workflows/`, `tests/`, `types/`, `package.json`, and `tsconfig.json`. The `types/` folder holds a local development aid — TypeScript declarations that `kici types` (and an authenticated `kici compile`) generate from your orchestrator's secret contexts. Its content is a snapshot of one org's secret keys, so it is not committed.
308
- 2. Create a `.kiciignore` file with sensible defaults
312
+ 2. Create two `.kiciignore` files with sensible defaults: one at the repo root, which selects the working-tree files a remote run uploads, and one inside `.kici/`, which declares the paths the per-workflow content hash skips
309
313
  3. Let you choose from starter workflow templates (hello-world, pr-checks)
310
314
  4. Install dependencies using the package manager detected for your repo (npm, pnpm, or yarn)
311
315
  5. Update `.gitignore` to exclude `.kici/node_modules/`, and write `.kici/.gitignore` to keep the generated `types/` declarations untracked (`kici.lock.json` stays tracked — the orchestrator fetches it from your repo)
@@ -629,7 +633,7 @@ For coding agents that want the entire documentation set up front, KiCI follows
629
633
 
630
634
  - `https://kici.dev/llms.txt` — curated link index grouped by SDK / patterns / CLI / architecture.
631
635
  - `https://kici.dev/llms-full.txt` — concatenated markdown of every page indexed above.
632
- - `kici docs llm` — print the same curated `llms.txt` index to stdout, offline, straight from the installed `@kici-dev/compiler` package. `kici docs llm <topic>` prints one task bundle (`getting-started`, `patterns`, `sdk`, `sdk-runtime`, `cli`, `features`, `features-execution`, `providers`, `architecture`), and `kici docs llm full` prints the whole `llms-full.txt`. Add `--out <path>` to write to a file. The agent can pipe the output into its own context buffer with no network call.
636
+ - `kici docs llm` — print the same curated `llms.txt` index to stdout, offline, straight from the installed `@kici-dev/compiler` package. `kici docs llm <topic>` prints one task bundle (`getting-started`, `patterns`, `sdk`, `sdk-runtime`, `cli`, `cli-remote`, `features`, `features-execution`, `providers`, `architecture`), and `kici docs llm full` prints the whole `llms-full.txt`. Add `--out <path>` to write to a file. The agent can pipe the output into its own context buffer with no network call.
633
637
  - `kici docs` — open the docs site in your browser.
634
638
 
635
639
  The offline bundle is regenerated from `docs/` every time the package is built, so it always matches the version of KiCI you've installed.
@@ -839,6 +843,12 @@ KiCI offers two equally-supported quickstart paths. Pick the one that fits your
839
843
 
840
844
  Each guide is split into two parts. **Part 1** gets you a green run against your own orchestrator with `kici run remote` — no GitHub App needed, just sign up, bring up the stack, and run. **Part 2** then wires up real GitHub pushes so your team's commits trigger runs automatically. You can stop after Part 1 and come back to Part 2 whenever you're ready.
841
845
 
846
+ ## Bring a coding agent
847
+
848
+ Workflows are TypeScript, so a coding agent can write them. KiCI ships its docs in a form an agent reads directly: point yours at [llms.txt](https://kici.dev/docs/llms.txt), or pipe a task bundle straight into its context with `kici docs llm` (`kici docs llm sdk` for the authoring API, `kici docs llm patterns` for recipes). An agent briefed that way can scaffold your first workflow, compile it, and read the failure when it breaks.
849
+
850
+ If your agent finds that these docs promise something KiCI does not do, that is worth telling us — `kici feedback` prints how to report it, and [Reporting a discrepancy](https://docs.kici.dev/user/reporting-discrepancies/) is the full guide.
851
+
842
852
  ## Option A — Docker / Podman (recommended)
843
853
 
844
854
  Two containers brought up with `docker compose up -d` (orchestrator + PostgreSQL), plus one short-lived agent container spawned per job by the container scaler. Minimal host setup, perfect for a laptop, home server, or a tiny VM. No need to install PostgreSQL or any other system service.
@@ -869,6 +879,143 @@ Both quickstarts deploy a real orchestrator + agent. If you only want to write a
869
879
 
870
880
  ---
871
881
 
882
+ ## Reporting a discrepancy
883
+
884
+ Source: https://docs.kici.dev/user/reporting-discrepancies/
885
+
886
+ This page is addressed to a **coding agent** working with KiCI. KiCI is built to
887
+ be driven by an LLM, so an agent is often the first to notice a mismatch. A
888
+ documented flag does not exist; a command prints something the docs never
889
+ promised. You can report that.
890
+
891
+ A human reader can use this page too. The rules below exist because the tracker
892
+ is public and you are filing under someone else's GitHub identity.
893
+
894
+ Run `kici feedback` to print this contract in your terminal, or
895
+ `kici feedback --json` to read it as structured data.
896
+
897
+ ## The rule, in one sentence
898
+
899
+ You find a checkable mismatch between what the docs or the CLI advertise and
900
+ what KiCI actually does. You draft an issue carrying a reproduction and a
901
+ justification, and show it to the person you are working with. You file it only
902
+ after they say yes.
903
+
904
+ You never file on your own initiative.
905
+
906
+ ## What qualifies
907
+
908
+ Report it when you can point at the claim and at the contradicting behaviour:
909
+
910
+ - A documented flag, command, or option that does not exist in the version you ran.
911
+ - Documented output — a shape, a field, an exit code — that differs from what the command produced.
912
+ - A CLI `--help` description that contradicts the published docs.
913
+ - A documented behaviour that does not happen, or a documented guarantee that does not hold.
914
+ - A documented error or limit that the tool does not actually enforce.
915
+
916
+ Two conditions apply to all of them. You ran the command and saw the result —
917
+ inference from reading the docs is not a reproduction. And you ran a published
918
+ version: check with `kici --version`.
919
+
920
+ ## What does not qualify
921
+
922
+ - Usage questions, or behaviour you find surprising but that the docs describe correctly.
923
+ - Feature requests and design preferences.
924
+ - Anything you inferred from reading docs without running the command.
925
+ - Anything reproduced only on a locally built or unreleased version.
926
+ - A failure that is your workflow, your credentials, or your environment.
927
+
928
+ If you are unsure whether the docs are wrong or you misread them, you are not
929
+ ready to file. Say so to the person you are working with and let them decide.
930
+
931
+ ## Never file these publicly
932
+
933
+ **A suspected vulnerability is never a public issue.** Do not file it, and do
934
+ not describe it in one. Open a private
935
+ [security advisory](https://github.com/kici-dev/kici-public/security/advisories/new)
936
+ instead, or tell the person you are working with to. This holds even if the
937
+ issue looks minor to you.
938
+
939
+ **A problem with someone's own runs is not a tracker issue.** That covers a
940
+ failing run, a broken deployment, and anything that needs their logs. Use the
941
+ private path in [Getting help](https://docs.kici.dev/user/getting-help/) instead:
942
+ `kici report --run <run-id> --upload` sends a redacted bundle straight to KiCI.
943
+
944
+ **Never put customer data in a public issue:**
945
+
946
+ - No secrets, tokens, or credentials — not even redacted-looking ones.
947
+ - No private repository names, internal hostnames, organization ids, or run ids.
948
+ - No log excerpts you have not read line by line.
949
+ - Reproduce with a minimal synthetic workflow, never the real one you were working on.
950
+
951
+ Rewriting the reproduction against a throwaway workflow in an empty directory is
952
+ the reliable way to satisfy all four at once.
953
+
954
+ ## Search before you draft
955
+
956
+ The tracker is small. The same finding arriving once per agent that reads the
957
+ docs is worse than not hearing it at all:
958
+
959
+ ```bash
960
+ gh issue list --repo kici-dev/kici-public --search "<terms>" --state all
961
+ ```
962
+
963
+ Search closed issues too — a closed one may carry the answer, or the decision
964
+ that the behaviour is intended. If a matching issue is open, add your
965
+ reproduction as a comment instead of opening a second one, under the same
966
+ approval rule.
967
+
968
+ ## What the report must carry
969
+
970
+ Five things. A report missing any of them cannot be acted on:
971
+
972
+ | Field | What it holds |
973
+ | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
974
+ | What the docs or CLI advertise | The exact claim, quoted, plus its source: a docs URL or the command whose `--help` says it. |
975
+ | What actually happened | The real output or behaviour, quoted, with any error text. |
976
+ | Minimal reproduction, including setup | Every step from an empty directory: the setup commands, a minimal synthetic workflow, and the exact command you ran. |
977
+ | Version and environment | Output of `kici --version`, plus Node version and OS. |
978
+ | Why this is a discrepancy | One or two sentences ruling out the likely misreads — why the docs cannot be read to match what you observed. |
979
+
980
+ The last field is the one agents skip, and it is the one that makes a report
981
+ usable. "The docs say `--foo` exists and it does not" is a claim. "`--foo` is
982
+ documented at <url>, and `kici bar --help` on 0.1.16 lists no such flag" is a
983
+ finding someone can act on in one pass.
984
+
985
+ Include setup steps whenever your reproduction needed any. A reproduction that
986
+ starts from state a maintainer cannot recreate is not a reproduction.
987
+
988
+ ## Filing it
989
+
990
+ The tracker is [kici-dev/kici-public](https://github.com/kici-dev/kici-public),
991
+ and the form is **Agent report**:
992
+
993
+ ```bash
994
+ kici feedback --open
995
+ ```
996
+
997
+ Draft the complete body first. Show it to the person you are working with —
998
+ the whole body, not a summary of it. File only after they say yes:
999
+
1000
+ ```bash
1001
+ gh issue create --repo kici-dev/kici-public --template agent_report.yml
1002
+ ```
1003
+
1004
+ The form asks you to confirm the report was drafted by an agent and reviewed by
1005
+ a person before filing. Answer honestly; it is what lets maintainers triage
1006
+ agent-filed reports as a group.
1007
+
1008
+ If they say no, that is the end of it. Do not re-file the same finding in
1009
+ another form, and do not open it as a question instead.
1010
+
1011
+ ## See also
1012
+
1013
+ - [Getting help](https://docs.kici.dev/user/getting-help/) — the private path for a problem with your own runs
1014
+ - [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/) — the MCP server, and the agent safety model
1015
+ - [Common failures](https://docs.kici.dev/user/common-failures/) — check here before concluding the docs are wrong
1016
+
1017
+ ---
1018
+
872
1019
  ## Why KiCI
873
1020
 
874
1021
  Source: https://docs.kici.dev/user/why-kici/
@@ -725,6 +725,74 @@ For a **minted app token**, prefer re-deriving over transporting — those expir
725
725
  after an hour, so one minted in an earlier job is often already dead by the time
726
726
  a later job reads it. Have the later job name the same secret, or mint its own.
727
727
 
728
+ ## What a job may ask for
729
+
730
+ A credential is authorized against the workflow you wrote, not against the code
731
+ running in the job. Three things must all hold before the orchestrator resolves
732
+ one:
733
+
734
+ 1. **The job declared it.** The orchestrator records the job's `gitCredentials`
735
+ map when it dispatches the job, and compares every request against that
736
+ record. A request naming a credential the job did not declare is refused.
737
+ This is why you pass `credential: 'forge'` — a name — rather than building a
738
+ credential reference in step code.
739
+ 2. **The named context admits the run.** A `prod:` reference runs the `prod`
740
+ context's own protection rules first: its branch restrictions, its
741
+ `minimumTrust`, its approval requirement. A credential named from a branch
742
+ the context does not allow is refused, and the git operation fails. The rule
743
+ that refused it is named in your orchestrator's log, not in the run — the
744
+ orchestrator returns a fixed error to the job rather than describing its own
745
+ configuration to code it does not trust.
746
+ 3. **The contributor is trusted.** A run from an untrusted ref — a fork pull
747
+ request — gets no declared credential at all. It still clones with the
748
+ source credential, so the build runs; only the declared credentials are
749
+ withheld. The reduced-privilege note on the run says so.
750
+
751
+ The context in a reference does **not** have to appear in the job's `contexts:`
752
+ list. The reference names its own context, and that context's rules are what
753
+ authorize it.
754
+
755
+ ## Generated jobs
756
+
757
+ A job produced by a `dynamicJob` generator has no entry in the lock file, so it
758
+ cannot declare credentials of its own. The **generator** declares them, and every
759
+ job it produces inherits that map:
760
+
761
+ ```typescript
762
+ dynamicJob('shards', {
763
+ gitCredentials: {
764
+ forge: { kind: 'token', tokenSecret: 'ci:FORGE_PAT' },
765
+ },
766
+ generate: async ({ ctx }) =>
767
+ ctx.event.payload.targets.map((target) =>
768
+ job(`publish-${target}`, {
769
+ runsOn: 'linux',
770
+ run: async ({ $, repo }) => {
771
+ await repo.withWrite(
772
+ { permissions: { contents: 'write' }, credential: 'forge' },
773
+ async () => {
774
+ await $`git push origin HEAD`;
775
+ },
776
+ );
777
+ },
778
+ }),
779
+ ),
780
+ });
781
+ ```
782
+
783
+ Three points follow from where the declaration lives:
784
+
785
+ - **All generated jobs share one map.** The generator is granted one ceiling, and
786
+ every job it produces gets exactly that ceiling. Use a second generator when
787
+ two sets of jobs need different credentials.
788
+ - **A `gitCredentials` map on a generated job is ignored.** The generator's
789
+ declaration is committed source that KiCI reads from the lock file. A generated
790
+ job's own declaration would come from the code that produced it, which is what
791
+ the authorization check above exists to be independent of.
792
+ - **The options form is required.** `dynamicJob('shards', async () => …)` — the
793
+ bare function form — has nowhere to put the declaration. Pass
794
+ `{ generate, gitCredentials }` instead; `needs` stays optional.
795
+
728
796
  ## How it works, and why long jobs still push
729
797
 
730
798
  An app token expires an hour after it is issued, and cannot be renewed. Rather
@@ -741,6 +809,10 @@ does at the start, and no credential is ever written into `.git/config`, into
741
809
  unaffected.
742
810
  - **The reserved `needs:` context is not resolvable yet** on a deployed
743
811
  orchestrator; naming it produces a clear error rather than a wrong credential.
812
+ - **A credential reference built in step code is refused.** The SDK takes a
813
+ credential _name_; there is no way to pass a reference. Code that constructs
814
+ one and sends it directly is rejected by the agent and, if it reaches the
815
+ orchestrator, by the declaration check above.
744
816
  - **A write window is bounded by the repository and the callback, not the step.**
745
817
  Steps running concurrently in the same job can push to the same repository
746
818
  while it is open. They cannot reach a different one.
@@ -1075,8 +1147,10 @@ KiCI has no native provider for Gogs, Forgejo, or Gitea, but these forges send H
1075
1147
  ```bash
1076
1148
  # Forgejo / Gitea send event name in X-Gitea-Event and signature in X-Gitea-Signature.
1077
1149
  # Gogs uses X-Gogs-Event and X-Gogs-Signature (same HMAC-SHA256 hex-digest format).
1150
+ # --org must be your Platform organization id: a generic source's routing key
1151
+ # embeds it, and the Platform refuses to register a key naming another org.
1078
1152
  kici-admin source add generic \
1079
- --org my-org \
1153
+ --org <platform-org-id> \
1080
1154
  --name forgejo-main \
1081
1155
  --verification hmac_sha256 \
1082
1156
  --secret @/path/to/webhook-secret.txt \
@@ -1084,7 +1158,7 @@ kici-admin source add generic \
1084
1158
  --rate-limit 120
1085
1159
  ```
1086
1160
 
1087
- Note the returned source ID, then register a webhook in the forge pointing at `https://<platform>/webhooks/<orgId>/generic/<sourceId>` (or the orchestrator's direct URL). Set content type to `application/json` and paste the same secret.
1161
+ Note the returned source ID, then register a webhook in the forge pointing at `https://<platform>/webhook/<orgId>/generic/<sourceId>` (or the orchestrator's direct URL, which takes the source **name** in place of the id). Set content type to `application/json` and paste the same secret.
1088
1162
 
1089
1163
  **Workflow:**
1090
1164
 
@@ -1151,14 +1225,16 @@ HTTPS with a forge PAT works the same way — store the token as a secret, `awai
1151
1225
 
1152
1226
  ## Plain GitHub repo webhooks (no GitHub App)
1153
1227
 
1154
- The Gogs/Forgejo/Gitea pattern above also applies when you want to trigger workflows from a GitHub repository **without installing the KiCI GitHub App** for example because you lack org-admin rights, you're on a restricted GitHub Enterprise tenant, or you simply don't want an App installation. Model the repo-level webhook as a generic source, accepting the same `genericWebhook()`-only ergonomics.
1228
+ The Gogs/Forgejo/Gitea pattern above also applies when you want to trigger workflows from a GitHub repository **without installing the KiCI GitHub App**. You may lack org-admin rights, sit on a restricted GitHub Enterprise tenant, or not want an App installation. Model the repo-level webhook as a generic source, accepting the same `genericWebhook()`-only ergonomics.
1155
1229
 
1156
1230
  **Operator setup:**
1157
1231
 
1158
1232
  ```bash
1159
1233
  # GitHub sends event name in X-GitHub-Event and HMAC-SHA256 signature in X-Hub-Signature-256.
1234
+ # --org must be your Platform organization id: a generic source's routing key
1235
+ # embeds it, and the Platform refuses to register a key naming another org.
1160
1236
  kici-admin source add generic \
1161
- --org my-org \
1237
+ --org <platform-org-id> \
1162
1238
  --name gh-repo-foo \
1163
1239
  --verification hmac_sha256 \
1164
1240
  --secret @/path/to/webhook-secret.txt \
@@ -1175,7 +1251,7 @@ curl -X PATCH https://<orchestrator>/api/v1/admin/generic-sources/<sourceId> \
1175
1251
 
1176
1252
  Then in the GitHub repo, go to **Settings → Webhooks → Add webhook**, set:
1177
1253
 
1178
- - **Payload URL:** `https://<platform>/webhooks/<orgId>/generic/<sourceId>` (or the orchestrator's direct URL)
1254
+ - **Payload URL:** `https://<platform>/webhook/<orgId>/generic/<sourceId>` (or the orchestrator's direct URL, which takes the source **name** in place of the id)
1179
1255
  - **Content type:** `application/json`
1180
1256
  - **Secret:** the same secret
1181
1257
  - **Events:** pick what you care about (e.g., `push`, `pull_request`)
@@ -627,10 +627,14 @@ kici-admin secret set <orgId> "__source__/<sourceId>" pat --value "<your-forgejo
627
627
  Finally, configure the forge to deliver webhooks to:
628
628
 
629
629
  ```
630
- https://<platform-host>/webhook/<orgId>/generic/<source-name>
630
+ https://<platform-host>/webhook/<orgId>/generic/<sourceId>
631
631
  ```
632
632
 
633
- with the same secret you passed to `--secret`.
633
+ with the same secret you passed to `--secret`. `<orgId>` is your Platform
634
+ organization id — the same value you passed to `--org`, which the source's
635
+ routing key embeds — and `<sourceId>` is the id `source add` printed. The
636
+ orchestrator's own ingest URL has the same shape but takes the source **name**
637
+ in place of the id.
634
638
 
635
639
  ## SSH deploy key
636
640
 
@@ -617,7 +617,7 @@ The SDK exposes three idempotency helpers — a generic function `idempotent()`,
617
617
  2. **Apply** the change only when drift is detected.
618
618
  3. **Surface** the resource (or its identifier) on both branches, so downstream steps don't need to know whether work happened or was skipped.
619
619
 
620
- `idempotent()` and `idempotentStep()` wrap the same underlying runner and always apply on drift. Pick `idempotentStep()` when the operation is the whole job of a step; use `idempotent()` from anywhere — inside a multi-action step, a hook, or a bare async function. Pick `checkStep()` when the step should respect the run-level check mode — `kici run --check` previews the drift without applying it.
620
+ `idempotent()` and `idempotentStep()` wrap the same underlying runner and always apply on drift. Pick `idempotentStep()` when the operation is the whole job of a step; use `idempotent()` from anywhere — inside a multi-action step, a hook, or a bare async function. Pick `checkStep()` when the step should respect the run-level check mode — `kici run remote --check` previews the drift without applying it.
621
621
 
622
622
  ## `idempotent(options)`
623
623
 
@@ -712,30 +712,30 @@ export const setup = job('setup', {
712
712
 
713
713
  ## `checkStep(name, options)`
714
714
 
715
- The check-mode-aware sibling of `idempotentStep()`. It takes a closely related option shape, but behaves differently when a run is started in check mode (`kici run --check`):
715
+ The check-mode-aware sibling of `idempotentStep()`. It takes a closely related option shape, but behaves differently when a run is started in check mode (`kici run remote --check`):
716
716
 
717
- | Factory | Behavior under `kici run --check` |
717
+ | Factory | Behavior under `kici run remote --check` |
718
718
  | ---------------- | ----------------------------------------- |
719
719
  | `idempotentStep` | always applies on drift |
720
720
  | `checkStep` | reports drift, applies only in apply mode |
721
721
 
722
- Use `checkStep()` for deploy-style steps where you want a dry-run preview of pending changes before committing them, and `idempotentStep()` for steps that must always converge (for example inside a hook). A `checkStep()` desugars to the first-class step check facet (`check` / `summarize` / `run(ctx, drift)` / `whenInSync`), so it participates in run-level check mode automatically: `kici run --check` reports the drift and skips `apply`, `kici run --check --fail-on-drift` exits non-zero when drift is detected, and apply mode applies the change.
722
+ Use `checkStep()` for deploy-style steps where you want a dry-run preview of pending changes before committing them, and `idempotentStep()` for steps that must always converge (for example inside a hook). A `checkStep()` desugars to the first-class step check facet (`check` / `summarize` / `run(ctx, drift)` / `whenInSync`), so it participates in run-level check mode automatically: `kici run remote --check` reports the drift and skips `apply`, `kici run remote --check --fail-on-drift` exits non-zero when drift is detected, and apply mode applies the change.
723
723
 
724
724
  ### Parameters
725
725
 
726
- | Name | Type | Required | Description |
727
- | ----------------- | ------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------- |
728
- | `name` | `string` | Yes | Step name. Appears in the run timeline and in log lines. |
729
- | `check` | `(ctx) => Promise<TDrift \| null>` | Yes | Read-only inspection. Return `null` when the system is already in the desired state. |
730
- | `apply` | `(ctx, drift: TDrift) => Promise<TApplied>` | Yes | Brings the system to the desired state. Runs only in apply mode (skipped under `kici run --check`). |
731
- | `summarize` | `(drift: TDrift) => string` | Yes | Human-readable summary of what `apply()` would do; shown in check-mode drift output. |
732
- | `whenInSync` | `(ctx) => Promise<TInSync>` | No | Runs when `check()` returned `null` (already in sync). |
733
- | `outputs` | `OutputSchema` | No | Zod schema validating the step's outputs at runtime. |
734
- | `continueOnError` | `boolean` | No | When true, the job proceeds even if this step fails. |
735
- | `timeout` | `number` | No | Step-level timeout in milliseconds. |
736
- | `retry` | `number \| RetryConfig` | No | Retry policy for the step; `retry: N` is shorthand for `{ maxAttempts: N }`. |
737
- | `cache` | `CacheInput` | No | Declarative cache restored before the step and saved after it succeeds. |
738
- | `rules` | `Rule[]` | No | Step-level conditional rules, evaluated agent-side. |
726
+ | Name | Type | Required | Description |
727
+ | ----------------- | ------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- |
728
+ | `name` | `string` | Yes | Step name. Appears in the run timeline and in log lines. |
729
+ | `check` | `(ctx) => Promise<TDrift \| null>` | Yes | Read-only inspection. Return `null` when the system is already in the desired state. |
730
+ | `apply` | `(ctx, drift: TDrift) => Promise<TApplied>` | Yes | Brings the system to the desired state. Runs only in apply mode (skipped under `kici run remote --check`). |
731
+ | `summarize` | `(drift: TDrift) => string` | Yes | Human-readable summary of what `apply()` would do; shown in check-mode drift output. |
732
+ | `whenInSync` | `(ctx) => Promise<TInSync>` | No | Runs when `check()` returned `null` (already in sync). |
733
+ | `outputs` | `OutputSchema` | No | Zod schema validating the step's outputs at runtime. |
734
+ | `continueOnError` | `boolean` | No | When true, the job proceeds even if this step fails. |
735
+ | `timeout` | `number` | No | Step-level timeout in milliseconds. |
736
+ | `retry` | `number \| RetryConfig` | No | Retry policy for the step; `retry: N` is shorthand for `{ maxAttempts: N }`. |
737
+ | `cache` | `CacheInput` | No | Declarative cache restored before the step and saved after it succeeds. |
738
+ | `rules` | `Rule[]` | No | Step-level conditional rules, evaluated agent-side. |
739
739
 
740
740
  Everything from `outputs` down is a plain [`step()` option](https://docs.kici.dev/user/sdk/core/) forwarded to the underlying step. The three step options `checkStep` does **not** accept are `onCancel`, `cleanup`, and `approval`.
741
741
 
@@ -769,7 +769,7 @@ export const deploy = job('deploy', {
769
769
  });
770
770
  ```
771
771
 
772
- Run `kici run --check` against this workflow to see the drift summary without touching DNS; run it without `--check` to apply.
772
+ Run `kici run remote --check` against this workflow to see the drift summary without touching DNS; run it without `--check` to apply.
773
773
 
774
774
  ## Worked example: create-if-missing returning a resource id
775
775
 
@@ -1532,6 +1532,8 @@ const deploy = job('deploy', {
1532
1532
 
1533
1533
  Request a short-lived OIDC ID token for the current job, bound to an `audience`. The token is a signed JWT whose identity claims (`repository`, `ref`, `sha`, `kici_run_id`, `kici_job_id`) are derived by your orchestrator from the run context — a step cannot spoof them. Use it to authenticate the build to an external service that trusts the orchestrator's OIDC issuer (for example, when generating build provenance).
1534
1534
 
1535
+ The token also carries the event context a cloud trust policy needs to tell a fork pull request from a trusted push — `is_fork`, `head_repository`, `trust_tier`, `event_name`, and a pull-request-specific `sub`. See [ID-token claims and cloud trust policies](https://docs.kici.dev/user/provenance/#id-token-claims-and-cloud-trust-policies) for the full claim table and a worked AWS policy.
1536
+
1535
1537
  ```typescript
1536
1538
  const publish = job('publish', {
1537
1539
  steps: [
@@ -1571,6 +1573,8 @@ interface HostInventoryEntry {
1571
1573
 
1572
1574
  Two dimensions describe a host. **Labels** are flat strings used for grouping and targeting (the same labels `runsOn` / `runsOnAll` match). **Properties** are typed host-vars (`string | number | boolean`) — the place for facts like `region`, `cores`, or `gpu`. A host reports its own properties via the agent's `KICI_PROPERTIES` config, and an operator can pre-declare them with `kici-admin host declare --prop key=value`; the two are shallow-merged (agent-reported keys win).
1573
1575
 
1576
+ `labels` and `hostname` come back lowercase — KiCI folds both, so a pool declaring `Docker` reports `docker`. Compare against a lowercase value: `h.labels.includes('gpu')`, not `h.labels.includes('GPU')`. A label selector passed to `query()` folds too, so `{ include: [['GPU']] }` matches. `agentId` and `properties` keep their case.
1577
+
1574
1578
  ```typescript
1575
1579
  // All hosts:
1576
1580
  const all = await ctx.kici.inventory.query();
@@ -142,6 +142,7 @@ runsOn: { labels: ['kici:os:linux'], exclude: ['kici:host:box-01'] }
142
142
 
143
143
  - **Required labels:** The agent must have every label in the `labels` array (or the string/array form).
144
144
  - **Excluded labels:** The agent must NOT have any label in the `exclude` array. This includes auto-derived labels like `kici:arch:arm64`, `kici:os:linux`, etc.
145
+ - **Case:** Label matching is **case-insensitive** at every step. `runsOn: 'gpu'` matches an agent that reports `GPU`, and a pool declaring `["Docker"]` serves a `runsOn: ["docker"]` job (see [auto-scaler matching rules](https://docs.kici.dev/operator/orchestrator/auto-scaler/operations/#matching-rules)). KiCI stores and displays every label in lowercase, so the dashboard, `kici-admin agent list`, and `ctx.kici.inventory[…].labels` report the folded form. Compare against a lowercase value when you read a label back in workflow code: `h.labels.includes('gpu')`, not `h.labels.includes('GPU')`.
145
146
  - **Compile-time validation:** The compiler will error if any label appears in both `labels` and `exclude` (overlap detection).
146
147
  - **Operator-declared mandatory labels:** Operators may mark a scaler with `mandatoryLabels` (Kubernetes-taint-style opt-in). When a scaler declares a mandatory label, a job is only allowed to land on it if `runsOn.labels` includes that label. A workflow targeting such a scaler must explicitly list the mandatory label in `runsOn`. See the [auto-scaler mandatory labels](https://docs.kici.dev/operator/orchestrator/auto-scaler/common-config/#mandatory--exclude-labels) for details.
147
148
 
@@ -194,6 +195,12 @@ Every selector element — in `runsOn`, in `runsOnAll`, on both the include and
194
195
  - **String with glob metacharacters (`*`, `?`, `[]`, `{}`) → glob.** `'kici:host:web-*'` matches every host label starting with `kici:host:web-`. `'kici:host:box-0[1-3]'` matches `box-01`, `box-02`, `box-03`.
195
196
  - **`RegExp` literal → regular expression.** `/kici:host:box-0[1-3]/` matches any label the expression matches.
196
197
 
198
+ All three forms match case-insensitively. `'GPU'`, `'kici:host:Web-*'` and `/kici:host:BOX-0[1-3]/` each match a label of any case, and a `RegExp` you write with the `i` flag behaves the same. The `g` and `y` flags are dropped — a selector asks one question per label, so a sticky match would resume part-way through the next one.
199
+
200
+ `kici:host:` carries the machine's hostname folded to lowercase. A host that calls itself `Build-Box-01` advertises `kici:host:build-box-01`, and both `runsOn: 'kici:host:build-box-01'` and `runsOn: 'kici:host:Build-Box-01'` match it.
201
+
202
+ Case folding covers labels and hostnames only. An **agent ID** stays an opaque identifier and compares exactly, which is what keeps a per-host secret binding on `prod-01` away from an agent named `PROD-01` — see [per-host secret scoping](https://docs.kici.dev/operator/security/secrets/#per-host-secret-scoping).
203
+
197
204
  Both the required (include) side and the excluded side accept all three forms:
198
205
 
199
206
  ```typescript
@@ -852,11 +859,19 @@ Tag a dynamic job generator with a group name so other jobs can reference it via
852
859
  ```typescript
853
860
  function dynamicJob(
854
861
  groupName: string,
855
- fnOrConfig: DynamicJobFn | { needs: DynamicJobNeed[]; generate: DynamicJobFn },
862
+ fnOrConfig:
863
+ | DynamicJobFn
864
+ | {
865
+ needs?: DynamicJobNeed[];
866
+ generate: DynamicJobFn;
867
+ gitCredentials?: GitCredentialMap;
868
+ },
856
869
  ): TaggedDynamicJobFn;
857
870
  ```
858
871
 
859
- The second argument is either a plain generator (event-only, evaluated at webhook time) or a result-aware `{ needs, generate }` config, which defers the generator until its declared upstreams complete and exposes their frozen outputs as `ctx.needs`. See [Rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/#dynamicjob--result-aware-generation) for the result-aware form.
872
+ The second argument is either a plain generator (event-only, evaluated at webhook time) or an options config. An options config that declares `needs` is result-aware: it defers the generator until those upstreams complete and exposes their frozen outputs as `ctx.needs`. See [Rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/#dynamicjob--result-aware-generation) for the result-aware form.
873
+
874
+ `needs` is optional. An options config without it is evaluated at webhook time, like the plain generator form. Use that form to declare `gitCredentials` on an event-only generator: every job the generator produces inherits the map, which is the only way a generated job gets named credentials. See [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/#generated-jobs).
860
875
 
861
876
  The generator runs twice: once in the init phase (to register expected job names) and once inside the executing agent (to produce the actual jobs). Mismatches between the two evaluations are detected as determinism drift -- see [dynamic-jobs](https://docs.kici.dev/architecture/execution/dynamic-jobs/).
862
877
 
@@ -1035,6 +1050,11 @@ The throw is a `ChangedFilesUnavailableError` (exported from `@kici-dev/sdk`, ca
1035
1050
 
1036
1051
  ### evaluateRules(rules, context, label, onRuleResult?)
1037
1052
 
1053
+ The agent calls this on your behalf. A workflow does not call it. It lives on
1054
+ `@kici-dev/sdk/internal`, outside semver, and stays exported from the root barrel as
1055
+ `@deprecated` until v1.0.0 — see [deprecations](https://docs.kici.dev/user/deprecations/). It is described here
1056
+ because its return shape is what a rule's outcome looks like in the run log.
1057
+
1038
1058
  Evaluate an array of rules sequentially with fail-fast behavior. Stops on the first failure.
1039
1059
 
1040
1060
  ```typescript
@@ -1102,7 +1122,7 @@ Every variant carries the shared `EventBase` fields — `type`, `action`, `targe
1102
1122
 
1103
1123
  Matrix configurations expand a single job into multiple instances, one per parameter combination. Maximum 256 combinations.
1104
1124
 
1105
- Combinations must be **unique**. Two combinations that would produce the same instance name — most simply, the same value listed twice — fail the job instead of quietly running it twice.
1125
+ Combinations must be **unique**. Two combinations that would produce the same instance name — in the simplest case, the same value listed twice — fail the job instead of quietly running it twice.
1106
1126
 
1107
1127
  Expansion happens at **dispatch time**: the orchestrator materializes the matrix into N execution jobs — one per combination, each dispatched to its own agent — before any job runs. Each instance receives its combination as `ctx.matrix`. This is identical whether the workflow runs via `kici run <event> --local` or remotely through a webhook trigger, and the dashboard groups the N instances under one parent node.
1108
1128
 
@@ -1285,8 +1305,13 @@ isDynamicFunction(matrix); // true if async function
1285
1305
 
1286
1306
  ### Matrix expansion utilities
1287
1307
 
1308
+ The agent expands a matrix for you. A workflow does not call these. They live on
1309
+ `@kici-dev/sdk/internal`, outside semver, and stay exported from the root barrel as
1310
+ `@deprecated` until v1.0.0 — see [deprecations](https://docs.kici.dev/user/deprecations/). They are described
1311
+ here because they define the combinations a matrix job actually produces.
1312
+
1288
1313
  ```typescript
1289
- import { expandMatrix, applyIncludeExclude } from '@kici-dev/sdk';
1314
+ import { expandMatrix, applyIncludeExclude } from '@kici-dev/sdk/internal';
1290
1315
  ```
1291
1316
 
1292
1317
  `expandMatrix(matrix)` takes a string array or an object of string arrays and returns all combinations as `MatrixValues[]`. For a single-dimension array, each value becomes `{ value: '...' }`. For multi-dimensional objects, it produces the Cartesian product. Anything else throws a `MatrixShapeError` naming the expected shape; numbers and booleans inside the values are accepted and converted to strings.
@@ -1344,7 +1369,8 @@ export default workflow('ci', {
1344
1369
  `dynamicJob(group, fnOrConfig)` tags a generator with a group name (so static jobs can depend on it via `needs: [dynamicGroup('group')]`). It is polymorphic:
1345
1370
 
1346
1371
  - **Function form** — event-only, dispatched at webhook time: `dynamicJob('shards', async ({ ctx }) => [...])`.
1347
- - **Options-object form** — result-aware, deferred until its declared `needs` complete, then run with the upstreams' frozen outputs as `ctx.needs`: `dynamicJob('reports', { needs, generate })`.
1372
+ - **Options-object form** — `dynamicJob('reports', { needs, generate })`. With `needs`, it is result-aware: deferred until those upstreams complete, then run with their frozen outputs as `ctx.needs`.
1373
+ - `needs` is optional. Without it the generator is dispatched at webhook time, like the function form. That form is how a generator declares `gitCredentials`, which every job it produces inherits — see [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/#generated-jobs).
1348
1374
 
1349
1375
  ```typescript
1350
1376
  import { workflow, job, step, dynamicJob, dynamicGroup, z } from '@kici-dev/sdk';
@@ -1517,7 +1543,7 @@ those files contain**. It is a declarative filter on the `pr()`, `push()`, and
1517
1543
  at the event's commit. The orchestrator evaluates it as pure data before
1518
1544
  dispatching — it reads only the referenced files, never clones the whole
1519
1545
  repository, and never runs any of your workflow code. A workflow whose `requires`
1520
- does not pass is simply not dispatched.
1546
+ does not pass is not dispatched.
1521
1547
 
1522
1548
  Each entry is a `ContentRequirement`:
1523
1549
 
@@ -1782,7 +1808,7 @@ dispatch({ types: ['deploy', 'rollback'] }); // Specific event types
1782
1808
  #### Typed dispatch inputs
1783
1809
 
1784
1810
  A `dispatch()` trigger can declare a typed `inputs` schema. Operators supply
1785
- values with `kici run --input key=value`; KiCI validates, coerces, defaults, and
1811
+ values with `kici run remote --input key=value`; KiCI validates, coerces, defaults, and
1786
1812
  exposes them to steps and rules as `ctx.dispatchInputs`. The values are validated
1787
1813
  on the orchestrator from the compiled lock file — a missing required input or a
1788
1814
  bad value is rejected before any agent runs, without cloning the repository.
@@ -2497,6 +2523,20 @@ import { workflow, job, step, pr, push, rule, defineEvent } from '@kici-dev/sdk'
2497
2523
 
2498
2524
  For the complete list of every named export (factory functions, triggers, rules, validation, hook factories, types), see the per-topic pages above.
2499
2525
 
2526
+ ## `@kici-dev/sdk/internal` is not a supported surface
2527
+
2528
+ The package also publishes an `@kici-dev/sdk/internal` subpath. It carries the runtime
2529
+ contract between the SDK and the KiCI agent. Those are the functions that install the maps
2530
+ a `.result` proxy reads, build the step context your workflow body receives, evaluate its
2531
+ rules, and expand its matrix. The agent drives all of it on your behalf.
2532
+
2533
+ It is **not covered by semver** and may change shape in any release. Do not import it from
2534
+ a workflow. Everything a workflow author needs is on the root entry point above.
2535
+
2536
+ Those same symbols are also still exported from the root barrel, marked `@deprecated`, so
2537
+ an older SDK in a repository keeps working. They are removed from the root at v1.0.0 — see
2538
+ [deprecations](https://docs.kici.dev/user/deprecations/).
2539
+
2500
2540
  ## See also
2501
2541
 
2502
2542
  - [Getting started](https://docs.kici.dev/user/getting-started/) -- install the SDK, write your first workflow, test locally