@jeger-ai/opengantry 3.0.1 → 3.1.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 (116) hide show
  1. package/LICENSE +10 -27
  2. package/README.md +64 -16
  3. package/dist/cli/commands/attest.d.ts +8 -0
  4. package/dist/cli/commands/attest.d.ts.map +1 -0
  5. package/dist/cli/commands/attest.js +19 -0
  6. package/dist/cli/commands/attest.js.map +1 -0
  7. package/dist/cli/commands/doctor.d.ts +4 -2
  8. package/dist/cli/commands/doctor.d.ts.map +1 -1
  9. package/dist/cli/commands/doctor.js +1 -1
  10. package/dist/cli/commands/doctor.js.map +1 -1
  11. package/dist/cli/lib/attest-mission.d.ts +13 -0
  12. package/dist/cli/lib/attest-mission.d.ts.map +1 -0
  13. package/dist/cli/lib/attest-mission.js +22 -0
  14. package/dist/cli/lib/attest-mission.js.map +1 -0
  15. package/dist/cli/lib/attestation-receipt.d.ts +40 -0
  16. package/dist/cli/lib/attestation-receipt.d.ts.map +1 -0
  17. package/dist/cli/lib/attestation-receipt.js +110 -0
  18. package/dist/cli/lib/attestation-receipt.js.map +1 -0
  19. package/dist/cli/lib/canonical-json.d.ts +3 -0
  20. package/dist/cli/lib/canonical-json.d.ts.map +1 -0
  21. package/dist/cli/lib/canonical-json.js +13 -0
  22. package/dist/cli/lib/canonical-json.js.map +1 -0
  23. package/dist/cli/lib/constants.d.ts +2 -0
  24. package/dist/cli/lib/constants.d.ts.map +1 -1
  25. package/dist/cli/lib/constants.js +2 -0
  26. package/dist/cli/lib/constants.js.map +1 -1
  27. package/dist/cli/lib/doctor-core.d.ts +1 -1
  28. package/dist/cli/lib/doctor-core.d.ts.map +1 -1
  29. package/dist/cli/lib/doctor-core.js +25 -3
  30. package/dist/cli/lib/doctor-core.js.map +1 -1
  31. package/dist/cli/lib/draft-token.d.ts +1 -2
  32. package/dist/cli/lib/draft-token.d.ts.map +1 -1
  33. package/dist/cli/lib/draft-token.js +2 -12
  34. package/dist/cli/lib/draft-token.js.map +1 -1
  35. package/dist/cli/lib/flight-telemetry-doctor.d.ts +3 -0
  36. package/dist/cli/lib/flight-telemetry-doctor.d.ts.map +1 -0
  37. package/dist/cli/lib/flight-telemetry-doctor.js +25 -0
  38. package/dist/cli/lib/flight-telemetry-doctor.js.map +1 -0
  39. package/dist/cli/lib/gxt-config.d.ts +11 -0
  40. package/dist/cli/lib/gxt-config.d.ts.map +1 -1
  41. package/dist/cli/lib/gxt-config.js +10 -0
  42. package/dist/cli/lib/gxt-config.js.map +1 -1
  43. package/dist/cli/lib/kpi-advisory-findings.d.ts +5 -0
  44. package/dist/cli/lib/kpi-advisory-findings.d.ts.map +1 -0
  45. package/dist/cli/lib/kpi-advisory-findings.js +16 -0
  46. package/dist/cli/lib/kpi-advisory-findings.js.map +1 -0
  47. package/dist/cli/lib/kpi-engine.d.ts +2 -1
  48. package/dist/cli/lib/kpi-engine.d.ts.map +1 -1
  49. package/dist/cli/lib/kpi-engine.js +36 -20
  50. package/dist/cli/lib/kpi-engine.js.map +1 -1
  51. package/dist/cli/lib/mcp-attest.d.ts +6 -0
  52. package/dist/cli/lib/mcp-attest.d.ts.map +1 -0
  53. package/dist/cli/lib/mcp-attest.js +15 -0
  54. package/dist/cli/lib/mcp-attest.js.map +1 -0
  55. package/dist/cli/lib/mcp-tools-register.d.ts.map +1 -1
  56. package/dist/cli/lib/mcp-tools-register.js +13 -0
  57. package/dist/cli/lib/mcp-tools-register.js.map +1 -1
  58. package/dist/cli/lib/policy-digest-doctor.d.ts +11 -0
  59. package/dist/cli/lib/policy-digest-doctor.d.ts.map +1 -0
  60. package/dist/cli/lib/policy-digest-doctor.js +37 -0
  61. package/dist/cli/lib/policy-digest-doctor.js.map +1 -0
  62. package/dist/cli/lib/receipt-signing.d.ts +12 -0
  63. package/dist/cli/lib/receipt-signing.d.ts.map +1 -0
  64. package/dist/cli/lib/receipt-signing.js +150 -0
  65. package/dist/cli/lib/receipt-signing.js.map +1 -0
  66. package/dist/cli/lib/runtime-exec.d.ts +1 -0
  67. package/dist/cli/lib/runtime-exec.d.ts.map +1 -1
  68. package/dist/cli/lib/runtime-exec.js +21 -17
  69. package/dist/cli/lib/runtime-exec.js.map +1 -1
  70. package/dist/cli/lib/types.d.ts +1 -0
  71. package/dist/cli/lib/types.d.ts.map +1 -1
  72. package/dist/cli/lib/verify-engine.d.ts +2 -1
  73. package/dist/cli/lib/verify-engine.d.ts.map +1 -1
  74. package/dist/cli/lib/verify-engine.js +13 -2
  75. package/dist/cli/lib/verify-engine.js.map +1 -1
  76. package/dist/cli/lib/verify-options.d.ts +4 -0
  77. package/dist/cli/lib/verify-options.d.ts.map +1 -1
  78. package/dist/cli/lib/verify-options.js.map +1 -1
  79. package/dist/cli/lib/verify-payload.d.ts +1 -0
  80. package/dist/cli/lib/verify-payload.d.ts.map +1 -1
  81. package/dist/cli/lib/verify-payload.js +4 -0
  82. package/dist/cli/lib/verify-payload.js.map +1 -1
  83. package/dist/cli/lib/verify-presenters.d.ts +10 -5
  84. package/dist/cli/lib/verify-presenters.d.ts.map +1 -1
  85. package/dist/cli/lib/verify-presenters.js +22 -26
  86. package/dist/cli/lib/verify-presenters.js.map +1 -1
  87. package/dist/cli/lib/verify-receipt.d.ts +11 -0
  88. package/dist/cli/lib/verify-receipt.d.ts.map +1 -0
  89. package/dist/cli/lib/verify-receipt.js +40 -0
  90. package/dist/cli/lib/verify-receipt.js.map +1 -0
  91. package/dist/cli/lib/verify-run.d.ts.map +1 -1
  92. package/dist/cli/lib/verify-run.js +44 -6
  93. package/dist/cli/lib/verify-run.js.map +1 -1
  94. package/dist/cli/lib/version.gen.d.ts +1 -1
  95. package/dist/cli/lib/version.gen.js +1 -1
  96. package/dist/cli/lib/working-digests.d.ts +8 -0
  97. package/dist/cli/lib/working-digests.d.ts.map +1 -0
  98. package/dist/cli/lib/working-digests.js +21 -0
  99. package/dist/cli/lib/working-digests.js.map +1 -0
  100. package/dist/cli/program-core.d.ts.map +1 -1
  101. package/dist/cli/program-core.js +8 -2
  102. package/dist/cli/program-core.js.map +1 -1
  103. package/dist/cli/program-workflow.d.ts.map +1 -1
  104. package/dist/cli/program-workflow.js +21 -0
  105. package/dist/cli/program-workflow.js.map +1 -1
  106. package/package.json +2 -2
  107. package/templates/.gitagent/config.json +3 -0
  108. package/templates/.gitagent/foreman/SUBSTRATE.version.json +1 -1
  109. package/templates/.gitagent/planner/KPI-REPORT.schema.yaml +4 -0
  110. package/templates/.gitagent/planner/PERFORMANCE.md +18 -0
  111. package/templates/.gitagent/planner/PERFORMANCE_RUBRIC.template.md +32 -0
  112. package/templates/.githooks/pre-push +5 -5
  113. package/templates/.github/workflows/gxt-validate.yml +7 -4
  114. package/templates/integrations/compatibility.json +1 -1
  115. package/templates/scripts/lib/trusted-automation.mjs +56 -9
  116. package/templates/scripts/verify-pr-missions.sh +3 -3
