@intentius/chant-lexicon-gitlab 0.57.0 → 0.59.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.
@@ -3,10 +3,12 @@
3
3
  *
4
4
  * The Op counterpart to `./generate-pipeline.ts` (#563): that module
5
5
  * synthesizes a deploy-time component graph as one `.gitlab-ci.yml`; this one
6
- * synthesizes a cron-triggered job per stateless Op — the CI-native
7
- * alternative to a Temporal `TemporalSchedule` for downstream projects that
8
- * don't run Temporal (`WorkflowAuditOp`/`PipelineAuditOp`/`ReconcileOp` all
9
- * accept an optional `schedule` precisely for this).
6
+ * synthesizes a cron-triggered job per stateless Op. An Op's cadence is an
7
+ * `OpSchedule` on the Op itself (`packages/core/src/op/types.ts`),
8
+ * runtime-neutral data each reader interprets; this module is the reader that
9
+ * turns it into a GitLab pipeline schedule (`WorkflowAuditOp`/
10
+ * `PipelineAuditOp`/`ReconcileOp` all accept an optional `schedule` precisely
11
+ * for this).
10
12
  *
11
13
  * Unlike GitHub Actions' per-workflow `on.schedule`, GitLab has no in-file
12
14
  * cron at all — a schedule is a project-level object (Settings → CI/CD →
@@ -20,10 +22,36 @@
20
22
  * inlined audit/reconcile logic. The finding-mode itself is already baked
21
23
  * into the Op's own activity args at build time by the composite that
22
24
  * created it; GitLab has no per-job `permissions:` concept (unlike GitHub
23
- * Actions), so a non-`report` mode's write access comes from whatever
25
+ * Actions), and `findingMode: "comment"` is refused by name here because it
26
+ * posts onto a GitHub pull-request event GitLab does not have (#2231), so a
27
+ * non-`report` mode's write access comes from whatever
24
28
  * `GITLAB_TOKEN`/CI-CD-variable configuration the project already has —
25
29
  * this generator documents the requirement rather than fabricating a
26
30
  * variable nothing reads.
31
+ *
32
+ * Neither of #2242's two per-Op options survives the crossing, and both are
33
+ * refused by name rather than dropped. A `setup` entry spelled `{ uses }` is
34
+ * a GitHub Actions marketplace action; GitLab CI has `script` and nothing
35
+ * else, so there is no shape to translate it into and no way to approximate
36
+ * `aws-actions/configure-aws-credentials` in a shell line. A `{ run }` entry
37
+ * translates exactly, and is emitted ahead of the `beforeScript` lines, the
38
+ * same position the github generator gives it. An additive `permissions` map
39
+ * is refused for the same reason `permissions:` is absent here at all: GitLab
40
+ * has no per-job token-scope mapping, and its OIDC surface is a different
41
+ * declaration (`id_tokens:` with an `aud`, exchanged for cloud credentials by
42
+ * the job itself) that chant does not generate. Ignoring the map would emit a
43
+ * job that reads as having OIDC and runs with no credentials.
44
+ *
45
+ * The gated-apply mapping (#2243) has nothing to attach to here. It exists
46
+ * because a push-to-main apply that stops at its gate exits 3 and paints the
47
+ * branch red on every merge; this generator has no push pipeline at all,
48
+ * refusing a `push` trigger by name (#2084) because a GitLab schedule is a
49
+ * project-level cron object rather than an event. GitLab does have its own
50
+ * equivalent of the mapping should one ever be wanted — `allow_failure:
51
+ * { exit_codes: [3] }` turns one exit code into a warning rather than a
52
+ * failure, without a flag on the invocation — so the day this generator
53
+ * grows a push trigger, that is the shape to reach for rather than
54
+ * `--gated-exit`.
27
55
  */
28
56
  import type { ComponentPipelineOptions as GenerateGitlabOpOptions, OpPipelineResult as GenerateGitlabOpResult, ScheduledOpSpec } from "@intentius/chant/lexicon";
29
57
  export type { GenerateGitlabOpOptions, GenerateGitlabOpResult };
@@ -1 +1 @@
1
- {"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EAGnD,gBAAgB,IAAI,sBAAsB,EAC1C,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAmBhE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CA6CxB"}
1
+ {"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAIH,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EAGnD,gBAAgB,IAAI,sBAAsB,EAC1C,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAiClC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAmBhE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAmExB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAQxC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG7D,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAIjC,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnI,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGtI,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGzH,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOxC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG7D,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAIjC,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnI,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGtI,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGzH,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "2c3566b38dbbfa11a15c278371c596b68388a8c1a7e1d55293d51fec467d8303",
4
+ "manifest.json": "05eca8e8e24c1045cd8d1b9903dfa59661449d40b52b9d2f880eb2a5a00c67d2",
5
5
  "meta.json": "931fc3246a55645b1493080bbeb160e5d205e42349e8bdca96ca243adb5f0da3",
6
6
  "types/index.d.ts": "5cd2e99f135a929b72bdd822d00d780d39b1cd407ba0cfb3d511c7ac9d667b58",
7
7
  "rules/artifact-no-expiry.ts": "3f3cabf9792cbf8207e53a25f506715466b19ec25e9c3b4d0d77fed6b2eb4542",
@@ -55,5 +55,5 @@
55
55
  "skills/chant-gitlab-migrate.md": "4853d04980560b379e5b0f7267dcb0a1e5d5879cb9e9931681ce76b2a5fe69d8",
56
56
  "skills/chant-gitlab-patterns.md": "6d9a44e9e8de4c3820be9d65381b0d2ede4cb3626aaaf0011e21838a9fbf556e"
57
57
  },
58
- "composite": "b121d7b8bd7d52a80a08fc2797792e895b16093302af3ded870482d6f5af1b9c"
58
+ "composite": "5ba4e358f25944a07f0966355b274aebb3c199d11fc997aa7713830c7ff5320e"
59
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA0E,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAEvI,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA6CvD,CAAC"}
1
+ {"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA0E,KAAK,QAAQ,EAAgB,MAAM,gCAAgC,CAAC;AAGrJ,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA6CvD,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Prior art for these audit rules: the open-source tools whose checks cover the
3
+ * same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
4
+ * the registry, the relation vocabulary, and why this is credit rather than
5
+ * authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
6
+ * when a credited tool's index no longer lists a rule cited here.
7
+ */
8
+ import type { Lineage } from "@intentius/chant/audit/catalog";
9
+ export declare const gitlabAuditLineage: Record<string, Lineage[]>;
10
+ //# sourceMappingURL=audit-lineage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-lineage.d.ts","sourceRoot":"","sources":["../../src/lint/audit-lineage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CA+DxD,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlab",
3
- "version": "0.57.0",
3
+ "version": "0.59.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitLab",
6
6
  "intrinsics": [
@@ -9,7 +9,7 @@ export interface GitlabPipelineArgs {
9
9
  /**
10
10
  * Trigger a GitLab CI pipeline and wait for it to complete successfully.
11
11
  * Requires `glab` CLI authenticated in the environment.
12
- * Uses longInfra profile — 20m timeout, heartbeat every poll.
12
+ * Uses the longInfra profile: 20m timeout, three attempts backing off from 30s.
13
13
  */
14
14
  export declare function gitlabPipeline(args: GitlabPipelineArgs, signal?: AbortSignal): Promise<void>;
15
15
  //# sourceMappingURL=gitlab.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gitlab.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAsClG"}
1
+ {"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gitlab.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqClG"}
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * gitlab Op activities — resolved by the core activity registry when a project's
3
3
  * `chant.config.ts` lists the `gitlab` lexicon. `gitlabPipeline` triggers a
4
- * pipeline over the GitLab CLI with heartbeat/retry semantics; relocated from the
5
- * temporal lexicon (#809) so gitlab's imperative activity lives with its product.
6
- * The `gitlabPipeline` step builder stays in core, re-exported from the temporal
7
- * Op-authoring barrel like the other core builders.
4
+ * pipeline over the GitLab CLI and polls it to completion under the step's
5
+ * profile; relocated from the hosting lexicon (#809) so gitlab's imperative
6
+ * activity lives with its product. The `gitlabPipeline` step builder stays in
7
+ * core and reaches authors through `@intentius/chant/op` like the other core
8
+ * builders.
8
9
  */
9
10
  export { gitlabPipeline } from "./gitlab.js";
10
11
  export type { GitlabPipelineArgs } from "./gitlab.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC"}
@@ -7,12 +7,11 @@
7
7
  * `./activities/gitlab.ts`. `opts`'s type below IS `GitlabPipelineArgs`
8
8
  * itself (via `Omit`/`WithStepRefs`) — never a hand-restated mirror.
9
9
  *
10
- * `core`'s own `gitlabPipeline` (in `@intentius/chant/op`, re-exported from
11
- * `@intentius/chant-lexicon-temporal`) is UNCHANGED and produces a
12
- * byte-identical `ActivityStep` for the same inputs — purely additive, and
13
- * deliberately not swapped into the temporal barrel (see
10
+ * `core`'s own `gitlabPipeline` (in `@intentius/chant/op`) is UNCHANGED and
11
+ * produces a byte-identical `ActivityStep` for the same inputs — purely
12
+ * additive, and deliberately not swapped into core's barrel (see
14
13
  * `lexicons/k8s/src/op/builders.ts`'s module doc for why: that would make
15
- * temporal depend on this package at runtime). An author who wants the typed
14
+ * core depend on this package at runtime). An author who wants the typed
16
15
  * surface imports it from here — `@intentius/chant-lexicon-gitlab`.
17
16
  */
18
17
  import { type ActivityStep, type NamedActivityStep, type WithStepRefs } from "@intentius/chant/op";
@@ -1 +1 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,KACzG,iBAGF,CAAC"}
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,KACzG,iBAGF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-gitlab",
3
- "version": "0.57.0",
3
+ "version": "0.59.0",
4
4
  "description": "GitLab CI lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -67,8 +67,8 @@
67
67
  "typescript": "^5.9.3"
68
68
  },
69
69
  "peerDependencies": {
70
- "@intentius/chant": "^0.57.0",
71
- "@intentius/chant-lexicon-github": "^0.57.0",
70
+ "@intentius/chant": "^0.59.0",
71
+ "@intentius/chant-lexicon-github": "^0.59.0",
72
72
  "typescript": "^5.9.3"
73
73
  },
74
74
  "peerDependenciesMeta": {
@@ -70,6 +70,36 @@ describe("generateGitlabOpPipeline: one file, one job per scheduled Op", () => {
70
70
  });
71
71
  });
72
72
 
73
+ describe("generateGitlabOpPipeline: no pull_request/push event model (#2084)", () => {
74
+ test("a pull_request trigger throws a clear error instead of silently ignoring it", () => {
75
+ const specs: ScheduledOpSpec[] = [{ name: "tf-plan", trigger: { kind: "pull_request" } }];
76
+ expect(() => generateGitlabOpPipeline(specs)).toThrow(/pull_request.*GitLab has no pull_request\/push event model/s);
77
+ });
78
+
79
+ test("a push trigger throws a clear error instead of silently ignoring it", () => {
80
+ const specs: ScheduledOpSpec[] = [{ name: "tf-apply", trigger: { kind: "push" } }];
81
+ expect(() => generateGitlabOpPipeline(specs)).toThrow(/push.*GitLab has no pull_request\/push event model/s);
82
+ });
83
+ });
84
+
85
+ describe("generateGitlabOpPipeline: no comment finding mode (#2231)", () => {
86
+ test("findingMode comment is refused by name, even on a cron trigger GitLab does support", () => {
87
+ const specs: ScheduledOpSpec[] = [
88
+ { name: "app-plan", schedule: "0 6 * * *", findingMode: "comment" },
89
+ ];
90
+ expect(() => generateGitlabOpPipeline(specs)).toThrow(
91
+ /Scheduled Op "app-plan".*findingMode "comment".*GitLab has no pull_request event/s,
92
+ );
93
+ });
94
+
95
+ test("the refusal names the modes GitLab does have", () => {
96
+ const specs: ScheduledOpSpec[] = [{ name: "app-plan", schedule: "0 6 * * *", findingMode: "comment" }];
97
+ expect(() => generateGitlabOpPipeline(specs)).toThrow(
98
+ /findingMode "issue" or "merge-request"/,
99
+ );
100
+ });
101
+ });
102
+
73
103
  describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit, not per-job", () => {
74
104
  test("runCommand/beforeScript/extraScript apply uniformly across every job", () => {
75
105
  const specs: ScheduledOpSpec[] = [
@@ -77,7 +107,7 @@ describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit
77
107
  { name: "prod-reconcile", schedule: "0 * * * *" },
78
108
  ];
79
109
  const result = generateGitlabOpPipeline(specs, {
80
- runCommand: ["chant", "run", "{name}", "--temporal"],
110
+ runCommand: ["chant", "run", "{name}", "--json"],
81
111
  beforeScript: ["npm ci"],
82
112
  extraScript: ["echo done"],
83
113
  });
@@ -87,8 +117,55 @@ describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit
87
117
  const job = parsed[spec.name] as Record<string, unknown>;
88
118
  const script = job.script as string[];
89
119
  expect(script[0]).toBe("npm ci");
90
- expect(script[1]).toContain("--temporal");
120
+ expect(script[1]).toContain("--json");
91
121
  expect(script[2]).toBe("echo done");
92
122
  }
93
123
  });
94
124
  });
