@klok-integration/cli 0.6.224 → 0.6.245
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.
- package/assets/agents/AGENTS.integration.md +6 -0
- package/assets/agents/AGENTS.workspace.md +11 -3
- package/assets/agents/knowledge/platform-rules.md +23 -3
- package/assets/agents/knowledge/retired-patterns.txt +7 -0
- package/assets/agents/knowledge/roles/integration-developer.md +8 -0
- package/assets/agents/skills/klok-release/SKILL.md +19 -6
- package/assets/agents/skills/klok-route/SKILL.md +34 -1
- package/assets/agents/skills/klok-solution/SKILL.md +3 -2
- package/dist/cli.js +7 -7
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ needs. Read `klok docs read klok-framework-klok-quarkus-extension` before writin
|
|
|
12
12
|
./gradlew updateKlokLibsToml # refresh gradle/klok-libs.versions.toml from the dependency catalog
|
|
13
13
|
./gradlew quarkusDev # run locally against the configured broker
|
|
14
14
|
./gradlew initDocsGeneration # route facts to build/klokRoutes/docs.json
|
|
15
|
+
./gradlew klokTopics # the broker topics the service needs, to build/klok-topics/topics.json
|
|
16
|
+
./gradlew klokTopicCheck --report # asks klok.dev whether each topic exists and is granted (KLOK_INTEGRATION_CONTEXT_TOKEN)
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
### Rules
|
|
@@ -25,6 +27,10 @@ needs. Read `klok docs read klok-framework-klok-quarkus-extension` before writin
|
|
|
25
27
|
matches it, and both projects' `klok-docs/` write it down. A topic has at most one purpose; a reader is its
|
|
26
28
|
subscription (its route id), never a suffix. Never consume a shared topic and discard the other flows' messages.
|
|
27
29
|
The `klok-route` skill has the details.
|
|
30
|
+
* The broker creates no topics. After adding or changing a route, a purpose or an override, run
|
|
31
|
+
`./gradlew klokTopics`, resolve its warnings, and make sure every topic it lists exists: request a missing one (or
|
|
32
|
+
a grant) in klok.dev, or add it to the tenant's Pulsar file in a merge request, and list it as a manual step. The
|
|
33
|
+
service fails at startup with `TOPIC_MISSING` or `TOPIC_ACCESS_DENIED` and the fix when one is missing.
|
|
28
34
|
* Errors: throw `Klok4XXException` for caller errors (no retry) and `Klok5XXException` or plain exceptions for
|
|
29
35
|
retryable failures; the framework retries, republishes to `_DLQ` and emits the failure event. Do not add
|
|
30
36
|
`onException` blocks unless the route owns the failure (`failureHandled()`).
|
|
@@ -338,9 +338,17 @@ access, Keycloak broker clients or a service that serves several tenants.
|
|
|
338
338
|
* **Who applies it:** Ansible (`klok/environment/klok-environment-setup`, `keycloak.yml` and `pulsar.yml`) applies
|
|
339
339
|
what those files declare: today the platform and tenant klok's services, plus any entries a person adds there for
|
|
340
340
|
another tenant's service (a playbook per tenant: klok/environment/klok-environment-setup#35). Broker access (a
|
|
341
|
-
tenant, a namespace, a topic
|
|
342
|
-
|
|
343
|
-
|
|
341
|
+
tenant, a namespace, a topic, a grant) changes only through a merge request to those Pulsar files. klok.dev merges
|
|
342
|
+
what needs no approval (the generic topic of every datamodel) and what an owner approved in klok.dev, and applies
|
|
343
|
+
topics and grants from the files; Ansible, run by a person, applies the same files and alone creates namespaces and
|
|
344
|
+
revokes grants. klok.dev provisions no broker clients yet (klok/application/frontend-klok-dev#51). A topic or grant
|
|
345
|
+
made only in the broker's admin API is drift.
|
|
346
|
+
* **Topics are provisioned, never created by the broker.** A topic exists when its tenant's Pulsar file lists it and
|
|
347
|
+
the file is applied; its `_DLQ`, and the `_Response` of a `_Request` topic, come with it. `./gradlew klokTopics`
|
|
348
|
+
lists what a project needs, and a missing topic or grant fails the deploy (`klokTopicCheck`) and the service's start
|
|
349
|
+
with `TOPIC_MISSING` or `TOPIC_ACCESS_DENIED` and the fix. Follow the message: request the topic or access in
|
|
350
|
+
klok.dev, or add the line it names in a merge request, and hand that to a person. Never ask for automatic topic
|
|
351
|
+
creation and never create a topic through the broker's admin API.
|
|
344
352
|
|
|
345
353
|
## Documentation is part of every change
|
|
346
354
|
|
|
@@ -93,6 +93,25 @@ documentation or the dependency catalog can give you.
|
|
|
93
93
|
for reading across tenants.
|
|
94
94
|
* Request/reply: `ReqRes_<Request>_<Reply>[_<purpose>]_Request` and `_Response`. Dead letters: `<topic>_DLQ`.
|
|
95
95
|
* One datamodel per topic. A breaking change is a new major version and therefore a new topic.
|
|
96
|
+
* Topics are provisioned from Git; the broker never creates one by itself. A topic exists when its owning tenant's
|
|
97
|
+
Pulsar file in `klok/environment/klok-environment-setup`
|
|
98
|
+
(`ansible-master/playbooks/group_vars/all/pulsar_tenant_<tenant>.yml`) lists it and the file has been applied. A
|
|
99
|
+
producer or consumer on any other topic is refused. Subscriptions are still created by their consumer.
|
|
100
|
+
* Companions come with every topic and are never requested or listed on their own: `<topic>_DLQ` for every topic, and
|
|
101
|
+
for a `_Request` topic its `_Response` (with that topic's `_DLQ`).
|
|
102
|
+
* klok.dev declares the generic topic `<Model>` of every datamodel, and the ODL's change event topics. Every other
|
|
103
|
+
topic (a purpose, a request/reply pair) and every grant is requested in klok.dev and approved by the owner (a
|
|
104
|
+
TenantAdmin of the tenant or a DomainAdmin of the namespace), or added to the Pulsar file in a merge request.
|
|
105
|
+
* `./gradlew klokTopics` lists the topics a project needs (`build/klok-topics/topics.json`, with a warning for every
|
|
106
|
+
topic the scan cannot resolve) and `./gradlew klokTopicCheck` asks klok.dev whether each exists and is granted to
|
|
107
|
+
the service; in CI it blocks the deploy. A service checks the same at startup and does not start without them.
|
|
108
|
+
* A missing topic or grant is `TOPIC_MISSING` or `TOPIC_ACCESS_DENIED`, never retried. The message names the topic,
|
|
109
|
+
the role, the operation, who approves, the klok.dev request link (`https://klok.dev/monitoring/broker/request?...`)
|
|
110
|
+
and the line to add by hand. Follow it: hand the request link or the merge request to a person. Never create a topic
|
|
111
|
+
or a grant through the broker's admin API, never ask for automatic topic creation, and never turn the startup check
|
|
112
|
+
(`klok.broker.verify-topics`) off in a deployed service.
|
|
113
|
+
* A route that consumes another tenant's topic cannot write its `_DLQ`; where such dead letters go is open
|
|
114
|
+
(klok/framework/klok-common#11).
|
|
96
115
|
* Every broker message is a CloudEvent in binary content mode: the attributes are message properties, the payload
|
|
97
116
|
is the typed datamodel. `ce-source` is `urn:klok:<tenant>:<solutionId>:<routeId>`; `ce-type` says what happened
|
|
98
117
|
(default `dev.klok.<tenant>.<domain>.<Model>.published`).
|
|
@@ -135,9 +154,10 @@ whether a model may travel on a topic; it decides how widely the model is shared
|
|
|
135
154
|
(`persistent://<tenant>[_<env>]/common/<Model>`), the service is granted `consume` on exactly those topics, and
|
|
136
155
|
what the platform writes back lives in klok's namespace, one topic per tenant
|
|
137
156
|
(`persistent://klok[_<env>]/<domain>/<Model>_<tenant>`).
|
|
138
|
-
* Broker access (a tenant, a namespace, a topic
|
|
139
|
-
|
|
140
|
-
|
|
157
|
+
* Broker access (a tenant, a namespace, a topic, a grant) changes only in Git, in the owning tenant's Pulsar file in
|
|
158
|
+
`klok/environment/klok-environment-setup`. klok.dev applies topics and grants from the file (merging what needs no
|
|
159
|
+
approval and what an owner approved); Ansible, run by a person, applies the same files and alone creates namespaces
|
|
160
|
+
and revokes. A topic or grant made only in the broker's admin API is drift.
|
|
141
161
|
* One Keycloak client per running deployment, never shared and never used by a person. Broker rights are called
|
|
142
162
|
`publish` and `consume`; *ingress* and *egress* say what a service is and are never permissions.
|
|
143
163
|
|
|
@@ -27,6 +27,13 @@ topic-dlq # dead letters are <topic>_DLQ
|
|
|
27
27
|
(?i)last purpose segment # a topic has one optional purpose set by the publisher; a reader is its subscription (route id)
|
|
28
28
|
(?i)_<reader> # a topic has one optional purpose set by the publisher; a reader is its subscription (route id)
|
|
29
29
|
(?i)\breader (as|after) the (last )?purpose # a topic has one optional purpose set by the publisher; a reader is its subscription
|
|
30
|
+
# Topics are provisioned from Git and never created by the broker (2026-10-08): a topic exists when its tenant's
|
|
31
|
+
# Pulsar file lists it and the file is applied; its _DLQ and a _Request topic's _Response come with it.
|
|
32
|
+
(?i)\btopics? (is|are) (auto-?)?created (automatically )?(by|on|with|at) (the |its )?first (producer|publisher|consumer|publish|send|use|message) # topics are provisioned from Git, never created on first use
|
|
33
|
+
(?i)created automatically by (the|its) first (producer|publisher|consumer) # topics are provisioned from Git, never created on first use
|
|
34
|
+
(?i)\btopics? (appears?|comes? into existence) (with|on|at) (the |its )?first (producer|publisher|consumer|publish|use|message) # topics are provisioned from Git, never created on first use
|
|
35
|
+
(?i)\ba topic created in advance # every topic is listed in its tenant's Pulsar file before it is used; "in advance" is the only way
|
|
36
|
+
(?i)\b(enable|turn on|switch on) (automatic|auto-?)[ -]?topic[ -]creation # the Klok broker runs with allowAutoTopicCreation=false; request the topic in klok.dev
|
|
30
37
|
#
|
|
31
38
|
# Framework classes that do not exist in the Quarkus extension 2.0 and later.
|
|
32
39
|
KlokSoapApi # removed in 2.0; SOAP is a KlokCustomRoute with camel-cxf
|
|
@@ -206,6 +206,14 @@ blindly. `klok-mock-*` headers are for test and staging and are stripped in prod
|
|
|
206
206
|
authentication: alert and dead letter), the tests.
|
|
207
207
|
5. **Code and review.** Check the transform, the dead letter path, the CloudEvent attributes, logging through the
|
|
208
208
|
framework (never `System.out`), and the service's broker client and grants.
|
|
209
|
+
6. **Topics.** The broker never creates a topic. A datamodel's generic topic `<Model>` and every companion (`_DLQ`,
|
|
210
|
+
the `_Response` of a `_Request`) come by themselves; a purpose topic, a request/reply pair and every grant outside
|
|
211
|
+
the service's own namespaces are requested. Run `./gradlew klokTopics`, then have each missing topic or grant
|
|
212
|
+
requested in klok.dev (`/monitoring/broker/request`) or proposed as a merge request to the tenant's Pulsar file,
|
|
213
|
+
approved by the namespace's Domain Admin or the Tenant Admin. When `klokTopicCheck`, the pipeline's
|
|
214
|
+
`klok_topic_check` or the startup report names a `TOPIC_MISSING` or `TOPIC_ACCESS_DENIED`, follow the message:
|
|
215
|
+
it holds the request link, the approver and the line to add. Never suggest automatic topic creation. The whole
|
|
216
|
+
way is the user guide page *Topics On The Klok Broker* (`klok docs read klok-user-guide`).
|
|
209
217
|
|
|
210
218
|
## Documentation, tests and delivery
|
|
211
219
|
|
|
@@ -17,21 +17,26 @@ roles and secrets, catalog update) before acting.
|
|
|
17
17
|
dependency catalog; datamodels publish the generated artifact and update the catalog; integrations build and
|
|
18
18
|
push an image and update the deployment manifest in `klok/environment/ipaas/ipaas-service-deploy-manifests`,
|
|
19
19
|
which ArgoCD applies; documentation projects publish to https://docs.klok.dev.
|
|
20
|
-
3.
|
|
20
|
+
3. An integration's deploy waits for its broker topics: the job `klok_topic_check` (`./gradlew klokTopicCheck`) asks
|
|
21
|
+
klok.dev whether every topic the service needs exists and is granted, blocks the manifest update on `main` when
|
|
22
|
+
one is not, and only warns in a merge request pipeline. Before a merge, run `./gradlew klokTopics` and hand every
|
|
23
|
+
missing topic or grant over as a request in klok.dev (the link in the message), approved by its owner; the broker
|
|
24
|
+
never creates a topic, so there is nothing to wait out and no automatic creation to ask for.
|
|
25
|
+
4. Check the pipeline after every push:
|
|
21
26
|
`glab api --hostname git.klok.dev "projects/<id>/pipelines?per_page=3"`, then
|
|
22
27
|
`projects/<id>/pipelines/<pid>/jobs` and `projects/<id>/jobs/<jid>/trace`. Fix a red pipeline before the next task.
|
|
23
|
-
|
|
28
|
+
5. Framework changes across libraries are released in order: klok-common, broker client, extension. Wait for the
|
|
24
29
|
catalog update of one before merging the next: the catalog's commit for that release, not the merge, which only
|
|
25
30
|
starts the pipeline that produces it:
|
|
26
31
|
`glab api --hostname git.klok.dev "projects/klok%2Fframework%2Fklok-gradle-dependency-catalog/repository/commits?ref_name=main&per_page=10" | jq -r '.[].title'`.
|
|
27
32
|
A library that needs no change of its own is not released by hand: after each catalog update the job runs
|
|
28
33
|
`klok catalog release-dependents`, which starts the pipeline of every framework library built against an older
|
|
29
34
|
version of what was released (its log says which; `--dry-run --library <artifact>` shows it by hand).
|
|
30
|
-
|
|
35
|
+
6. **Never cancel, stop or restart a running job, pipeline or deployment without a human's explicit yes.** A
|
|
31
36
|
release waiting on Maven Central or npm can take half an hour, and `main` pipelines queue behind each other or run
|
|
32
37
|
in parallel, so a long wait is normal. Check the current time with `date -u` before judging how long anything has run. Report the
|
|
33
38
|
job id, its run time, the last log lines with timestamps and what waits behind it, propose what to do, and wait.
|
|
34
|
-
|
|
39
|
+
7. Merging is the human's step. Hand it over as KLOK-AGENTS.md *Manual steps are handed over as a guide* says:
|
|
35
40
|
every merge request with its link, whether the order matters and why, and for each later step the release
|
|
36
41
|
result to wait for and the command that shows it.
|
|
37
42
|
|
|
@@ -42,7 +47,8 @@ project by hand — `glab api POST /projects`, or cloning the template and swapp
|
|
|
42
47
|
fallback under *Git är sanningen*.
|
|
43
48
|
|
|
44
49
|
Neither path provisions a pipeline schedule yet, nor for a deployed service the manifest, its Vault objects, its
|
|
45
|
-
Keycloak client and its broker grants (see below; tracked in klok/application/frontend-klok-dev#41 and #42).
|
|
50
|
+
Keycloak client and its broker grants (see below; tracked in klok/application/frontend-klok-dev#41 and #42). Its
|
|
51
|
+
topics beyond the generic topic of each datamodel are requested in klok.dev and wait for an owner's approval. Name
|
|
46
52
|
those human steps up front instead of letting them surface
|
|
47
53
|
as a red pipeline. Vault CI login is not one of them: it needs nothing per project.
|
|
48
54
|
|
|
@@ -63,7 +69,9 @@ as a red pipeline. Vault CI login is not one of them: it needs nothing per proje
|
|
|
63
69
|
hand before the first deploy, with the Kubernetes service account; a Vault Kubernetes role `<name>-role` (with
|
|
64
70
|
`audience=https://kubernetes.default.svc`) and its policy `<name>-policy` reading
|
|
65
71
|
`secret/data/<tenant>/integration/<name>/env`; the env secret itself (broker credentials, service specific
|
|
66
|
-
keys);
|
|
72
|
+
keys); the service's own Keycloak client with its broker grants; and every topic `./gradlew klokTopics` lists, in
|
|
73
|
+
its owning tenant's Pulsar file and applied (requested in klok.dev, or a merge request to
|
|
74
|
+
`klok/environment/klok-environment-setup`). A topic's `_DLQ` and a `_Request` topic's `_Response` come with it.
|
|
67
75
|
* Documentation projects need the tenant's docs upload client: Keycloak client `klok-docs-upload-<tenant>` and its
|
|
68
76
|
id and secret in `secret/<tenant>/common/repo/cicd-environment-variables`.
|
|
69
77
|
|
|
@@ -84,3 +92,8 @@ field, and let a person run it.
|
|
|
84
92
|
| `already deployed` from JReleaser | Maven Central already has that version; a `main` build is a new timestamp, do not retry the same job |
|
|
85
93
|
| `sed: can't read klok/integration/<name>/<name>-crds.yaml` | no deployment manifest for the service yet |
|
|
86
94
|
| `cannot run ssh` | the job needs `openssh-client`; the templates install it before pushing over SSH |
|
|
95
|
+
| `klok_topic_check` fails with `TOPIC_MISSING` | a topic the service needs is not in its tenant's Pulsar file: request it with the link in the output, an owner approves |
|
|
96
|
+
| `klok_topic_check` fails with `TOPIC_ACCESS_DENIED` | the service's broker client holds no grant on the topic: request `consume` (or `publish` in its own tenant) with the link |
|
|
97
|
+
| `klok_topic_check` fails with `NOT_APPLIED` | the entry is merged but not on the broker yet: the next *Ensure* run in klok.dev applies it |
|
|
98
|
+
| `klok_topic_check` says to configure `KLOK_DEV_URL` or `KLOK_INTEGRATION_CONTEXT_TOKEN` | the CI environment object lacks them; a person adds them in Vault |
|
|
99
|
+
| a pod restarts with `broker topics are missing or not granted; this service cannot start` | the startup check found the same problems; follow each entry of the report, then restart the deployment |
|
|
@@ -20,7 +20,8 @@ motivates.
|
|
|
20
20
|
2. **Name it.** `<TENANTSHORT><NNN><LEG><seq>_<Description>`, one class per route, the solution number from the
|
|
21
21
|
portal registry. Sub routes are `S`, timers `T`.
|
|
22
22
|
3. **Bind the datamodels** as type parameters; take them from the catalog bundle, never hand written DTOs.
|
|
23
|
-
4. **Choose the topic purpose** of every topic the route publishes or consumes (
|
|
23
|
+
4. **Choose the topic purpose** of every topic the route publishes or consumes (*Topic purpose*), and make sure every
|
|
24
|
+
topic exists (*Topics on the broker*): the broker never creates one.
|
|
24
25
|
5. **Configure** through `routeConfig("key", default)` / `requiredRouteConfig("key")` and document every key in
|
|
25
26
|
`application.properties` as `<RouteId>.<key>`. Reserved keys: `enabled`, `logevents.*`, `retry.*`, `dlq.*`,
|
|
26
27
|
`topic.*`, `subscription.*`, `claimcheck.*`.
|
|
@@ -89,10 +90,42 @@ the purpose.
|
|
|
89
90
|
The user guide page *Topic Purpose: One Datamodel, Several Flows* (`klok docs read klok-user-guide`)
|
|
90
91
|
explains the reasons and the naming in full.
|
|
91
92
|
|
|
93
|
+
## Topics on the broker
|
|
94
|
+
|
|
95
|
+
The broker creates no topics. A topic exists when its owning tenant's Pulsar file in
|
|
96
|
+
`klok/environment/klok-environment-setup` (`ansible-master/playbooks/group_vars/all/pulsar_tenant_<tenant>.yml`)
|
|
97
|
+
lists it and the file has been applied; its `_DLQ`, and for a `_Request` topic its `_Response`, come with it.
|
|
98
|
+
Subscriptions are created by their consumer. klok.dev declares the generic topic `<Model>` of every datamodel; a
|
|
99
|
+
purpose topic, a request/reply pair and a grant on another domain's or tenant's topic are requested.
|
|
100
|
+
|
|
101
|
+
1. **List.** After adding or changing a route, a purpose, a subscription or an override, run `./gradlew klokTopics`
|
|
102
|
+
and read `build/klok-topics/topics.json` (not the console). Every entry in `warnings` is work: make the purpose,
|
|
103
|
+
subscription or override something the scan can read (configuration or a constant), or name the topic in the
|
|
104
|
+
merge request yourself.
|
|
105
|
+
2. **Check.** With `KLOK_INTEGRATION_CONTEXT_TOKEN` set, run `./gradlew klokTopicCheck --report` and read
|
|
106
|
+
`build/klok-topics/topic-check.json`. `TOPIC_MISSING`: the topic is not in Git. `TOPIC_ACCESS_DENIED`: the service's
|
|
107
|
+
broker client (`service-account-<klok.broker.auth.client-id>`) holds no grant. `NOT_APPLIED`: in Git, not yet on
|
|
108
|
+
the broker; nothing to request.
|
|
109
|
+
3. **Add what is missing, through the request.** For each problem, hand the person the request link from the message
|
|
110
|
+
(`https://klok.dev/monitoring/broker/request?topic=…&access=…`) and who approves it (a TenantAdmin of the owning
|
|
111
|
+
tenant or a DomainAdmin of its namespace), or, when the task asks for it, open the merge request that adds the
|
|
112
|
+
exact line the message names to the owning tenant's Pulsar file. List the topic itself, never its `_DLQ` or
|
|
113
|
+
`_Response`. Both are manual steps in your hand-over guide; the deploy waits for them.
|
|
114
|
+
4. **When a service does not start,** its log has one report from `KlokTopicCheck` with an entry per missing topic or
|
|
115
|
+
grant, each ending with the request link and the line to add by hand. Do what each entry says; never work around
|
|
116
|
+
it.
|
|
117
|
+
|
|
118
|
+
`klok.broker.verify-topics=false` is only for a local broker that creates topics itself, never in a deployed service.
|
|
119
|
+
A route that consumes another tenant's topic cannot write that topic's `_DLQ`; say so in the design, and set
|
|
120
|
+
`<RouteId>.failure.unresolved` deliberately (open question klok/framework/klok-common#11).
|
|
121
|
+
|
|
92
122
|
## Things that are always wrong
|
|
93
123
|
|
|
94
124
|
* A route id that does not start with the solution id, or two routes with the same id.
|
|
95
125
|
* Publishing to a hand written topic string; use the resolver and the datamodel.
|
|
126
|
+
* Asking for automatic topic creation, creating a topic or a grant through the broker's admin API, or retrying a
|
|
127
|
+
`TOPIC_MISSING` or `TOPIC_ACCESS_DENIED` failure as if the broker were down; the topic is requested and approved
|
|
128
|
+
through Git.
|
|
96
129
|
* Consuming a shared topic and discarding the messages of other flows (a filter on a payload field, an ODL lookup
|
|
97
130
|
per message) where the producer could publish the flow with a topic purpose.
|
|
98
131
|
* A purpose with `_`, a version or an environment in it, a second suffix after the purpose, a tenant in it that the
|
|
@@ -74,8 +74,9 @@ shared platform service) in `klok/integration-solutions`.
|
|
|
74
74
|
* When the document and the code disagree, the code on `main` is what is built: correct the document. When the code
|
|
75
75
|
differs from what was agreed, file an issue in the owning project and name it in the document's open questions.
|
|
76
76
|
* The deployed landscape counts too: a leg that is on `main` but not deployed, or deployed from somewhere the
|
|
77
|
-
document does not name, is written down. Deployment manifests are in
|
|
78
|
-
hosts and stateful services in the Ansible inventories of
|
|
77
|
+
document does not name, is written down. Deployment manifests are in
|
|
78
|
+
`klok/environment/ipaas/ipaas-service-deploy-manifests`, hosts and stateful services in the Ansible inventories of
|
|
79
|
+
`klok/environment/klok-environment-setup`.
|
|
79
80
|
|
|
80
81
|
## Things that are always wrong
|
|
81
82
|
|