@intentius/chant-lexicon-k8s 0.56.0 → 0.58.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 (47) hide show
  1. package/dist/api/sweep-types.d.ts +1 -2
  2. package/dist/api/sweep-types.d.ts.map +1 -1
  3. package/dist/composites/operator-stack.d.ts +19 -18
  4. package/dist/composites/operator-stack.d.ts.map +1 -1
  5. package/dist/config.d.ts +4 -4
  6. package/dist/deep-observe-hooks.d.ts +4 -4
  7. package/dist/deep-observe.d.ts +23 -16
  8. package/dist/deep-observe.d.ts.map +1 -1
  9. package/dist/export-resources.d.ts +1 -1
  10. package/dist/integrity.json +3 -3
  11. package/dist/lint/audit-catalog.d.ts.map +1 -1
  12. package/dist/lint/audit-lineage.d.ts +10 -0
  13. package/dist/lint/audit-lineage.d.ts.map +1 -0
  14. package/dist/manifest.json +1 -1
  15. package/dist/op/activities/argo.d.ts +4 -4
  16. package/dist/op/activities/argo.d.ts.map +1 -1
  17. package/dist/op/activities/index.d.ts +9 -7
  18. package/dist/op/activities/index.d.ts.map +1 -1
  19. package/dist/op/activities/kubectl.d.ts +7 -7
  20. package/dist/op/activities/kubectl.d.ts.map +1 -1
  21. package/dist/op/activities/wait-for-ready.d.ts +6 -5
  22. package/dist/op/activities/wait-for-ready.d.ts.map +1 -1
  23. package/dist/op/builders.d.ts +13 -13
  24. package/dist/skills/chant-k8s-argo.md +27 -13
  25. package/package.json +3 -3
  26. package/src/api/sweep-types.ts +1 -2
  27. package/src/composites/composites.test.ts +13 -2
  28. package/src/composites/operator-stack.ts +22 -20
  29. package/src/config.ts +4 -4
  30. package/src/crd/crd-sources.ts +4 -4
  31. package/src/deep-observe-hooks.ts +4 -4
  32. package/src/deep-observe.test.ts +219 -46
  33. package/src/deep-observe.ts +28 -21
  34. package/src/export-resources.ts +1 -1
  35. package/src/lint/audit-catalog.ts +5 -1
  36. package/src/lint/audit-lineage.ts +127 -0
  37. package/src/op/activities/argo.test.ts +7 -8
  38. package/src/op/activities/argo.ts +4 -4
  39. package/src/op/activities/index.ts +9 -7
  40. package/src/op/activities/kubectl.test.ts +5 -4
  41. package/src/op/activities/kubectl.ts +52 -67
  42. package/src/op/activities/wait-for-ready.test.ts +2 -2
  43. package/src/op/activities/wait-for-ready.ts +7 -7
  44. package/src/op/builders.ts +13 -13
  45. package/src/plugin.ts +1 -1
  46. package/src/serializer.test.ts +26 -0
  47. package/src/skills/chant-k8s-argo.md +27 -13
@@ -18,20 +18,20 @@
18
18
  * earlier step's declared output.
19
19
  *
20
20
  * `core`'s own `kubectlApply`/`waitForReady`/`ensureSecret` (in
21
- * `@intentius/chant/op`, re-exported from `@intentius/chant-lexicon-temporal`
22
- * for lexicon-agnostic single-import convenience) are UNCHANGED and produce
23
- * byte-identical `ActivityStep` output for the same inputs these are purely
24
- * additive. Deliberately not swapped into the temporal barrel: that would
25
- * make `@intentius/chant-lexicon-temporal` depend on this package (and on
26
- * helm, for `helmInstall`) at runtime, which is exactly the "a worker image
27
- * needs no kubectl binary" / "temporal stays product-agnostic" property the
21
+ * `@intentius/chant/op`, the lexicon-agnostic single-import barrel) are
22
+ * UNCHANGED and produce byte-identical `ActivityStep` output for the same
23
+ * inputs these are purely additive. Deliberately not swapped into core's
24
+ * barrel: that would make `@intentius/chant` depend on this package (and on
25
+ * helm, for `helmInstall`) at runtime, so a project that never touches
26
+ * Kubernetes would install both just to import anything from `op`. That is
27
+ * exactly the "core stays product-agnostic" property the
28
28
  * `op/activities/index.ts` module docs across this repo call out on purpose