125
+
126
+ /**
127
+ * #2242's two per-Op options against a provider that has neither concept.
128
+ * Both degrade by name at build time rather than being dropped into a job
129
+ * that would run without the thing the option asked for.
130
+ */
131
+ describe("generateGitlabOpPipeline: setup steps and additive permissions (#2242)", () => {
132
+ test("refuses a `uses` setup step by name, and names the action", () => {
133
+ expect(() =>
134
+ generateGitlabOpPipeline([
135
+ {
136
+ name: "actions-audit",
137
+ schedule: "0 6 * * *",
138
+ setup: [{ uses: "aws-actions/configure-aws-credentials@v6" }],
139
+ },
140
+ ]),
141
+ ).toThrow(/setup step 1 is `uses: "aws-actions\/configure-aws-credentials@v6"`.*GitLab CI jobs run/s);
142
+ });
143
+
144
+ test("emits a `run` setup entry ahead of the beforeScript lines", () => {
145
+ const result = generateGitlabOpPipeline(
146
+ [
147
+ {
148
+ name: "actions-audit",
149
+ schedule: "0 6 * * *",
150
+ setup: [{ run: "aws sts get-caller-identity" }],
151
+ },
152
+ ],
153
+ { beforeScript: ["npm ci"], extraScript: ["echo done"] },
154
+ );
155
+ const job = parseYAML(result.files[0].yaml)["actions-audit"] as { script: string[] };
156
+ expect(job.script).toEqual([
157
+ "aws sts get-caller-identity",
158
+ "npm ci",
159
+ "chant run actions-audit",
160
+ "echo done",
161
+ ]);
162
+ });
163
+
164
+ test("refuses additive permissions by name, and points at GitLab's own OIDC surface", () => {
165
+ expect(() =>
166
+ generateGitlabOpPipeline([
167
+ { name: "actions-audit", schedule: "0 6 * * *", permissions: { "id-token": "write" } },
168
+ ]),
169
+ ).toThrow(/adds permissions \{ id-token: write \}.*id_tokens:/s);
170
+ });
171
+ });
@@ -3,10 +3,12 @@
3
3
  *