@@ -0,0 +1,32 @@
1
+ # PERFORMANCE_RUBRIC (advisory LLM judge)
2
+
3
+ Use this rubric with a BYO `llm_verifiers` command in mission YAML. Verdicts are **ADVISORY_ONLY** — write `findings[]` to the KPI report; do **not** add blocking `kpi_gate` thresholds on judge metrics.
4
+
5
+ Judge **structural strategies only** (pooling, blocking I/O, memoization, streaming). Never guess runtime latency or throughput from source inspection.
6
+
7
+ Anchor each question to a rule `id` and cite `PERFORMANCE.md` via `doc_anchor` when emitting findings:
8
+
9
+ | Rule ID | Review question |
10
+ |---------|-----------------|
11
+ | `PERF-POOLING` | Did the diff introduce per-iteration client instantiation instead of pooling or injection? |
12
+ | `PERF-NONBLOCKING` | Did the diff add sync file/crypto/I/O inside an async handler critical path? |
13
+ | `PERF-MEMOIZATION` | Did the diff drop memoization or cache for an expensive repeated pure computation? |
14
+
15
+ Example verifier stdout fragment:
16
+
17
+ ```json
18
+ {
19
+ "metrics": { "perf_judge::reviewed": 1 },
20
+ "findings": [
21
+ {
22
+ "id": "PERF-POOLING",
23
+ "severity": "warn",
24
+ "path": "src/db.ts",
25
+ "line": 12,
26
+ "message": "new Pool() inside loop — see PERFORMANCE.md pooling policy",
27
+ "doc_anchor": "PERFORMANCE.md#connection-pooling"
28
+ }
29
+ ],
30
+ "exit_code": 0
31
+ }
32
+ ```
@@ -5,9 +5,9 @@ set -euo pipefail
5
5
  cd "$(git rev-parse --show-toplevel)"
