@intentius/chant-lexicon-temporal 0.1.18 → 0.1.19

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "ece9208dea0d260b5cd4f98f60ca94899c926f0feba151144ad3744cead23709",
4
+ "manifest.json": "f2aa4d7464c549279a3a981fec57d9984500a487d1320188f7dd5c0bb4dd194a",
5
5
  "meta.json": "c77a3c415993bed3865e07fa6db4fb7b9e87de0afa8d8675f64eadf50f914077",
6
6
  "types/index.d.ts": "5216f5256321f084a7c8211ef66ab599f621c74751cc3485cfc2a62502b81e2f",
7
7
  "rules/tmp001.ts": "689222211a93716a7e4432f6dd6a2a2ab54020b232049fb602893872585f9978",
@@ -11,5 +11,5 @@
11
11
  "skills/chant-temporal.md": "ff929983b33bb420c49ab61851f3799c5610256cb972d6c584792bb98b0cfb22",
12
12
  "skills/chant-temporal-ops.md": "7e83bc3e6e63af4ab14b8dc07aa00132d51991cf35b1bca29560d48934bff6dc"
13
13
  },
14
- "composite": "85f39f3f056d72a2e84243e18a7f9294d0ac12e118c02a08b4a3164068e711bb"
14
+ "composite": "996205d7000bcb6cddb88e9d6383a5195fc7504010952a727dc550425f186e2e"
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temporal",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Temporal",
6
6
  "intrinsics": [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-temporal",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Temporal lexicon for chant — server deployment, namespaces, search attributes, and schedules",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
package/src/config.ts CHANGED
@@ -143,6 +143,16 @@ export const TEMPORAL_ACTIVITY_PROFILES = {
143
143
  nonRetryableErrorTypes: ["ArgoSyncFailedError"],
144
144
  },
145
145
  },
146
+
147
+ /**
148
+ * Organizational policy gate (`policyGate`): build the project and evaluate
149
+ * `lint.policies`. Deterministic — a violation is the same on every attempt —
150
+ * so a single attempt, short timeout, no retry. Fails fast in both executors.
151
+ */
152
+ policyCheck: {
153
+ startToCloseTimeout: "5m",
154
+ retry: { maximumAttempts: 1 },
155
+ },
146
156
  } as const satisfies Record<string, TemporalActivityProfile>;
147
157
 
148
158
  export interface TemporalWorkerProfile {
package/src/index.ts CHANGED
@@ -48,5 +48,6 @@ export {
48
48
  lifecycleSnapshot,
49
49
  shell,
50
50
  teardown,
51
+ policyGate,
51
52
  } from "@intentius/chant/op";
52
53
  export type { OpConfig, PhaseDefinition, StepDefinition, ActivityStep, GateStep } from "@intentius/chant/op";
@@ -30,3 +30,6 @@ export type { NativeApplyArgs, CompensateApplyArgs, ApplyTarget, DeleteMode } fr
30
30
 
31
31
  export { waitForArgoSync, defaultArgoStatusFetcher, ArgoSyncFailedError } from "./argo";
32
32
  export type { WaitForArgoSyncArgs, ArgoAppStatus, ArgoStatusFetcher } from "./argo";
33
+
34
+ export { policyGate } from "./policy";
35
+ export type { PolicyGateArgs } from "./policy";
@@ -0,0 +1,19 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { resolve } from "node:path";
3
+ import { policyGate } from "./policy";
4
+
5
+ // The #201 org-policy example: a compliant workload + a policy pack
6
+ // (ORG-COST-CENTER all-envs, ORG-PROD-TLS prod-only). policyGate builds it and
7
+ // runs the policies — passing in dev, blocking in prod.
8
+ const orgPolicySrc = resolve(import.meta.dirname, "../../../../k8s/examples/org-policy/src");
9
+
10
+ describe("policyGate activity (#201 T4 — gate an apply on policy)", () => {
11
+ test("passes when policy is satisfied (no env, and dev)", async () => {
12
+ await expect(policyGate({ path: orgPolicySrc })).resolves.toBeUndefined();
13
+ await expect(policyGate({ path: orgPolicySrc, env: "dev" })).resolves.toBeUndefined();
14
+ }, 30_000);
15
+
16
+ test("blocks the apply on a prod policy violation", async () => {
17
+ await expect(policyGate({ path: orgPolicySrc, env: "prod" })).rejects.toThrow(/ORG-PROD-TLS/);
18
+ }, 30_000);
19
+ });
@@ -0,0 +1,32 @@
1
+ import { ApplicationFailure } from "@temporalio/common";
2
+ import { evaluateProjectPolicies } from "@intentius/chant/lint/policy";
3
+
4
+ export interface PolicyGateArgs {
5
+ /** Project/source directory to build and evaluate. Default ".". */
6
+ path?: string;
7
+ /** Environment for policy evaluation (falls back to `ownership.env`). */
8
+ env?: string;
9
+ }
10
+
11
+ /**
12
+ * Gate an apply on organizational policy: build the project, run its
13
+ * `lint.policies` over the resolved resources, and **block** on any violation.
14
+ * Place it as a step before the apply phase — a violation fails the workflow
15
+ * (non-retryable; the `policyCheck` profile is single-attempt) so nothing is
16
+ * applied. A clean evaluation passes through.
17
+ *
18
+ * Runs in both executors — it is a plain activity, not a Temporal gate.
19
+ */
20
+ export async function policyGate(args: PolicyGateArgs, _signal?: AbortSignal): Promise<void> {
21
+ const { violations, env } = await evaluateProjectPolicies({ path: args.path ?? ".", env: args.env });
22
+ if (violations.length > 0) {
23
+ const summary = violations
24
+ .map((v) => `[${v.checkId}]${v.entity ? ` ${v.entity}:` : ""} ${v.message}`)
25
+ .join("; ");
26
+ throw ApplicationFailure.nonRetryable(
27
+ `Organizational policy blocked the apply — ${violations.length} violation(s): ${summary}`,
28
+ "PolicyViolation",
29
+ );
30
+ }
31
+ console.log(`[policy] ${env ? `env=${env}: ` : ""}no violations — apply may proceed`);
32
+ }