@kici-dev/compiler 0.4.0 → 0.6.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 (83) hide show
  1. package/dist/cli.js +37 -7
  2. package/dist/commands/approve.d.ts +12 -0
  3. package/dist/commands/approve.js +5 -2
  4. package/dist/commands/compile.js +5 -15
  5. package/dist/commands/doctor.js +2 -2
  6. package/dist/commands/endpoints.js +4 -6
  7. package/dist/commands/held-run-client.d.ts +21 -1
  8. package/dist/commands/held-run-client.js +34 -15
  9. package/dist/commands/hook.js +22 -20
  10. package/dist/commands/index.d.ts +2 -0
  11. package/dist/commands/index.js +2 -1
  12. package/dist/commands/init.d.ts +9 -2
  13. package/dist/commands/init.js +43 -16
  14. package/dist/commands/login.js +1 -1
  15. package/dist/commands/orchestrators.js +3 -2
  16. package/dist/commands/preview.js +1 -8
  17. package/dist/commands/reject.d.ts +12 -0
  18. package/dist/commands/reject.js +5 -2
  19. package/dist/commands/report/collect.d.ts +82 -0
  20. package/dist/commands/report/collect.js +234 -0
  21. package/dist/commands/report/identity.d.ts +48 -0
  22. package/dist/commands/report/identity.js +49 -0
  23. package/dist/commands/report/index.d.ts +63 -0
  24. package/dist/commands/report/index.js +119 -0
  25. package/dist/commands/report/upload.d.ts +38 -0
  26. package/dist/commands/report/upload.js +64 -0
  27. package/dist/commands/run-hold-watch.js +2 -2
  28. package/dist/commands/run.js +6 -3
  29. package/dist/commands/runs/show.js +80 -1
  30. package/dist/commands/types.js +51 -9
  31. package/dist/errors/formatter.d.ts +2 -4
  32. package/dist/errors/formatter.js +1 -3
  33. package/dist/errors/index.d.ts +1 -1
  34. package/dist/errors/index.js +2 -2
  35. package/dist/execution/sdk-alias.js +4 -2
  36. package/dist/fixtures/compiler.js +2 -1
  37. package/dist/format.js +3 -3
  38. package/dist/generators/secrets-dts.d.ts +8 -3
  39. package/dist/generators/secrets-dts.js +3 -3
  40. package/dist/hooks/installer.js +2 -1
  41. package/dist/llm-context/llms-architecture.txt +59 -23
  42. package/dist/llm-context/llms-cli.txt +168 -34
  43. package/dist/llm-context/llms-features-execution.txt +2017 -0
  44. package/dist/llm-context/llms-features.txt +96 -1100
  45. package/dist/llm-context/llms-full.txt +3131 -1407
  46. package/dist/llm-context/llms-getting-started.txt +164 -31
  47. package/dist/llm-context/llms-patterns.txt +186 -7
  48. package/dist/llm-context/llms-providers.txt +15 -33
  49. package/dist/llm-context/llms-sdk-runtime.txt +62 -40
  50. package/dist/llm-context/llms-sdk.txt +284 -58
  51. package/dist/llm-context/llms.txt +23 -15
  52. package/dist/local-plane/paths.d.ts +15 -0
  53. package/dist/local-plane/paths.js +22 -1
  54. package/dist/local-plane/plane-manager.js +2 -2
  55. package/dist/local-plane/port-holder.js +1 -1
  56. package/dist/local-plane/postgres.d.ts +3 -16
  57. package/dist/local-plane/postgres.js +10 -15
  58. package/dist/lockfile/generator.d.ts +12 -0
  59. package/dist/lockfile/generator.js +182 -54
  60. package/dist/lockfile/index.d.ts +0 -2
  61. package/dist/lockfile/index.js +1 -2
  62. package/dist/postinstall.js +2 -1
  63. package/dist/remote/config.d.ts +2 -15
  64. package/dist/remote/config.js +2 -16
  65. package/dist/remote/dashboard-client.d.ts +39 -0
  66. package/dist/remote/dashboard-client.js +41 -0
  67. package/dist/remote/oauth.js +7 -5
  68. package/dist/remote/uploader.js +2 -2
  69. package/dist/templates/package-json.js +1 -1
  70. package/dist/test-runner/dry-run.d.ts +1 -2
  71. package/dist/test-runner/dry-run.js +5 -20
  72. package/dist/test-runner/git-detector.js +2 -1
  73. package/dist/test-runner/job-executor.js +2 -1
  74. package/dist/test-runner/payload-builder.js +11 -17
  75. package/dist/types.d.ts +64 -10
  76. package/dist/types.js +7 -1
  77. package/dist/validation/validator.js +63 -6
  78. package/package.json +16 -11
  79. package/sbom.spdx.json +957 -900
  80. package/dist/lockfile/purity-analyzer.d.ts +0 -25
  81. package/dist/lockfile/purity-analyzer.js +0 -204
  82. package/dist/lockfile/purity-diagnostics.d.ts +0 -31
  83. package/dist/lockfile/purity-diagnostics.js +0 -52
@@ -18,22 +18,24 @@ function workflow(name: string, options: WorkflowOptions): Workflow;
18
18
 
19
19
  **Parameters:**
20
20
 