6
6
 
7
7
  if [[ -f dist/cli/index.js ]]; then
8
- GAPMAN=(node dist/cli/index.js)
8
+ GANTRY=(node dist/cli/index.js)
9
9
  elif command -v gantry >/dev/null 2>&1; then
10
- GAPMAN=(gantry)
10
+ GANTRY=(gantry)
11
11
  else
12
12
  echo "pre-push: gantry not found — run: npm run build" >&2
13
13
  exit 1
@@ -35,17 +35,17 @@ for mission in "${missions[@]}"; do
35
35
  [[ -n "$mission" ]] || continue
36
36
  [[ -f "$mission" ]] || continue
37
37
  echo "pre-push: gantry verify --mission ${mission} (--pre-push)" >&2
38
- "${GAPMAN[@]}" verify --mission "$mission" --pre-push || exit 1
38
+ "${GANTRY[@]}" verify --mission "$mission" --pre-push || exit 1
39
39
  done
40
40
 
41
41
  if git diff --name-only "${base}..HEAD" | grep -qE '^\.gitagent/foreman/MANIFEST\.json$|^\.gitagent/planner/RULES\.md$|\.gxt-skill\.yaml$'; then
42
42
  echo "pre-push: gantry perimeter (advisory)" >&2
43
- "${GAPMAN[@]}" perimeter --base-ref "$base" || true
43
+ "${GANTRY[@]}" perimeter --base-ref "$base" || true
44
44
  fi
45
45
 
46
46
  if git diff --name-only "${base}..HEAD" | grep -qE '^\.gitagent/foreman/MANIFEST\.json$|^skills/'; then
47
47
  echo "pre-push: gantry check (manifest / skills changed)" >&2
48
- "${GAPMAN[@]}" check || exit 1
48
+ "${GANTRY[@]}" check || exit 1
49
49
  fi
50
50
 
51
51
  if git diff --name-only "${base}..HEAD" -- 'src/cli/**/*.ts' | grep -q .; then
@@ -41,7 +41,7 @@ jobs:
41
41
  with:
42
42
  fetch-depth: 0
43
43
 
44
- - uses: actions/setup-node@v6
44
+ - uses: actions/setup-node@v7
45
45
  with:
46
46
  node-version: "24"
47
47
  cache: "npm"
@@ -80,7 +80,7 @@ jobs:
80
80
  fetch-depth: 0
81
81
  ref: ${{ github.event.pull_request.head.sha }}
82
82
 
83
- - uses: actions/setup-node@v6
83
+ - uses: actions/setup-node@v7
84
84
  with:
85
85
  node-version: "24"
86
86
  cache: "npm"
@@ -102,7 +102,7 @@ jobs:
102
102
  steps:
103
103
  - uses: actions/checkout@v7
104
104
 
105
- - uses: actions/setup-node@v6
105
+ - uses: actions/setup-node@v7
106
106
  with:
107
107
  node-version: "24"
108
108
  cache: npm
@@ -142,7 +142,10 @@ jobs:
142
142
  fetch-depth: 0
143
143
  ref: ${{ github.event.pull_request.head.sha }}
144
144
 
145
- - uses: actions/setup-node@v6
145
+ - name: Install ripgrep
146
+ run: sudo apt-get update && sudo apt-get install -y ripgrep
147
+
148
+ - uses: actions/setup-node@v7
146
149
  with:
147
150
  node-version: "24"
148
151
  cache: npm
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema_version": "1",
3
- "opengantry_version": "3.0.1",
3
+ "opengantry_version": "3.1.0",
4
4
  "asset_catalog": "integrations/asset-catalog.json",