4
4
  * The Op counterpart to `./generate-pipeline.ts` (#563): that module
5
5
  * synthesizes a deploy-time component graph as one `.gitlab-ci.yml`; this one
6
- * synthesizes a cron-triggered job per stateless Op — the CI-native
7
- * alternative to a Temporal `TemporalSchedule` for downstream projects that
8
- * don't run Temporal (`WorkflowAuditOp`/`PipelineAuditOp`/`ReconcileOp` all
9
- * accept an optional `schedule` precisely for this).
6
+ * synthesizes a cron-triggered job per stateless Op. An Op's cadence is an
7
+ * `OpSchedule` on the Op itself (`packages/core/src/op/types.ts`),
8
+ * runtime-neutral data each reader interprets; this module is the reader that
9
+ * turns it into a GitLab pipeline schedule (`WorkflowAuditOp`/
10
+ * `PipelineAuditOp`/`ReconcileOp` all accept an optional `schedule` precisely
11
+ * for this).
10
12
  *
11
13
  * Unlike GitHub Actions' per-workflow `on.schedule`, GitLab has no in-file
12
14
  * cron at all — a schedule is a project-level object (Settings → CI/CD →
@@ -20,13 +22,40 @@
20
22
  * inlined audit/reconcile logic. The finding-mode itself is already baked
21
23
  * into the Op's own activity args at build time by the composite that
22
24
  * created it; GitLab has no per-job `permissions:` concept (unlike GitHub
23
- * Actions), so a non-`report` mode's write access comes from whatever
25
+ * Actions), and `findingMode: "comment"` is refused by name here because it
26
+ * posts onto a GitHub pull-request event GitLab does not have (#2231), so a
27
+ * non-`report` mode's write access comes from whatever
24
28
  * `GITLAB_TOKEN`/CI-CD-variable configuration the project already has —
25
29
  * this generator documents the requirement rather than fabricating a
26
30
  * variable nothing reads.
31
+ *
32
+ * Neither of #2242's two per-Op options survives the crossing, and both are
33
+ * refused by name rather than dropped. A `setup` entry spelled `{ uses }` is
34
+ * a GitHub Actions marketplace action; GitLab CI has `script` and nothing
35
+ * else, so there is no shape to translate it into and no way to approximate
36
+ * `aws-actions/configure-aws-credentials` in a shell line. A `{ run }` entry
37
+ * translates exactly, and is emitted ahead of the `beforeScript` lines, the
38
+ * same position the github generator gives it. An additive `permissions` map
39
+ * is refused for the same reason `permissions:` is absent here at all: GitLab
40
+ * has no per-job token-scope mapping, and its OIDC surface is a different
41
+ * declaration (`id_tokens:` with an `aud`, exchanged for cloud credentials by
42
+ * the job itself) that chant does not generate. Ignoring the map would emit a
43
+ * job that reads as having OIDC and runs with no credentials.
44
+ *
45
+ * The gated-apply mapping (#2243) has nothing to attach to here. It exists
46
+ * because a push-to-main apply that stops at its gate exits 3 and paints the
47
+ * branch red on every merge; this generator has no push pipeline at all,
48
+ * refusing a `push` trigger by name (#2084) because a GitLab schedule is a
49
+ * project-level cron object rather than an event. GitLab does have its own
50
+ * equivalent of the mapping should one ever be wanted — `allow_failure:
51
+ * { exit_codes: [3] }` turns one exit code into a warning rather than a
52
+ * failure, without a flag on the invocation — so the day this generator
53
+ * grows a push trigger, that is the shape to reach for rather than
54
+ * `--gated-exit`.
27
55
  */
28
56
 
29
57
  import { emitYAML } from "@intentius/chant/yaml";
58
+ import { resolveOpTrigger } from "@intentius/chant/lexicon";
30
59
  import type {
31
60
  ComponentPipelineOptions as GenerateGitlabOpOptions,
32
61
  OpFindingMode,
@@ -35,6 +64,37 @@ import type {
35
64
  ScheduledOpSpec,
36
65
  } from "@intentius/chant/lexicon";
37
66
 
67
+ /**
68
+ * Refuse the two #2242 options GitLab cannot honour, by name and before any
69
+ * YAML exists, and return the `run` setup lines that do translate. See the
70
+ * module doc for why each one is a refusal rather than a silent drop.
71
+ */
72
+ function gitlabSetupScript(spec: ScheduledOpSpec): string[] {
73
+ if (spec.permissions && Object.keys(spec.permissions).length > 0) {
74
+ const scopes = Object.entries(spec.permissions)
75
+ .map(([scope, value]) => `${scope}: ${value}`)
76
+ .join(", ");
77
+ throw new Error(
78
+ `Scheduled Op "${spec.name}" adds permissions { ${scopes} }, but GitLab CI has no per-job token-scope ` +
79
+ `mapping — there is no \`permissions:\` key to add them to (#2242). Its OIDC surface is a separate ` +
80
+ `\`id_tokens:\` declaration the job exchanges for cloud credentials itself, which chant does not ` +
81
+ `generate. Drop the option here, or generate this Op for github.`,
82
+ );
83
+ }
84
+ const lines: string[] = [];
85
+ (spec.setup ?? []).forEach((step, index) => {
86
+ if ("uses" in step) {
87
+ throw new Error(
88
+ `Scheduled Op "${spec.name}" setup step ${index + 1} is \`uses: "${step.uses}"\`, a GitHub Actions ` +
89
+ `marketplace action. GitLab CI jobs run \`script\` lines only, so there is nothing to translate it ` +
90
+ `into (#2242). Express the setup as a \`{ run }\` entry, or generate this Op for github/forgejo.`,
91
+ );
92
+ }
93
+ lines.push(step.run);
94
+ });
95
+ return lines;
96
+ }
97
+
38
98
  export type { GenerateGitlabOpOptions, GenerateGitlabOpResult };
39
99
 
40
100
  /** GitLab CI job names must be safe YAML keys; Op names are already kebab-case in every fixture, but normalize defensively (mirrors `./generate-pipeline.ts`'s `toJobName`). */
@@ -49,9 +109,9 @@ const STAGE = "scheduled-ops";
49
109
  const SELECTOR_VAR = "CHANT_SCHEDULED_OP";
50
110
 
51
111
  /** One setup line per Op in the generated file's header comment. */
52
- function setupLine(spec: ScheduledOpSpec, jobName: string, mode: OpFindingMode): string {
112
+ function setupLine(spec: ScheduledOpSpec, cron: string, jobName: string, mode: OpFindingMode): string {
53
113
  const tokenNote = mode === "report" ? "" : " — needs a GITLAB_TOKEN CI/CD variable (masked, scope: api)";
54
- return `# ${jobName}: cron "${spec.schedule}", ${SELECTOR_VAR}="${spec.name}", finding-mode ${mode}${tokenNote}`;
114
+ return `# ${jobName}: cron "${cron}", ${SELECTOR_VAR}="${spec.name}", finding-mode ${mode}${tokenNote}`;
55
115
  }
56
116
 
57
117
  /**
@@ -82,13 +142,35 @@ export function generateGitlabOpPipeline(
82
142
  ];
83
143
 
84
144
  for (const spec of ops) {
145
+ const setupScript = gitlabSetupScript(spec);
85
146
  const findingMode = spec.findingMode ?? "report";
147
+ if (findingMode === "comment") {
148
+ // The mode posts onto the pull request that triggered the run (#2231),
149
+ // read out of the GitHub Actions event payload by `reconcilePr`. GitLab
150
+ // has neither that event model nor that payload, and core carries no
151
+ // GitLab API client that would post the merge-request note instead, so
152
+ // this refuses the mode by name rather than emitting a job whose finding
153
+ // step fails on every pipeline.
154
+ throw new Error(
155
+ `Scheduled Op "${spec.name}" has findingMode "comment", which posts its finding on the pull request ` +
156
+ `that triggered the run. GitLab has no pull_request event and chant has no GitLab merge-request ` +
157
+ `note activity (#2231). Use findingMode "issue" or "merge-request" here, or generate this Op for ` +
158
+ `github.`,
159
+ );
160
+ }
161
+ const trigger = resolveOpTrigger(spec);
162
+ if (trigger.kind !== "cron") {
163
+ throw new Error(
164
+ `Scheduled Op "${spec.name}" has a "${trigger.kind}" trigger, but GitLab has no pull_request/push event model ` +
165
+ `(#2084) — only a project-level Pipeline Schedule (cron). Give it a cron trigger, or generate it for github/forgejo instead.`,
166
+ );
167
+ }
86
168
  const jobName = toJobName(spec.name);
87
- jobs.push({ jobName, op: spec.name, schedule: spec.schedule, findingMode });
88
- headerLines.push(setupLine(spec, jobName, findingMode));
169
+ jobs.push({ jobName, op: spec.name, trigger, findingMode });
170
+ headerLines.push(setupLine(spec, trigger.schedule, jobName, findingMode));
89
171
 
90
172
  const runParts = runCommand.map((part) => part.replace("{name}", spec.name));
91
- const script = [...beforeScript, runParts.join(" "), ...extraScript];
173
+ const script = [...setupScript, ...beforeScript, runParts.join(" "), ...extraScript];
92
174
 
93
175
  doc[jobName] = {
94
176
  stage: STAGE,
@@ -186,7 +186,7 @@ describe("generateGitlabPipeline: a cross-cutting change is one generator edit,
186
186
  test("changing the trigger command (runCommand) updates every job's script uniformly", () => {
187
187
  const components = pilotComponents();
188
188
  const result = generateGitlabPipeline(components, {
189
- runCommand: ["chant", "run", "--components", "{name}", "--env", "staging", "--temporal"],
189
+ runCommand: ["chant", "run", "--components", "{name}", "--env", "staging", "--verbose"],
190
190
  });
191
191
  const parsed = parseYAML(result.yaml);
192
192
 
@@ -194,7 +194,7 @@ describe("generateGitlabPipeline: a cross-cutting change is one generator edit,
194
194
  const script = (parsed[job.jobName] as Record<string, unknown>).script as string[];
195
195
  // The runCommand prefix reflects in every job; output-threading flags
196
196
  // (--seed-outputs/--dump-outputs) may be appended per the dependency graph.
197
- expect(script[0].startsWith(`chant run --components ${job.component} --env staging --temporal`)).toBe(true);
197
+ expect(script[0].startsWith(`chant run --components ${job.component} --env staging --verbose`)).toBe(true);
198
198
  }
199
199
  });
200
200
 
package/src/index.ts CHANGED
@@ -7,9 +7,8 @@ export { gitlabPlugin } from "./plugin";
7
7
  // Typed Op step-builder wrapper (chant #1288 Stage 2) — gitlabPipeline with
8
8
  // authoring-time types derived from this lexicon's own GitlabPipelineArgs
9
9
  // (see lexicons/k8s/src/op/builders.ts's module doc for why this lives here
10
- // rather than in core or the temporal barrel). Opt-in:
11
- // `@intentius/chant-lexicon-temporal`'s same-named export is core's original
12
- // untyped builder, unchanged, for cloud-agnostic authoring.
10
+ // rather than in core). Opt-in: `@intentius/chant/op`'s same-named export is
11
+ // core's original untyped builder, unchanged, for cloud-agnostic authoring.
13
12
  export { gitlabPipeline } from "./op/builders";
14
13
 
15
14
  // Intrinsics
@@ -2,7 +2,8 @@
2
2
  * The gitlab lexicon's chant audit catalog — metadata for its post-synth rules
3
3
  * (WGL GitLab CI rules). Contributed via gitlabPlugin.auditCatalog() (#687).
4
4
  */
5
- import { auditRule, GH_INJECTION, GH_OIDC, GH_PWN, GH_SECRETS, SCORECARD_PINNED, type RuleMeta } from "@intentius/chant/audit/catalog";
5
+ import { auditRule, GH_INJECTION, GH_OIDC, GH_PWN, GH_SECRETS, SCORECARD_PINNED, type RuleMeta, applyLineage } from "@intentius/chant/audit/catalog";
6
+ import { gitlabAuditLineage } from "./audit-lineage";
6
7
 
7
8
  export const gitlabAuditCatalog: Record<string, RuleMeta> = {
8
9
  WGL010: auditRule("WGL010", "merge-worthy", "guidance", "Job references an undefined stage", "Add the stage to `stages:` or fix the job's `stage:`.", { category: "correctness" }),
@@ -50,3 +51,6 @@ export const gitlabAuditCatalog: Record<string, RuleMeta> = {
50
51
  WGL049: auditRule("WGL049", "report-only", "guidance", "Dependency install without a cache", "Add a `cache:` covering the dependency directory.", { category: "efficiency" }),
51
52
  WGL050: auditRule("WGL050", "report-only", "guidance", "Merge-request job missing interruptible", "Add `interruptible: true` so a superseded pipeline can be cancelled.", { category: "efficiency" }),
52
53
  };
54
+
55
+ // Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
56
+ applyLineage(gitlabAuditCatalog, gitlabAuditLineage);
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Prior art for these audit rules: the open-source tools whose checks cover the
3
+ * same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
4
+ * the registry, the relation vocabulary, and why this is credit rather than
5
+ * authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
6
+ * when a credited tool's index no longer lists a rule cited here.
7
+ */
8
+ import type { Lineage } from "@intentius/chant/audit/catalog";
9
+
10
+ export const gitlabAuditLineage: Record<string, Lineage[]> = {
11
+ WGL010: [
12
+ { tool: "gitlab-docs", rule: "stage: supported values are the default stages or stages listed in `stages`", url: "https://docs.gitlab.com/ci/yaml/#stage", relation: "equivalent" },
13
+ ],
14
+ WGL012: [
15
+ { tool: "gitlab-docs", rule: "Deprecated keywords (only/except, globally-defined image/services/cache/before_script/after_script)", url: "https://docs.gitlab.com/ci/yaml/deprecated_keywords/#only--except", relation: "equivalent" },
16
+ ],
17
+ WGL013: [
18
+ { tool: "gitlab-docs", rule: "Error: 'job' does not exist in the pipeline (needs)", url: "https://docs.gitlab.com/ci/yaml/needs/#error-job-does-not-exist-in-the-pipeline", relation: "equivalent" },
19
+ ],
20
+ WGL014: [
21
+ { tool: "gitlab-docs", rule: "extends: supported values are the name(s) of another job in the pipeline", url: "https://docs.gitlab.com/ci/yaml/#extends", relation: "equivalent" },
22
+ ],
23
+ WGL016: [
24
+ { tool: "gitlab-docs", rule: "CI/CD variable security (secrets belong in masked/protected variables, not in .gitlab-ci.yml)", url: "https://docs.gitlab.com/ci/variables/#cicd-variable-security", relation: "overlaps" },
25
+ ],
26
+ WGL023: [
27
+ { tool: "gitlab-docs", rule: "Pipeline warning: Job may allow multiple pipelines to run for a single action (final `when` rule without conditions)", url: "https://docs.gitlab.com/ci/debugging/#job-may-allow-multiple-pipelines-to-run-for-a-single-action-warning", relation: "overlaps" },
28
+ ],
29
+ WGL024: [
30
+ { tool: "gitlab-docs", rule: "allow_failure default: true for manual jobs, false for rules:when: manual", url: "https://docs.gitlab.com/ci/yaml/#allow_failure", relation: "overlaps" },
31
+ ],
32
+ WGL025: [
33
+ { tool: "gitlab-docs", rule: "cache:key: all jobs with `cache` but no `cache:key` share the `default` cache", url: "https://docs.gitlab.com/ci/yaml/#cachekey", relation: "overlaps" },
34
+ ],
35
+ WGL026: [
36
+ { tool: "gitlab-docs", rule: "Docker-in-Docker with TLS enabled (recommended); DOCKER_TLS_CERTDIR", url: "https://docs.gitlab.com/ci/docker/docker_in_docker/#docker-in-docker-with-tls-enabled-in-the-docker-executor-recommended", relation: "overlaps" },
37
+ ],
38
+ WGL029: [
39
+ { tool: "gitlab-docs", rule: "include:project ref guidance: use a specific SHA hash, protected branch/tag rules", url: "https://docs.gitlab.com/ci/yaml/#includeproject", relation: "overlaps" },
40
+ { tool: "gitlab-docs", rule: "CI/CD component security best practices: use pinned versions (commit SHA preferred), avoid `latest`", url: "https://docs.gitlab.com/ci/components/#for-component-users", relation: "overlaps" },
41
+ ],
42
+ WGL030: [
43
+ { tool: "gitlab-docs", rule: "include:remote: treat as a third-party dependency; verify integrity with include:integrity", url: "https://docs.gitlab.com/ci/yaml/#includeremote", relation: "overlaps" },
44
+ ],
45
+ WGL031: [
46
+ { tool: "gitlab-docs", rule: "Use checksum to keep your image secure (image@sha256:digest)", url: "https://docs.gitlab.com/ci/docker/using_docker_images/#use-checksum-to-keep-your-image-secure", relation: "overlaps" },
47
+ ],
48
+ WGL033: [
49
+ { tool: "gitlab-docs", rule: "id_tokens: the required `aud` sub-keyword configures the aud claim", url: "https://docs.gitlab.com/ci/yaml/#id_tokens", relation: "overlaps" },
50
+ ],
51
+ WGL035: [
52
+ { tool: "poutine", rule: "injection", url: "https://boostsecurityio.github.io/poutine/rules/injection/", relation: "overlaps" },
53
+ ],
54
+ WGL038: [
55
+ { tool: "gitlab-docs", rule: "Protect a CI/CD variable (available only to protected branches/tags)", url: "https://docs.gitlab.com/ci/variables/#protect-a-cicd-variable", relation: "overlaps" },
56
+ { tool: "gitlab-docs", rule: "Merge request pipelines from forked projects can steal secrets in the parent project", url: "https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/#use-with-forked-projects", relation: "overlaps" },
57
+ ],
58
+ WGL039: [
59
+ { tool: "gitlab-docs", rule: "CI/CD variable security: accidental-leak-job example (echo of $PASSWORD); mask sensitive variables", url: "https://docs.gitlab.com/ci/variables/#mask-a-cicd-variable", relation: "overlaps" },
60
+ ],
61
+ WGL044: [
62
+ { tool: "gitlab-docs", rule: "artifacts:public (default true: downloadable by anonymous users in public pipelines)", url: "https://docs.gitlab.com/ci/yaml/#artifactspublic", relation: "overlaps" },
63
+ ],
64
+ WGL046: [
65
+ { tool: "gitlab-docs", rule: "Cache key names: protected and non-protected branches do not share the cache by default", url: "https://docs.gitlab.com/ci/caching/#cache-key-names", relation: "overlaps" },
66
+ ],
67
+ WGL047: [
68
+ { tool: "poutine", rule: "unverified_script_exec", url: "https://boostsecurityio.github.io/poutine/rules/unverified_script_exec/", relation: "equivalent" },
69
+ ],
70
+ WGL048: [
71
+ { tool: "gitlab-docs", rule: "Use pipeline names (workflow:name)", url: "https://docs.gitlab.com/ci/debugging/#use-pipeline-names", relation: "overlaps" },
72
+ ],
73
+ };
@@ -1,6 +1,6 @@
1
1
  import { exec } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
- import { safeHeartbeat, sleep } from "@intentius/chant/op";
3
+ import { sleep } from "@intentius/chant/op";
4
4
 
5
5
  const execAsync = promisify(exec);
6
6
 
@@ -16,7 +16,7 @@ export interface GitlabPipelineArgs {
16
16
  /**
17
17
  * Trigger a GitLab CI pipeline and wait for it to complete successfully.
18
18
  * Requires `glab` CLI authenticated in the environment.
19
- * Uses longInfra profile — 20m timeout, heartbeat every poll.
19
+ * Uses the longInfra profile: 20m timeout, three attempts backing off from 30s.
20
20
  */
21
21
  export async function gitlabPipeline(args: GitlabPipelineArgs, signal?: AbortSignal): Promise<void> {
22
22
  const ref = args.ref ?? "HEAD";
@@ -34,7 +34,6 @@ export async function gitlabPipeline(args: GitlabPipelineArgs, signal?: AbortSig
34
34
  while (true) {
35
35
  if (signal?.aborted) throw new Error("gitlabPipeline aborted");
36
36
  attempt++;
37
- safeHeartbeat({ step: "gitlabPipeline", project: args.name, attempt });
38
37
 
39
38
  const { stdout } = await execAsync(
40
39
  `glab ci status --project ${args.name} --format json`,
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * gitlab Op activities — resolved by the core activity registry when a project's
3
3
  * `chant.config.ts` lists the `gitlab` lexicon. `gitlabPipeline` triggers a
4
- * pipeline over the GitLab CLI with heartbeat/retry semantics; relocated from the
5
- * temporal lexicon (#809) so gitlab's imperative activity lives with its product.
6
- * The `gitlabPipeline` step builder stays in core, re-exported from the temporal
7
- * Op-authoring barrel like the other core builders.
4
+ * pipeline over the GitLab CLI and polls it to completion under the step's
5
+ * profile; relocated from the hosting lexicon (#809) so gitlab's imperative
6
+ * activity lives with its product. The `gitlabPipeline` step builder stays in
7
+ * core and reaches authors through `@intentius/chant/op` like the other core
8
+ * builders.
8
9
  */
9
10
  export { gitlabPipeline } from "./gitlab";
10
11
  export type { GitlabPipelineArgs } from "./gitlab";
@@ -7,12 +7,11 @@
7
7
  * `./activities/gitlab.ts`. `opts`'s type below IS `GitlabPipelineArgs`
8
8
  * itself (via `Omit`/`WithStepRefs`) — never a hand-restated mirror.
9
9
  *
10
- * `core`'s own `gitlabPipeline` (in `@intentius/chant/op`, re-exported from
11
- * `@intentius/chant-lexicon-temporal`) is UNCHANGED and produces a
12
- * byte-identical `ActivityStep` for the same inputs — purely additive, and
13
- * deliberately not swapped into the temporal barrel (see
10
+ * `core`'s own `gitlabPipeline` (in `@intentius/chant/op`) is UNCHANGED and
11
+ * produces a byte-identical `ActivityStep` for the same inputs — purely
12
+ * additive, and deliberately not swapped into core's barrel (see
14
13
  * `lexicons/k8s/src/op/builders.ts`'s module doc for why: that would make
15
- * temporal depend on this package at runtime). An author who wants the typed
14
+ * core depend on this package at runtime). An author who wants the typed
16
15
  * surface imports it from here — `@intentius/chant-lexicon-gitlab`.
17
16
  */
18
17