29
- * (activities were moved OUT of temporal into per-product lexicons for this
30
- * reason; the step-builder layer shouldn't reintroduce the coupling from the
31
- * other direction). An author who wants the typed surface imports it from
32
- * here — `@intentius/chant-lexicon-k8s` — which a project using `kubectlApply`
33
- * already depends on; existing `@intentius/chant-lexicon-temporal` imports
34
- * keep working exactly as before, opting in only if the import is changed.
29
+ * (activities live in per-product lexicons for this reason; the step-builder
30
+ * layer shouldn't reintroduce the coupling from the other direction). An
31
+ * author who wants the typed surface imports it from here —
32
+ * `@intentius/chant-lexicon-k8s` — which a project using `kubectlApply`
33
+ * already depends on; existing `@intentius/chant/op` imports keep working
34
+ * exactly as before, opting in only if the import is changed.
35
35
  */
36
36
 
37
37
  import {
package/src/plugin.ts CHANGED
@@ -632,7 +632,7 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
632
632
  {
633
633
  file: "chant-k8s-argo.md",
634
634
  name: "chant-k8s-argo",
635
- description: "Argo CD composites — ArgoAppFor, ArgoAppSetForRegions, AppProject scoping, cluster registration, and the Argo-vs-Temporal split",
635
+ description: "Argo CD composites — ArgoAppFor, ArgoAppSetForRegions, AppProject scoping, cluster registration, and how a deploy splits between Argo and a chant Op",
636
636
  triggers: [
637
637
  { type: "context", value: "argo" },
638
638
  { type: "context", value: "argo cd" },
@@ -1,6 +1,7 @@
1
1
  import { describe, test, expect } from "vitest";
2
2
  import { k8sSerializer } from "./serializer";
3
3
  import { DECLARABLE_MARKER } from "@intentius/chant/declarable";
4
+ import { heldElsewhere } from "@intentius/chant";
4
5
  import {
5
6
  defaultLabels,
6
7
  defaultAnnotations,
@@ -69,6 +70,31 @@ describe("k8sSerializer", () => {
69
70
  expect(result).toContain("replicas: 2");
70
71
  });
71
72
 
73
+ // #2162 — the honest example the issue names: an HPA owns `spec.replicas`
74
+ // on a Deployment after the first apply. `heldElsewhere()` omits the field
75
+ // from the manifest entirely, every apply, so the API server defaults it
76
+ // once at creation and the HPA is free to write it from there without
77
+ // chant's own apply fighting it back.
78
+ test("a heldElsewhere() replicas field never reaches the manifest", () => {
79
+ const entities = new Map<string, any>();
80
+ entities.set(
81
+ "myApp",
82
+ mockResource("K8s::Apps::Deployment", {
83
+ metadata: { name: "my-app", labels: { app: "my-app" } },
84
+ spec: {
85
+ replicas: heldElsewhere<number>({ by: "hpa", reason: "the autoscaler owns replicas after the first apply" }),
86
+ selector: { matchLabels: { app: "my-app" } },
87
+ },
88
+ }),
89
+ );
90
+
91
+ const result = k8sSerializer.serialize(entities);
92
+ expect(result).toContain("kind: Deployment");
93
+ expect(result).toContain("selector:");
94
+ expect(result).not.toContain("replicas");
95
+ expect(result).not.toContain("heldElsewhere");
96
+ });
97
+
72
98
  test("metadata.name auto-generated from logical name (camelCase→kebab-case)", () => {
73
99
  const entities = new Map<string, any>();
74
100
  entities.set(
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  skill: chant-k8s-argo
3
- description: Argo CD composites for GitOps reconciliation — ArgoAppFor, ArgoAppSetForRegions, AppProject scoping, cluster registration, and the Argo-vs-Temporal split
3
+ description: Argo CD composites for GitOps reconciliation — ArgoAppFor, ArgoAppSetForRegions, AppProject scoping, cluster registration, and how a deploy splits between Argo and a chant Op
4
4
  user-invocable: true
5
5
  ---
6
6
 
@@ -14,9 +14,9 @@ Chant authors typed infrastructure into manifests. Argo CD continuously reconcil
14
14
  |---|---|---|
15
15
  | **Chant** | Authoring typed infra → manifests | the lexicons |
16
16
  | **Argo CD** | Continuously reconciling declarative manifests (the apply layer) | `ArgoAppFor` / `ArgoAppSetForRegions` |
17
- | **Temporal** | Procedural steps Argo can't express ordering, signals, human gates, one-shot RPCs | the temporal lexicon + `waitForArgoSync` |
17
+ | **A chant Op** | Procedural steps Argo can't express: ordering, human gates, one-shot RPCs | an `Op` in the project, plus this lexicon's `waitForArgoSync` |
18
18
 
19
- Rule of thumb: **if it's declarative and converges, let Argo reconcile it. If it's a procedure with ordering, gates, or out-of-band steps, orchestrate it in Temporal.** Prefer Argo CD over Argo Workflows the procedural layer stays Temporal.
19
+ Rule of thumb: **if it's declarative and converges, let Argo reconcile it. If it's a procedure with ordering, gates, or out-of-band steps, write it as a chant Op and run it from CI or a steward.** Prefer Argo CD over Argo Workflows; the procedural layer stays an Op.
20
20
 
21
21
  ## Prerequisites
22
22
 
@@ -138,9 +138,9 @@ Produces a `Secret` labelled `argocd.argoproj.io/secret-type: cluster`. After th
138
138
 
139
139
  ---
140
140
 
141
- ## The Argo-vs-Temporal split
141
+ ## Splitting a deploy between Argo and an Op
142
142
 
143
- When a deploy has both declarative and procedural parts, let each layer own what it's good at. Example the multi-region CockroachDB deploy:
143
+ When a deploy has both declarative and procedural parts, let each layer own what it's good at. Example, the multi-region CockroachDB deploy:
144
144
 
145
145
  | Step | Owner | Why |
146
146
  |---|---|---|
@@ -148,16 +148,30 @@ When a deploy has both declarative and procedural parts, let each layer own what
148
148
  | Install ESO / operators (Helm) | **Argo** | Declarative Helm source |
149
149
  | Apply per-cluster K8s manifests | **Argo** (`ApplicationSet`) | One App per workload cluster |
150
150
  | Wait for workloads Healthy | **Argo** (`Health=Healthy`) | Subsumed by Application health |
151
- | Wait for DNS delegation | **Temporal** | Signal/update/auto-poll race out of band |
152
- | Generate + push TLS certs | **Temporal** | One-shot procedure, secrets not in git |
153
- | `cockroach init`, configure regions | **Temporal** | Ordered one-shot RPCs |
154
-
155
- From a Temporal workflow, gate procedural steps on Argo finishing a declarative apply with the `waitForArgoSync` activity (temporal lexicon, `argoSync` profile):
151
+ | Wait for DNS delegation | **an Op** | Out of band, and a human confirms it |
152
+ | Generate + push TLS certs | **an Op** | One-shot procedure, secrets not in git |
153
+ | `cockroach init`, configure regions | **an Op** | Ordered one-shot RPCs |
154
+
155
+ Argo owns the sync. The Op owns the ordering and the gates: its phases run in
156
+ sequence in one process (`packages/core/src/op/local-executor.ts`), and a `gate`
157
+ step reads the gate ledger, so a run that reaches a gate nobody has approved
158
+ records the pending fact, ends with status `gated` and exits 3. Someone runs
159
+ `chant approve <op> <gate>`, the next run reads the resolution and walks
160
+ through. CI is what runs the Op, on whatever cadence the Op's `schedule`
161
+ names.
162
+
163
+ To make a step wait on Argo, use this lexicon's `waitForArgoSync` activity. It is
164
+ exported from `lexicons/k8s/src/op/activities/index.ts`, and the core activity
165
+ registry resolves it by export name once `k8s` is in the project's `lexicons`.
166
+ Give the step core's `argoSync` profile
167
+ (`packages/core/src/op/activity-profiles.ts`): a 30m timeout, five attempts
168
+ backing off from 10s, and `ArgoSyncFailedError` marked non-retryable so a
169
+ terminally unhealthy Application fails fast instead of polling to the cap.
156
170
 
157
171
  ```typescript
158
- // In a Temporal Op workflow:
159
- await waitForArgoSync({ appName: "east-crdb", namespace: "argocd" });
160
- // ...now run the procedural steps that depend on the workloads being Healthy.
172
+ // In an Op phase:
173
+ activity("waitForArgoSync", { appName: "east-crdb", namespace: "argocd" }, "argoSync"),
174
+ // Later steps in the phase run once the workloads are Healthy.
161
175
  ```
162
176
 
163
177
  `waitForArgoSync` is dependency-free — it polls the Application's status (`health=Healthy && sync=Synced`) and never imports the Argo CRD types.