5
5
  "integrations": {
6
6
  "cursor": {
@@ -3,11 +3,14 @@ import path from "node:path";
3
3
  import { execFileSync } from "node:child_process";
4
4
  import { createRequire } from "node:module";
5
5
  import { fileURLToPath } from "node:url";
6
- import { pathMatchesAllowed } from "./glob-match.mjs";
7
- import { isMsnEnforcedPath, listMsnEnforcedPrefixes } from "./manifest-validate.mjs";
6
+ import { matchGlob, pathMatchesAllowed } from "./glob-match.mjs";
7
+ import { isMsnEnforcedPath, listMsnEnforcedPrefixes, readManifest } from "./manifest-validate.mjs";
8
8
 
9
- const HARD_MAX_NET_LOC = 5;
10
- const ALLOWED_STRUCTURAL_CHANGES = new Set(["workflow_version_pin"]);
9
+ const STRUCTURAL_KIND_HARD_MAX_NET_LOC = {
10
+ workflow_version_pin: 5,
11
+ bounded_content: 100,
12
+ };
13
+ const ALLOWED_STRUCTURAL_CHANGES = new Set(Object.keys(STRUCTURAL_KIND_HARD_MAX_NET_LOC));
11
14
  const CONFIG_REL = ".gitagent/config.json";
12
15
 
13
16
  function normalizePath(p) {
@@ -75,11 +78,18 @@ export function loadTrustedAutomationRules(repoRoot) {
75
78
  throw new Error(`gxt-manifest-lib: rule ${rule.id} has unsupported structural change: ${change}`);
76
79
  }
77
80
  }
81
+ if (rule.allowed_structural_changes.length !== 1) {
82
+ throw new Error(
83
+ `gxt-manifest-lib: rule ${rule.id} must declare exactly one allowed_structural_changes kind`,
84
+ );
85
+ }
86
+ const structuralKind = rule.allowed_structural_changes[0];
87
+ const hardCap = STRUCTURAL_KIND_HARD_MAX_NET_LOC[structuralKind];
78
88
  if (typeof rule.max_net_loc !== "number" || !Number.isInteger(rule.max_net_loc) || rule.max_net_loc < 1) {
79
89
  throw new Error(`gxt-manifest-lib: rule ${rule.id} max_net_loc must be a positive integer`);
80
90
  }
81
- if (rule.max_net_loc > HARD_MAX_NET_LOC) {
82
- throw new Error(`gxt-manifest-lib: rule ${rule.id} max_net_loc exceeds hard cap ${HARD_MAX_NET_LOC}`);
91
+ if (rule.max_net_loc > hardCap) {
92
+ throw new Error(`gxt-manifest-lib: rule ${rule.id} max_net_loc exceeds hard cap ${hardCap} for ${structuralKind}`);
83
93
  }
84
94
  rules.push({
85
95
  id: rule.id,
@@ -213,10 +223,36 @@ function readBlobAt(repoRoot, commitSha, filePath) {
213
223
  }
214
224
  }
215
225
 
216
- function validateStructuralChanges(repoRoot, files, rule, baseSha, headSha, mode) {
217
- if (!rule.allowed_structural_changes.has("workflow_version_pin")) {
218
- return { ok: false, reason: `rule ${rule.id}: no matching structural change policy` };
226
+ function listPerimeterProtectedGlobs(repoRoot) {
227
+ const manifest = readManifest(repoRoot);
228
+ if (!Array.isArray(manifest.perimeter_protected)) {
229
+ return [];
230
+ }
231
+ return manifest.perimeter_protected.map((g) => normalizePath(String(g)));
232
+ }
233
+
234
+ function isSubstrateHardDeniedPath(filePath, perimeterGlobs) {
235
+ const p = normalizePath(filePath);
236
+ if (p === ".gitagent" || p.startsWith(".gitagent/")) {
237
+ return true;
219
238
  }
239
+ return perimeterGlobs.some((g) => matchGlob(g, p));
240
+ }
241
+
242
+ function validateBoundedContentStructural(repoRoot, files, rule) {
243
+ const perimeterGlobs = listPerimeterProtectedGlobs(repoRoot);
244
+ for (const file of files) {
245
+ if (isSubstrateHardDeniedPath(file, perimeterGlobs)) {
246
+ return {
247
+ ok: false,
248
+ reason: `rule ${rule.id}: substrate hard-deny path not allowed (${file})`,
249
+ };
250
+ }
251
+ }
252
+ return { ok: true, reason: "" };
253
+ }
254
+
255
+ function validateWorkflowVersionPinStructural(repoRoot, files, rule, baseSha, headSha, mode) {
220
256
  const yaml = resolveYamlParser(repoRoot);
221
257
  const workflowFiles = files.filter((f) => /\.ya?ml$/i.test(f));
222
258
  for (const file of workflowFiles) {
@@ -241,6 +277,17 @@ function validateStructuralChanges(repoRoot, files, rule, baseSha, headSha, mode
241
277
  return { ok: true, reason: "" };
242
278
  }
243
279
 
280
+ function validateStructuralChanges(repoRoot, files, rule, baseSha, headSha, mode) {
281
+ const kind = [...rule.allowed_structural_changes][0];
282
+ if (kind === "bounded_content") {
283
+ return validateBoundedContentStructural(repoRoot, files, rule);
284
+ }
285
+ if (kind === "workflow_version_pin") {
286
+ return validateWorkflowVersionPinStructural(repoRoot, files, rule, baseSha, headSha, mode);
287
+ }
288
+ return { ok: false, reason: `rule ${rule.id}: no matching structural change policy` };
289
+ }
290
+
244
291
  function findMatchingRule(rules, actorEmail) {
245
292
  const actor = actorEmail.toLowerCase();
246
293
  return rules.filter((rule) => rule.allowed_actors.includes(actor));
@@ -56,9 +56,9 @@ if [[ ${#UNIQUE_MSNS[@]} -gt 1 ]]; then
56
56
  fi
57
57
 
58
58
  if [[ -f dist/cli/index.js ]]; then
59
- GAPMAN=(node dist/cli/index.js)
59
+ GANTRY=(node dist/cli/index.js)
60
60
  elif command -v gantry >/dev/null 2>&1; then
61
- GAPMAN=(gantry)
61
+ GANTRY=(gantry)
62
62
  else
63
63
  echo "verify-pr-missions: build gantry first (npm run build)" >&2
64
64
  exit 1
@@ -141,7 +141,7 @@ fi
141
141
 
142
142
  for mission in "${CHANGED_MISSIONS[@]}"; do
143
143
  echo "verify-pr-missions: gantry verify --mission ${mission}" >&2
144
- "${GAPMAN[@]}" verify --mission "$mission" --audience verifier || exit 1
144
+ "${GANTRY[@]}" verify --mission "$mission" --audience verifier || exit 1
145
145
  done
146
146
 
147
147
  echo "verify-pr-missions OK (${#CHANGED_MISSIONS[@]} mission(s))"