21
- | Parameter | Type | Required | Description |
22
- | --------------------- | ---------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
- | `name` | `string` | yes | Unique workflow name |
24
- | `options.jobs` | `JobOrFactory[]` | yes | Static jobs and/or dynamic job generators |
25
- | `options.on` | `Trigger \| Trigger[]` | no | When the workflow should trigger |
26
- | `options.rules` | `Rule[]` | no | Conditions that must pass for execution |
27
- | `options.description` | `string` | no | Human-readable description |
28
- | `options.hashFiles` | `string[]` | no | Extra repo-relative paths or globs mixed into the workflow content hash. Changes invalidate the source cache. |
29
- | `options.registries` | `Registry[]` | no | Private npm registries the agent authenticates against before `npm install`. Each `tokenSecret` uses qualified `<context>:<secret>` syntax. |
30
- | `options.installEnv` | `string[]` | no | Qualified `<context>:<secret>` refs projected as env vars onto the install subprocess (used with a customer-committed `.kici/.npmrc`). |
31
- | `options.onCancel` | `HookInput` | no | Runs when the workflow is cancelled |
32
- | `options.cleanup` | `HookInput` | no | Always runs after the workflow (success, failure, or cancel) |
33
- | `options.onSuccess` | `HookInput` | no | Runs on workflow success |
34
- | `options.onFailure` | `HookInput` | no | Runs on workflow failure |
35
- | `options.concurrency` | `{ group: (ctx) => string; cancelInProgress?: boolean; max?: number }` | no | Workflow-scoped concurrency. See [Concurrency](https://docs.kici.dev/user/concurrency/). |
36
- | `options.timeout` | `number` | no | Whole-run wall-clock timeout in milliseconds across all jobs. On breach the orchestrator cancels the run and marks it timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
21
+ | Parameter | Type | Required | Description |
22
+ | --------------------- | ---------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | `name` | `string` | yes | Unique workflow name |
24
+ | `options.jobs` | `JobOrFactory[]` | yes | Static jobs and/or dynamic job generators |
25
+ | `options.on` | `Trigger \| Trigger[]` | no | When the workflow should trigger |
26
+ | `options.rules` | `Rule[]` | no | Conditions that must pass for execution |
27
+ | `options.filter` | `FilterFn` | no | Pre-dispatch predicate deciding whether the workflow applies to the event's source repo. A `false` result suppresses the workflow's jobs -- none is dispatched and none is reported as skipped. See [Global workflows](https://docs.kici.dev/user/global-workflows/). |
28
+ | `options.description` | `string` | no | Human-readable description |
29
+ | `options.hashFiles` | `string[]` | no | Extra repo-relative paths or globs mixed into the workflow content hash. Changes invalidate the source cache. |
30
+ | `options.registries` | `Registry[]` | no | Private npm registries the agent authenticates against before `npm install`. Each `tokenSecret` uses qualified `<context>:<secret>` syntax. |
31
+ | `options.installEnv` | `string[]` | no | Qualified `<context>:<secret>` refs projected as env vars onto the install subprocess (used with a customer-committed `.kici/.npmrc`). |
32
+ | `options.onCancel` | `HookInput` | no | Runs when the workflow is cancelled |
33
+ | `options.cleanup` | `HookInput` | no | Always runs after the workflow (success, failure, or cancel) |
34
+ | `options.onSuccess` | `HookInput` | no | Runs on workflow success |
35
+ | `options.onFailure` | `HookInput` | no | Runs on workflow failure |
36
+ | `options.concurrency` | `{ group: (ctx) => string; cancelInProgress?: boolean; max?: number }` | no | Workflow-scoped concurrency. See [Concurrency](https://docs.kici.dev/user/concurrency/). |
37
+ | `options.timeout` | `number` | no | Whole-run wall-clock timeout in milliseconds across all jobs. On breach the orchestrator cancels the run and marks it timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
38
+ | `options.approval` | `ApprovalConfig` | no | Pause for a manual human approval before the whole workflow dispatches. See [Approval gates](https://docs.kici.dev/user/approvals/). |
37
39
 
38
40
  **Returns:** `Workflow` -- an immutable workflow definition.
39
41
 
@@ -59,34 +61,42 @@ function job(options: JobOptions): Job;
59
61
 
60
62
  **Parameters:**
61
63
 
62
- | Parameter | Type | Required | Description |
63
- | -------------------------- | ---------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
64
- | `name` | `string` | no | Job name (auto-generated UUID if omitted) |
65
- | `options.runsOn` | `RunsOn` | yes | Runner label(s) and optional exclusions (see below) |
66
- | `options.steps` | `StepInput[]` | yes (or use `run`) | Steps to execute in order. Mutually exclusive with `run`. |
67
- | `options.run` | `(ctx) => Promise<unknown>` | yes (or use `steps`) | Single-step shorthand -- see [Single-step job shorthand](https://docs.kici.dev/user/sdk/core/#single-step-job-shorthand). Mutually exclusive with `steps`. |
68
- | `options.needs` | `NeedsEntry[]` | no | Job dependencies (must complete first) -- see [Job dependencies (`needs`)](https://docs.kici.dev/user/sdk/core/#job-dependencies-needs) |
69
- | `options.rules` | `Rule[]` | no | Conditions for conditional execution |
70
- | `options.description` | `string` | no | Human-readable description |
71
- | `options.matrix` | `Matrix` | no | Matrix configuration for job expansion |
72
- | `options.include` | `MatrixInclude[]` | no | Additional matrix combinations |
73
- | `options.exclude` | `MatrixExclude[]` | no | Matrix combinations to remove |
74
- | `options.checkout` | `boolean` | no (default: `true`) | When `false`, agent skips git clone. Useful for deploy/notify jobs. |
75
- | `options.container` | `string \| ContainerConfig` | no | Docker image for job execution. String form is the image name; object form adds `env`. All steps run inside the container. |
76
- | `options.sandbox` | `{ capabilities?: string[]; network?: 'default' \| 'none' \| 'host' }` | no | Per-job container sandbox escape hatch (container jobs only). Request extra Linux capabilities / host networking; granted only within your operator's allow-list, else the run fails at dispatch. See below. |
77
- | `options.environment` | `string \| ((event) => string \| Promise<string>)` | no | Deployment environment for this job. Static string or async/dynamic function -- see [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
78
- | `options.env` | `Record<string, string> \| ((event) => Record<string, string>)` | no | Environment variables. Static object or async/dynamic function -- see [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
79
- | `options.concurrencyGroup` | `string \| ((event) => string \| Promise<string>)` | no | Concurrency group name (defaults to environment name) -- see [Concurrency](https://docs.kici.dev/user/concurrency/). |
80
- | `options.onCancel` | `HookInput` | no | Hook that runs when the job is cancelled |
81
- | `options.cleanup` | `HookInput` | no | Hook that always runs after completion |
82
- | `options.onSuccess` | `HookInput` | no | Hook that runs when the job succeeds |
83
- | `options.onFailure` | `HookInput` | no | Hook that runs when the job fails |
84
- | `options.beforeStep` | `HookInput` | no | Hook that runs before each step |
85
- | `options.afterStep` | `HookInput` | no | Hook that runs after each step |
86
- | `options.gracePeriod` | `number` | no | Seconds before SIGKILL after SIGTERM during cancellation -- see [Hooks](https://docs.kici.dev/user/hooks/#hook-timeout). |
87
- | `options.timeout` | `number` | no | Total job wall-clock timeout in milliseconds (init + all steps + hooks). On breach the job is aborted and reported timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
88
- | `options.resources` | `ResourceRequest` | no | Per-job CPU / memory request and limit. See [Per-job resources](https://docs.kici.dev/user/sdk/core/#per-job-resources) below. |
89
- | `options.init` | `InitConfig` | no | Per-job initialization run after clone, before steps -- provisions a toolchain. A generic config, a typed preset (`'mise'` / `{ mise }`), `'auto'`, or `false`. See [Per-job init](https://docs.kici.dev/user/sdk/core/#per-job-init) below. |
64
+ | Parameter | Type | Required | Description |
65
+ | ------------------------------ | -------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
66
+ | `name` | `string` | no | Job name (auto-generated UUID if omitted) |
67
+ | `options.runsOn` | `RunsOn` | yes (or `runsOnAll`) | Single-agent targeting -- runner label(s) and optional exclusions (see below). Mutually exclusive with `runsOnAll`. |
68
+ | `options.runsOnAll` | `RunsOnAllInput` | yes (or `runsOn`) | Host fan-out -- one pinned execution per roster host matching the predicate. Mutually exclusive with `runsOn`. See [runsOnAll host fan-out](https://docs.kici.dev/user/sdk/runs-on-all/). |
69
+ | `options.onUnreachable` | `'skip' \| 'fail' \| 'hold'` | no (default: `hold`) | Failure policy for unreachable durable hosts. `skip` omits them, `fail` fails the run, `hold` queues a pinned child and waits. Only meaningful alongside `runsOnAll`. |
70
+ | `options.includeUninitialized` | `boolean` | no (default: `false`) | Widen a `runsOnAll` fan-out to declared-but-un-agented hosts -- a matching host with no live agent gets a temporary init-runner brought up over SSH. Only meaningful alongside `runsOnAll`. |
71
+ | `options.steps` | `StepInput[]` | yes (or use `run`) | Steps to execute in order. Mutually exclusive with `run`. |
72
+ | `options.run` | `(ctx) => Promise<unknown>` | yes (or use `steps`) | Single-step shorthand -- see [Single-step job shorthand](https://docs.kici.dev/user/sdk/core/#single-step-job-shorthand). Mutually exclusive with `steps`. |
73
+ | `options.needs` | `Array<Job \| string \| DynamicGroupRef \| { name; when? } \| { group; when? }>` | no | Job dependencies (must complete first) -- see [Job dependencies (`needs`)](https://docs.kici.dev/user/sdk/core/#job-dependencies-needs) |
74
+ | `options.rules` | `Rule[]` | no | Conditions for conditional execution |
75
+ | `options.description` | `string` | no | Human-readable description |
76
+ | `options.matrix` | `Matrix` | no | Matrix configuration for job expansion |
77
+ | `options.include` | `MatrixInclude[]` | no | Additional matrix combinations |
78
+ | `options.exclude` | `MatrixExclude[]` | no | Matrix combinations to remove |
79
+ | `options.maxParallel` | `number` | no | Fan-out concurrency width -- the maximum number of fan-out children (matrix combinations or `runsOnAll` hosts) running at once. A sliding window; `1` is strictly serial. Must be `>= 1`. |
80
+ | `options.failFast` | `boolean` | no (default: `false`) | Halt the fan-out on the first child failure: stop releasing new children and skip the ones still held. Applies to both matrix and `runsOnAll` fan-out. |
81
+ | `options.checkout` | `boolean` | no (default: `true`) | When `false`, agent skips git clone. Useful for deploy/notify jobs. |
82
+ | `options.container` | `string \| ContainerConfig` | no | Docker image for job execution. String form is the image name; object form adds `env`. All steps run inside the container. |
83
+ | `options.sandbox` | `{ capabilities?: string[]; network?: 'default' \| 'none' \| 'host' }` | no | Per-job container sandbox escape hatch (container jobs only). Request extra Linux capabilities / host networking; granted only within your operator's allow-list, else the run fails at dispatch. See below. |
84
+ | `options.context` | `string \| ((event) => string \| Promise<string>)` | no | Bound context for this job -- the secret / variable scope it resolves against. Static string or async/dynamic function -- see [Contexts](https://docs.kici.dev/user/contexts/) and [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
85
+ | `options.contexts` | `(string \| ((event) => string \| Promise<string>))[]` | no | Bound contexts in merge order (later entries override earlier on name collisions). Mutually exclusive with `context`. |
86
+ | `options.env` | `Record<string, string> \| ((event) => Record<string, string>)` | no | Environment variables. Static object or async/dynamic function -- see [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
87
+ | `options.concurrencyGroup` | `string \| ((event) => string \| Promise<string>)` | no | Concurrency group name. Defaults to the first bound context's name -- see [Concurrency](https://docs.kici.dev/user/concurrency/). |
88
+ | `options.onCancel` | `HookInput` | no | Hook that runs when the job is cancelled |
89
+ | `options.cleanup` | `HookInput` | no | Hook that always runs after completion |
90
+ | `options.onSuccess` | `HookInput` | no | Hook that runs when the job succeeds |
91
+ | `options.onFailure` | `HookInput` | no | Hook that runs when the job fails |
92
+ | `options.beforeStep` | `HookInput` | no | Hook that runs before each step |
93
+ | `options.afterStep` | `HookInput` | no | Hook that runs after each step |
94
+ | `options.gracePeriod` | `number` | no | Seconds before SIGKILL after SIGTERM during cancellation -- see [Hooks](https://docs.kici.dev/user/hooks/#hook-timeout). |
95
+ | `options.timeout` | `number` | no | Total job wall-clock timeout in milliseconds (init + all steps + hooks). On breach the job is aborted and reported timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
96
+ | `options.resources` | `ResourceRequest` | no | Per-job CPU / memory request and limit. See [Per-job resources](https://docs.kici.dev/user/sdk/core/#per-job-resources) below. |
97
+ | `options.init` | `InitConfig` | no | Per-job initialization run after clone, before steps -- provisions a toolchain. A generic config, a typed preset (`'mise'` / `{ mise }`), `'auto'`, or `false`. See [Per-job init](https://docs.kici.dev/user/sdk/core/#per-job-init) below. |
98
+ | `options.cache` | `CacheInput` | no | Declarative cache: restored before steps, saved after the job on a key miss. See [Caching](https://docs.kici.dev/user/sdk/caching/). |
99
+ | `options.approval` | `ApprovalConfig` | no | Pause for a manual human approval before this job dispatches. See [Approval gates](https://docs.kici.dev/user/approvals/). |
90
100
 
91
101
  **Returns:** `Job` -- an immutable job definition.
92
102
 
@@ -951,7 +961,7 @@ Source: https://docs.kici.dev/user/sdk/rules-matrix-dynamic/
951
961
 
952
962
  Rules control conditional execution of workflows and jobs. A rule that returns `false` (or whose check function returns `false`) prevents execution.
953
963
 
954
- A rule's check function that **returns `false`** cleanly skips the job or step. A check function that **throws** is treated as an evaluation failure, not a skip: the job or step **fails** with the error surfaced (both on a remote run and when running locally with `kici run --local`), so a broken rule can never silently pass as a green run. For example, `rule('main only', (ctx) => ctx.event.ref.endsWith('main'))` throws on an event whose `ref` is undefined — that run fails with the error instead of quietly skipping every step. Fix the thrown error (guard the access) rather than relying on the skip.
964
+ A rule's check function that **returns `false`** cleanly skips the job or step. A check function that **throws** counts as an evaluation failure, not a skip. The job or step **fails** with the error surfaced (both on a remote run and when running locally with `kici run --local`), so a broken rule can never silently pass as a green run. For example, `rule('main only', (ctx) => ctx.event.ref.endsWith('main'))` throws on an event whose `ref` is undefined — that run fails with the error instead of quietly skipping every step. Fix the thrown error (guard the access) rather than relying on the skip.
955
965
 
956
966
  ### rule(label) / rule(label, check)
957
967
 
@@ -1002,13 +1012,15 @@ type RuleCheckFn = (ctx: RuleContext) => Promise<boolean> | boolean;
1002
1012
 
1003
1013
  Can be sync or async. Receives a `RuleContext`:
1004
1014
 
1005
- | Property | Type | Description |
1006
- | -------------------- | ----------------------------------------- | --------------------------------------------------------------------- |
1007
- | `event` | `EventPayload` | The triggering event payload (discriminated union — narrow on `type`) |
1008
- | `changedFiles` | `string[]` | Files changed in this event (see availability note below) |
1009
- | `changedFilesStatus` | `'fetched' \| 'unavailable' \| 'skipped'` | Whether `changedFiles` is available |
1010
- | `env` | `Record<string, string\|undefined>` | Environment variables |
1011
- | `$` | zx shell | Shell executor for running commands |
1015
+ | Property | Type | Description |
1016
+ | -------------------- | ----------------------------------------- | ----------------------------------------------------------------------- |
1017
+ | `event` | `EventPayload` | The triggering event payload (discriminated union — narrow on `type`) |
1018
+ | `changedFiles` | `string[]` | Files changed in this event (see availability note below) |
1019
+ | `changedFilesStatus` | `'fetched' \| 'unavailable' \| 'skipped'` | Whether `changedFiles` is available |
1020
+ | `sourceRepo` | `RepoInfo \| undefined` | Repo whose event triggered the run, when the evaluation has a checkout |
1021
+ | `workflowRepo` | `RepoInfo \| undefined` | Repo that registered the workflow (same repo outside a global workflow) |
1022
+ | `env` | `Record<string, string\|undefined>` | Environment variables |
1023
+ | `$` | zx shell | Shell executor for running commands |
1012
1024
 
1013
1025
  `changedFiles` is available on `push` and `pull_request` events — the agent computes the diff from its checkout, so no `paths:` trigger is required. It is `unavailable` for events with no diff (`schedule`, `tag`, `manual_schedule`), and in the rare case where the diff cannot be computed (e.g. a history deeper than the agent's bounded fetch). Reading `changedFiles` when it is unavailable throws and fails the job, so guard with `changedFilesStatus` first when a rule can run on such events:
1014
1026
 
@@ -1019,6 +1031,8 @@ rule('has source changes', (ctx) => {
1019
1031
  });
1020
1032
  ```
1021
1033
 
1034
+ The throw is a `ChangedFilesUnavailableError` (exported from `@kici-dev/sdk`, carrying the `changedFilesStatus` and `eventType` that produced it). `evaluateRules()` deliberately re-throws it rather than folding it into a `passed=false` skip, so a path-based gate fails loudly instead of silently mis-evaluating.
1035
+
1022
1036
  ### evaluateRules(rules, context, label, onRuleResult?)
1023
1037
 
1024
1038
  Evaluate an array of rules sequentially with fail-fast behavior. Stops on the first failure.
@@ -1291,12 +1305,18 @@ type DynamicJobFn = (context: DynamicJobContext) => Promise<Job[]>;
1291
1305
 
1292
1306
  Receives a `DynamicJobContext`:
1293
1307
 
1294
- | Property | Type | Description |
1295
- | -------- | ----------------------------------- | --------------------------- |
1296
- | `$` | zx shell | Shell executor |
1297
- | `ctx` | `{ workflow, event? }` | Workflow metadata and event |
1298
- | `log` | `Logger` | Structured logger |
1299
- | `env` | `Record<string, string\|undefined>` | Environment variables |
1308
+ | Property | Type | Description |
1309
+ | -------------- | ----------------------------------- | ----------------------------------------------------------------------- |
1310
+ | `$` | zx shell | Shell executor |
1311
+ | `ctx` | `{ workflow, event? }` | Workflow metadata and event |
1312
+ | `log` | `Logger` | Structured logger |
1313
+ | `env` | `Record<string, string\|undefined>` | Environment variables |
1314
+ | `sourceRepo` | `RepoInfo \| undefined` | Repo whose event triggered the run, when the evaluation has a checkout |
1315
+ | `workflowRepo` | `RepoInfo \| undefined` | Repo that registered the workflow (same repo outside a global workflow) |
1316
+
1317
+ `RepoInfo` carries `path` — an absolute path to that repo's checkout — plus optional `ref` and `sha`; guard before reading either, since an event that carries no single ref leaves them undefined. In a [global workflow](https://docs.kici.dev/user/global-workflows/) `sourceRepo` and `workflowRepo` are different repos, so one generator can produce a different job set per source repo.
1318
+
1319
+ **`sourceRepo.path` is not stable across calls.** A generator is invoked once to discover the job set and again to extract the step closures of the job being run; both see the same tree at the same commit, but not necessarily the same path or even the same machine. Read _through_ it, and derive job names from what the tree contains — never from the path itself, or the second call produces different names and the run fails the determinism check.
1300
1320
 
1301
1321
  ```typescript
1302
1322
  const discoverJobs: DynamicJobFn = async ({ $ }) => {
@@ -1489,6 +1509,182 @@ workflow runs rather than being silently dropped, and the delivery is recorded
1489
1509
  as degraded. GitHub always provides an exact list; a transient API error fails
1490
1510
  loudly, not as empty.
1491
1511
 
1512
+ ### Content requirements (`requires`)
1513
+
1514
+ Where `paths` filters on **which files changed**, `requires` filters on **what
1515
+ those files contain**. It is a declarative filter on the `pr()`, `push()`, and
1516
+ `tag()` triggers: a list of queries over the bytes of named source files, read
1517
+ at the event's commit. The orchestrator evaluates it as pure data before
1518
+ dispatching — it reads only the referenced files, never clones the whole
1519
+ repository, and never runs any of your workflow code. A workflow whose `requires`
1520
+ does not pass is simply not dispatched.
1521
+
1522
+ Each entry is a `ContentRequirement`:
1523
+
1524
+ ```typescript
1525
+ interface ContentRequirement {
1526
+ file: string; // repo-relative path to query
1527
+ format?: 'auto' | 'json' | 'yaml' | 'text'; // how to parse the file (default: 'auto')
1528
+ exists?: string[]; // JSONPath expressions that must each resolve to ≥1 node (json/yaml)
1529
+ match?: Record<string, unknown>; // JSONPath → expected value; every one must match (json/yaml)
1530
+ not?: Record<string, unknown>; // JSONPath → value; passes only when NONE match (json/yaml)
1531
+ contains?: string | string[]; // literal substrings, all of which must appear (text only)
1532
+ notContains?: string | string[]; // literal substrings, none of which may appear (text only)
1533
+ matches?: string | RegExp | (string | RegExp)[]; // regexes, all of which must match (text only)
1534
+ notMatches?: string | RegExp | (string | RegExp)[]; // regexes, none of which may match (text only)
1535
+ ignoreCase?: boolean; // applies to contains/notContains only (default: false)
1536
+ absent?: boolean; // passes only when the file does NOT exist
1537
+ }
1538
+ ```
1539
+
1540
+ **Format.** `format: 'auto'` (the default) picks the parser by extension:
1541
+ `.json` → JSON, `.yaml` / `.yml` → YAML, everything else → text. Set `format`
1542
+ explicitly to override — e.g. treat an extensionless file as JSON, or read a
1543
+ `.json` file as raw text. JSON and YAML both parse to an object, so the JSONPath
1544
+ keys (`exists` / `match` / `not`) work identically over either; `text` files are
1545
+ queried by `contains`, `notContains`, `matches`, and `notMatches` over the raw
1546
+ bytes.
1547
+
1548
+ **Query keys.**
1549
+
1550
+ - **`exists`** — an array of JSONPath expressions; each must resolve to at least
1551
+ one node in the parsed document.
1552
+ - **`match`** — a JSONPath → expected-value map; every expression must match. An
1553
+ expected value is an exact value, a regex string in `/pattern/flags` form
1554
+ (against a string node), or an array of acceptable values (any one matches).
1555
+ - **`not`** — the same map shape, inverted: the entry passes only when **none** of
1556
+ the expressions match.
1557
+ - **`contains` / `notContains`** — literal substrings tested against the raw file
1558
+ text. Every entry must be present (`contains`) or absent (`notContains`). No
1559
+ escaping needed (text format only).
1560
+ - **`matches`** — one or several regexes (a `RegExp` or `/pattern/flags` string),
1561
+ each of which must match the raw file text (text format only).
1562
+ - **`notMatches`** — the inverse of `matches`: the entry passes only when none of
1563
+ the regexes match (text format only).
1564
+ - **`ignoreCase`** — case-insensitive `contains` / `notContains` only; a regex
1565
+ carries its own flags. Default false.
1566
+ - **`absent: true`** — passes only when the file does **not** exist at the event's
1567
+ commit. It is mutually exclusive with the query keys above.
1568
+ - A bare `{ file }` with no query key requires the file to **exist**.
1569
+
1570
+ The keys inside one entry are AND-ed, and the entries in a `requires` list are
1571
+ AND-ed with each other. An empty or absent `requires` matches everything, exactly
1572
+ like `paths`.
1573
+
1574
+ **Examples:**
1575
+
1576
+ ```typescript
1577
+ // Only run CI when package.json declares a `ci` script.
1578
+ push({ branches: 'main', requires: [{ file: 'package.json', exists: ['$.scripts.ci'] }] });
1579
+
1580
+ // Deploy only when the service config enables it (YAML, matched by value).
1581
+ push({
1582
+ branches: 'main',
1583
+ requires: [{ file: 'service.yaml', match: { '$.deploy.enabled': true } }],
1584
+ });
1585
+
1586
+ // Only run when the Dockerfile builds from a Node base image (raw-text regex).
1587
+ pr({ requires: [{ file: 'Dockerfile', format: 'text', matches: '/^FROM node:/m' }] });
1588
+
1589
+ // Skip the workflow whenever a repo carries an opt-out marker file.
1590
+ push({ requires: [{ file: '.skip-ci', absent: true }] });
1591
+
1592
+ // Combine filters: a tag build that requires a version file AND forbids a draft flag.
1593
+ tag({
1594
+ patterns: ['v*'],
1595
+ requires: [
1596
+ { file: 'VERSION', matches: '/^\\d+\\.\\d+\\.\\d+$/' },
1597
+ { file: 'release.json', not: { '$.draft': true } },
1598
+ ],
1599
+ });
1600
+ ```
1601
+
1602
+ **Fail-visible evaluation.** Files are read at the event's commit. If a
1603
+ referenced file is larger than **1 MiB**, or fails to parse for its format, the
1604
+ requirement is **indeterminate** — the candidate workflow is dropped and does
1605
+ **not** run. A `requires` that cannot be evaluated never silently passes.
1606
+
1607
+ **Compile-time validation.** `kici compile` rejects a malformed requirement before
1608
+ it ever reaches the orchestrator: a raw-text key that is invalid or catastrophic
1609
+ (ReDoS-prone) is rejected by a safe-regex check; a text file cannot carry a
1610
+ JSON/YAML query key (`exists` / `match` / `not`) and a JSON/YAML file cannot carry a
1611
+ raw-text key; `absent` cannot be combined with a query key; and an explicit
1612
+ `format` with no query key is rejected as having nothing to check.
1613
+
1614
+ ### Commit-message filters (`commitMessage`)
1615
+
1616
+ Where `requires` filters on what the repository's **files** contain,
1617
+ `commitMessage` filters on what the **event** says. It is a declarative filter on
1618
+ the `pr()`, `push()`, and `tag()` triggers. The orchestrator evaluates it
1619
+ directly from the webhook payload: no file is fetched, and no repository is
1620
+ cloned. For an organization-wide workflow it dispatches no evaluation job. It is
1621
+ the cheapest gate available.
1622
+
1623
+ The text it tests is the **full head-commit message** — subject and body — for
1624
+ `push` and `tag`, and the **title plus body** for pull-request events.
1625
+
1626
+ ```typescript
1627
+ interface TextMatch {
1628
+ contains?: string | string[]; // every needle must be present
1629
+ notContains?: string | string[]; // no needle may be present
1630
+ matches?: string | RegExp | (string | RegExp)[]; // every regex must match
1631
+ notMatches?: string | RegExp | (string | RegExp)[]; // no regex may match
1632
+ ignoreCase?: boolean; // applies to contains/notContains only (default: false)
1633
+ }
1634
+ ```
1635
+
1636
+ **Every entry in a list is a conjunct.** `contains: ['a', 'b']` passes only when
1637
+ the text contains both, and the keys AND together. To express OR, declare two
1638
+ triggers — a workflow's trigger list already matches on the first one that fits:
1639
+
1640
+ ```typescript
1641
+ // AND — one trigger.
1642
+ push({ commitMessage: { contains: ['release:', 'approved'] } });
1643
+
1644
+ // OR — two triggers.
1645
+ on: [
1646
+ push({ branches: 'main', commitMessage: { contains: 'deploy:' } }),
1647
+ push({ branches: 'main', commitMessage: { contains: 'release:' } }),
1648
+ ];
1649
+ ```
1650
+
1651
+ Needles are **literal substrings** — no glob, no regex, no escaping, so a needle
1652
+ containing `.*` matches only the literal `.*`. Use `matches` / `notMatches` for a
1653
+ pattern; both accept a `RegExp` literal, and the `m` flag reaches the body:
1654
+
1655
+ ```typescript
1656
+ // The single most common use: skip marker commits.
1657
+ push({ branches: 'main', commitMessage: { notContains: ['[skip ci]', '[ci skip]'] } });
1658
+
1659
+ // Ignore dependency-bump noise across an organization.
1660
+ push({ commitMessage: { notMatches: /^chore\(deps\):/ } });
1661
+
1662
+ // Require a conventional-commit prefix and forbid a WIP marker.
1663
+ pr({ target: 'main', commitMessage: { matches: /^(feat|fix)\(/, notContains: 'WIP' } });
1664
+
1665
+ // Match a trailer in the commit body.
1666
+ push({ commitMessage: { matches: /^Fixes: #\d+$/m } });
1667
+ ```
1668
+
1669
+ `ignoreCase` affects `contains` and `notContains` only — a regex already carries
1670
+ its own flags, so write `/^feat:/i` rather than expecting `ignoreCase` to reach
1671
+ it.
1672
+
1673
+ **Fail-visible evaluation.** Some events carry no message at all. A
1674
+ branch-deletion push has no head commit, and a self-hosted forge may publish
1675
+ none. The trigger then does **not** match, and the decision trace records it as
1676
+ `indeterminate` rather than as an exclusion. A `commitMessage` filter that cannot
1677
+ be evaluated never silently passes.
1678
+
1679
+ **Compile-time validation.** `kici compile` rejects a malformed matcher. It
1680
+ refuses:
1681
+
1682
+ - a matcher with no query key;
1683
+ - an `ignoreCase` that would affect nothing;
1684
+ - an empty needle list;
1685
+ - an empty-string needle (it would match every text);
1686
+ - a regex that is invalid or catastrophic (ReDoS-prone).
1687
+
1492
1688
  ### tag()
1493
1689
 
1494
1690
  Create a tag trigger. Returns a frozen `TagTriggerConfig`.
@@ -2239,6 +2435,36 @@ workflow('post-deploy', {
2239
2435
 
2240
2436
  The orchestrator automatically emits system events for workflow and job completions. You do not need to call `ctx.emit()` for these -- they are generated by the orchestrator after execution. Listen for them with `workflowComplete()` and `jobComplete()` triggers.
2241
2437
 
2438
+ ### Event scaler events
2439
+
2440
+ The [event scaler backend](https://docs.kici.dev/operator/orchestrator/event-scaler/) emits two reserved events that your provisioning and teardown workflows subscribe to. The SDK exports their names and payload schemas, so a workflow imports the contract instead of re-declaring it.
2441
+
2442
+ | Export | What it is |
2443
+ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
2444
+ | `SCALER_EVENT_NAMES` | The two reserved event names: `SCALER_EVENT_NAMES.scaleUp` and `SCALER_EVENT_NAMES.scaleDown`. |
2445
+ | `ScalerScaleUpPayload` | Schema of the scale-up payload. `.parse(ctx.rawPayload)` returns the typed payload and rejects a malformed one. |
2446
+ | `ScalerScaleDownPayload` | Schema of the scale-down payload, including the narrowed `reason`. |
2447
+ | `ScaleDownReason` | Why the scaler asked for a teardown: `idle`, `job-complete`, `heartbeat-timeout`, `spawn-timeout`, `drain`, `shutdown`. |
2448
+
2449
+ ```ts
2450
+ import { workflow, job, kiciEvent, SCALER_EVENT_NAMES, ScalerScaleUpPayload } from '@kici-dev/sdk';
2451
+
2452
+ export default workflow('provision', {
2453
+ on: [kiciEvent({ name: SCALER_EVENT_NAMES.scaleUp, match: { '$.scalerName': 'hetzner' } })],
2454
+ jobs: [
2455
+ job('provision', {
2456
+ runsOn: ['kici:os:linux'],
2457
+ run: async (ctx) => {
2458
+ const payload = ScalerScaleUpPayload.parse(ctx.rawPayload);
2459
+ ctx.log.info(`provision agent ${payload.agentId}`);
2460
+ },
2461
+ }),
2462
+ ],
2463
+ });
2464
+ ```
2465
+
2466
+ These names are reserved. `ctx.emit()` rejects any event name that starts with `kici.`, so a workflow step cannot forge a scaler event. For every payload field, see the [event contract reference](https://docs.kici.dev/operator/orchestrator/event-scaler-events/). For complete provisioning and teardown workflows, see [autoscaling workflows](https://docs.kici.dev/user/workflows/autoscaling-workflows/).
2467
+
2242
2468
  ---
2243
2469
 
2244
2470
  ## SDK reference
@@ -8,19 +8,21 @@ The full markdown bundle of every page indexed here is available at https://docs
8
8
 
9
9
  Each bundle below is a self-contained markdown file for one authoring task. Fetch only the one your task needs instead of the full bundle:
10
10
 
11
- - [getting-started](https://docs.kici.dev/llms-getting-started.txt) (51 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
12
- - [patterns](https://docs.kici.dev/llms-patterns.txt) (49 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
13
- - [sdk](https://docs.kici.dev/llms-sdk.txt) (110 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
14
- - [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (112 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
15
- - [cli](https://docs.kici.dev/llms-cli.txt) (185 KB) — Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift
16
- - [features](https://docs.kici.dev/llms-features.txt) (135 KB) — Workflow features: concurrency, contexts, secrets, approvals, provenance, events
17
- - [providers](https://docs.kici.dev/llms-providers.txt) (37 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
18
- - [architecture](https://docs.kici.dev/llms-architecture.txt) (92 KB) — How the runtime works: three-tier relay model, data flows, configuration
11
+ - [getting-started](https://docs.kici.dev/llms-getting-started.txt) (58 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
12
+ - [patterns](https://docs.kici.dev/llms-patterns.txt) (57 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
13
+ - [sdk](https://docs.kici.dev/llms-sdk.txt) (127 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
14
+ - [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (114 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
15
+ - [cli](https://docs.kici.dev/llms-cli.txt) (194 KB) — Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift
16
+ - [features](https://docs.kici.dev/llms-features.txt) (90 KB) — Deployment contexts, scoped secrets, private registries, approvals, provenance, dashboard and account access
17
+ - [features-execution](https://docs.kici.dev/llms-features-execution.txt) (120 KB) — Concurrency, dynamic values, events, container jobs, environment variables, global workflows, idempotent steps
18
+ - [providers](https://docs.kici.dev/llms-providers.txt) (35 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
19
+ - [architecture](https://docs.kici.dev/llms-architecture.txt) (99 KB) — How the runtime works: three-tier relay model, data flows, configuration
19
20
 
20
21
  ## Getting started
21
22
 
22
23
  - [User guide](https://docs.kici.dev/user/): Writing and testing CI/CD workflows in TypeScript, running on infrastructure you control
23
24
  - [How your workflow code executes](https://docs.kici.dev/user/execution-model/): When and where your workflow TypeScript runs — compile time, orchestrator time, and agent time
25
+ - [Getting help](https://docs.kici.dev/user/getting-help/): How to report a problem to KiCI and send the diagnostic context privately
24
26
  - [Getting started with workflows](https://docs.kici.dev/user/getting-started/): Install the SDK, write your first workflow, compile and test locally
25
27
  - [Migrating from GitHub Actions](https://docs.kici.dev/user/migrating-from-github-actions/): Map GitHub Actions concepts to KiCI, translate a real workflow side-by-side, and see what has no equivalent yet
26
28
  - [5-minute quickstart](https://docs.kici.dev/user/quickstart/): Stand up a KiCI orchestrator + agent and run your first workflow
@@ -30,6 +32,7 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
30
32
 
31
33
  - [Basic workflow patterns](https://docs.kici.dev/user/patterns/basic/): Basic CI, PR-only / push-only filters, multiple triggers, manual-only workflows
32
34
  - [Conditionals & matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/): Conditional execution with rules, matrix builds (static + dynamic), dynamic job generation
35
+ - [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/): Authenticated git in a workflow — declaring credentials from the secrets backend, and pushing
33
36
  - [Host restart & wait-for-alive](https://docs.kici.dev/user/patterns/host-restart/): Reboot the host a workflow runs on and continue after it comes back
34
37
  - [Integration patterns](https://docs.kici.dev/user/patterns/integrations/): Workflow chaining, generic webhooks, Stripe, self-hosted git forges, plain GitHub repos
35
38
  - [Pattern reference](https://docs.kici.dev/user/patterns/reference/): Step context, examples repository, GitHub check run output — cross-cutting reference for all patterns
@@ -59,7 +62,7 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
59
62
 
60
63
  - [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/): Point a coding agent at KiCI's MCP server and let it trigger, read, and re-run your CI under your own identity.
61
64
  - [CLI authentication](https://docs.kici.dev/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
62
- - [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, preview, local, fixture, types, workflows, hook, docs, run, runs, approve, reject, login, logout, init, org, pat, secrets, admin, orchestrators, endpoints, notifications, verify-attestation, diagnostics, doctor
65
+ - [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, preview, local, fixture, types, workflows, hook, docs, run, runs, approve, reject, login, logout, init, org, pat, secrets, admin, orchestrators, endpoints, notifications, verify-attestation, diagnostics, doctor, report
63
66
  - [Common failures](https://docs.kici.dev/user/common-failures/): Symptom-to-fix reference for the failures workflow authors hit most — no jobs dispatched, lock-file drift, missing webhooks, and agents that won't connect
64
67
  - [Lifecycle hooks](https://docs.kici.dev/user/hooks/): SDK hook API for cancel, cleanup, success, failure, and step-level callbacks
65
68
  - [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/): Keep the lock file in sync with your workflow source and avoid drift
@@ -70,21 +73,26 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
70
73
  - [kici: notifications & diagnostics](https://docs.kici.dev/user/cli/notifications-and-diagnostics/): Notification channels, attestation verification, and diagnostics
71
74
  - [kici: runs & approvals](https://docs.kici.dev/user/cli/runs-and-approvals/): Run push and inspection plus approval / rejection of held runs
72
75
 
73
- ## Workflow features
76
+ ## Workflow features: access and approval
74
77
 
75
78
  - [Account and sign-in](https://docs.kici.dev/user/account-and-login/): How your KiCI account relates to sign-in methods, and how to change the way you sign in.
76
79
  - [Approval gates](https://docs.kici.dev/user/approvals/): Pause a workflow for human sign-off at step, job, or workflow granularity with approval
77
- - [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
78
80
  - [Contexts](https://docs.kici.dev/user/contexts/): Configure deployment contexts with variables, secrets, and protection rules
79
81
  - [Dashboard](https://docs.kici.dev/user/dashboard/): Web UI for monitoring workflow runs, managing sources, secrets, and organization settings.
80
- - [Dynamic values](https://docs.kici.dev/user/dynamic-values/)
82
+ - [Private npm registries](https://docs.kici.dev/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
83
+ - [Build provenance and attestations](https://docs.kici.dev/user/provenance/): Generate and verify signed SLSA provenance for the artifacts your workflows build
84
+ - [Secrets](https://docs.kici.dev/user/secrets/): How to access secrets in KiCI workflow steps
85
+
86
+ ## Workflow features: execution
87
+
88
+ - [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
89
+ - [Container jobs](https://docs.kici.dev/user/container-jobs/): Run a job inside any container image, including a private one, without that image shipping Node or git
90
+ - [Dynamic values](https://docs.kici.dev/user/dynamic-values/): Compute a job's context, env, and concurrencyGroup at runtime from the incoming event
81
91
  - [Environment variables](https://docs.kici.dev/user/env-vars/): Environment variable reference for the CLI
82
92
  - [Event system](https://docs.kici.dev/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
83
93
  - [Global workflows](https://docs.kici.dev/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
84
94
  - [Idempotent steps and check mode](https://docs.kici.dev/user/idempotent-steps/): Declare desired state with a step check facet, then run in apply or --check preview mode
85
- - [Private npm registries](https://docs.kici.dev/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
86
- - [Build provenance and attestations](https://docs.kici.dev/user/provenance/): Generate and verify signed SLSA provenance for the artifacts your workflows build
87
- - [Secrets](https://docs.kici.dev/user/secrets/): How to access secrets in KiCI workflow steps
95
+ - [Autoscaling workflows](https://docs.kici.dev/user/workflows/autoscaling-workflows/): Write TypeScript provisioning and teardown workflows that boot and delete ephemeral cloud agents in response to the event scaler
88
96
 
89
97
  ## Providers
90
98
 
@@ -7,6 +7,21 @@ export declare const PLANE_STAMP_VERSION = 3;
7
7
  /**
8
8
  * Root directory of the local dev plane's state, following the same
9
9
  * `KICI_CONFIG_DIR` → `~/.kici` convention the rest of the CLI uses.
10
+ *
11
+ * RESOLVED THROUGH SYMLINKS, deliberately. The plane is a singleton on fixed
12
+ * ports, so a caller may reach it through a config dir that only symlinks
13
+ * `local` at the durable one — `pnpm deploy:stg` does exactly that, to run
14
+ * against a throwaway config dir carrying no credentials while still reusing
15
+ * the warm plane.
16
+ *
17
+ * Without resolving, the plane's Postgres is started with a data directory
18
+ * addressed through that ephemeral path and keeps it open. When the caller
19
+ * removes its temp dir, Postgres PANICs — `could not open file
20
+ * "<tmp>/local/pgdata/global/pg_control"` — and shuts the whole plane down,
21
+ * taking every later phase with it. Resolving first means Postgres only ever
22
+ * sees the durable path, so a caller's temp dir can come and go beneath it.
23
+ *
24
+ * A path that does not exist yet resolves to itself: a fresh plane creates it.
10
25
  */
11
26
  export declare function planeRoot(): string;
12
27
  /**