Skip to content

Commit a1a545a

Browse files
scotty595claude
andcommitted
Release governance-sdk 0.13.0
- OTel conventions default flips from "both" to "gen_ai"; governance spans now correlate with Anthropic/OpenAI/Vercel-AI spans out of the box. Pass conventions: "both" to keep emitting legacy governance.* op names alongside. - createMCPTrustRegistry and createChainAuditor emit a one-shot console.warn pointing to the honest names (createMCPAllowlist, createMCPCallRecorder). Refactored internals into buildAllowlist / buildCallRecorder so the honest names don't re-trigger the warning. - 1,340 tests, 0 failures (3 new tests pin the 0.13 OTel default). Both changes were promised in the 0.12 CHANGELOG. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 85075e2 commit a1a545a

7 files changed

Lines changed: 167 additions & 20 deletions

File tree

packages/governance/CHANGELOG.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,59 @@
11
# Changelog
22

3+
## [0.13.0] - 2026-04-16 — Conventions flip + deprecation notices
4+
5+
Follow-up to 0.12. Two small, deliberate changes that the 0.12 roadmap
6+
promised — committed now so users have runtime notice before 1.0.
7+
8+
### Changed — OTel `conventions` default flips from `"both"` to `"gen_ai"`
9+
10+
`createOtelHooks()` now defaults to emitting only the GenAI semantic
11+
conventions. Governance spans correlate out of the box with Anthropic,
12+
OpenAI, and Vercel-AI SDK spans in Honeycomb / Datadog / New Relic when
13+
you ingest them through the same tracer.
14+
15+
**Migration.** If your dashboards query the legacy `governance.*`
16+
operation names (`governance.enforcement`, `governance.audit`, etc.),
17+
set `conventions: "both"` explicitly:
18+
19+
```ts
20+
createOtelHooks({ conventions: "both" });
21+
```
22+
23+
This keeps the old op names alongside the new `gen_ai.*` attributes,
24+
same as the 0.12 default. `conventions: "governance"` disables GenAI
25+
emission entirely for customers who cannot adopt the spec yet.
26+
27+
### Changed — `createMCPTrustRegistry` and `createChainAuditor` now warn
28+
29+
Both of these names misrepresented what the functions do. The honest
30+
names (`createMCPAllowlist` and `createMCPCallRecorder`) shipped in
31+
0.12 as path re-exports, and 0.13 adds a one-shot `console.warn` when
32+
the old names are called so you see the nudge at runtime, once per
33+
process.
34+
35+
- `createMCPTrustRegistry` → rename to `createMCPAllowlist`
36+
(path: `governance-sdk/plugins/mcp-allowlist`)
37+
- `createChainAuditor` → rename to `createMCPCallRecorder`
38+
(path: `governance-sdk/plugins/mcp-call-recorder`)
39+
40+
Removal is scheduled for 1.0. Behaviour is identical across both
41+
names — the internals were refactored into a shared `buildAllowlist` /
42+
`buildCallRecorder` so the honest names call the core directly and
43+
don't trigger the deprecation path.
44+
45+
### Tests
46+
47+
1,340 tests, 0 failures (up from 1,337 — three new tests pinning the
48+
0.13 OTel default).
49+
50+
### Roadmap (0.14+)
51+
52+
Unchanged from the 0.12 CHANGELOG:
53+
- Multi-modal input scanning (image / PDF / audio) on Anthropic /
54+
Vercel AI / Bedrock / Genkit / LlamaIndex.
55+
- Signed compliance evidence export (EU AI Act + NIST AI RMF).
56+
357
## [0.12.0] - 2026-04-16 — Trust hardening
458

559
Closes the three most load-bearing honesty gaps surfaced by the post-0.11

packages/governance/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "governance-sdk",
3-
"version": "0.12.0",
3+
"version": "0.13.0",
44
"description": "AI Agent Governance for TypeScript — policy enforcement, scoring, compliance, and audit for AI agents",
55
"type": "module",
66
"main": "./dist/index.js",

packages/governance/src/otel-hooks.test.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ describe("OTel Hooks", () => {
1010
});
1111

1212
it("converts governance event to span", () => {
13-
const hooks = createOtelHooks();
13+
// Pin conventions: "both" because this test predates the 0.13 default
14+
// flip and asserts the legacy `governance.*` operation name + attrs.
15+
const hooks = createOtelHooks({ conventions: "both" });
1416
const span = hooks.toSpan({
1517
type: "enforcement",
1618
timestamp: new Date().toISOString(),
@@ -55,7 +57,7 @@ describe("OTel Hooks", () => {
5557
});
5658

5759
it("creates enforcement span via convenience method", () => {
58-
const hooks = createOtelHooks();
60+
const hooks = createOtelHooks({ conventions: "both" });
5961
const span = hooks.enforcementSpan({
6062
blocked: false,
6163
outcome: "allow",
@@ -69,7 +71,7 @@ describe("OTel Hooks", () => {
6971
});
7072

7173
it("handles missing optional fields gracefully", () => {
72-
const hooks = createOtelHooks();
74+
const hooks = createOtelHooks({ conventions: "both" });
7375
const span = hooks.toSpan({ type: "audit", timestamp: new Date().toISOString(), detail: {} });
7476
assert.equal(span.operationName, "governance.audit");
7577
assert.ok(!("governance.agent.id" in span.attributes));
@@ -84,6 +86,38 @@ describe("OTel Hooks", () => {
8486
});
8587
});
8688

89+
describe("OTel defaults (0.13)", () => {
90+
it("defaults to gen_ai conventions for events with a GenAI analogue", () => {
91+
const hooks = createOtelHooks();
92+
const span = hooks.toSpan({
93+
type: "enforcement",
94+
timestamp: new Date().toISOString(),
95+
detail: { blocked: false, outcome: "allow" },
96+
});
97+
assert.equal(span.operationName, "gen_ai.policy.evaluate");
98+
});
99+
100+
it("defaults to gen_ai: audit events use gen_ai.audit.log, not governance.audit", () => {
101+
const hooks = createOtelHooks();
102+
const span = hooks.toSpan({
103+
type: "audit",
104+
timestamp: new Date().toISOString(),
105+
detail: {},
106+
});
107+
assert.equal(span.operationName, "gen_ai.audit.log");
108+
});
109+
110+
it("defaults to gen_ai: events with no GenAI analogue still use governance.*", () => {
111+
const hooks = createOtelHooks();
112+
const span = hooks.toSpan({
113+
type: "kill",
114+
timestamp: new Date().toISOString(),
115+
detail: { reason: "emergency" },
116+
});
117+
assert.equal(span.operationName, "governance.kill");
118+
});
119+
});
120+
87121
describe("OTel GenAI semantic conventions (0.12)", () => {
88122
it("emits gen_ai.* attributes when conventions: 'gen_ai'", () => {
89123
const hooks = createOtelHooks({ conventions: "gen_ai" });

packages/governance/src/otel-hooks.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
* Produces structured span-compatible data from governance events.
55
* Zero dependencies — outputs plain objects that users wire to their OTel tracer.
66
*
7-
* Since 0.12, `createOtelHooks({ conventions })` can emit attributes using:
7+
* Since 0.13, `createOtelHooks({ conventions })` defaults to "gen_ai" so
8+
* governance spans correlate out of the box with Anthropic / OpenAI /
9+
* Vercel-AI SDK spans in Honeycomb / Datadog / New Relic. Options:
810
* - "governance" — legacy `governance.*` namespace (pre-0.12 default)
9-
* - "gen_ai" — OpenTelemetry GenAI semantic conventions (recommended
10-
* going forward — correlates with spans emitted by the
11-
* Anthropic / OpenAI / Vercel-AI SDKs when they adopt
12-
* the same conventions)
13-
* - "both" — emit both; current default for 0.12, flips to
14-
* "gen_ai" in 0.13
11+
* - "gen_ai" — OpenTelemetry GenAI semantic conventions (0.13 default)
12+
* - "both" — emit both; pin this if you have dashboards that query
13+
* the legacy `governance.*` operation names
1514
*
1615
* GenAI spec: https://opentelemetry.io/docs/specs/semconv/gen-ai/
1716
*
@@ -56,8 +55,9 @@ export interface OtelHooksConfig {
5655
/** Service name for span attributes (default: "governance-sdk") */
5756
serviceName?: string;
5857
/**
59-
* Which attribute namespace to emit. Default: "both" in 0.12 for smooth
60-
* migration. Flips to "gen_ai" in 0.13. Set explicitly to pin behaviour.
58+
* Which attribute namespace to emit. Default "gen_ai" from 0.13 onward.
59+
* Pass "both" to additionally emit the legacy `governance.*` namespace
60+
* or "governance" to fully disable gen_ai.* output.
6161
*/
6262
conventions?: OtelConventions;
6363
/** Custom attribute mapper — runs after built-in mapping. */
@@ -153,7 +153,10 @@ function addGovernanceAttributes(
153153

154154
export function createOtelHooks(config: OtelHooksConfig = {}) {
155155
const serviceName = config.serviceName ?? "governance-sdk";
156-
const conventions = config.conventions ?? "both";
156+
// Default flipped from "both" → "gen_ai" in 0.13 per the 0.12 roadmap.
157+
// Back-compat: set `conventions: "both"` to keep emitting legacy
158+
// governance.* operation names alongside the new gen_ai.* ones.
159+
const conventions = config.conventions ?? "gen_ai";
157160

158161
return {
159162
/**

packages/governance/src/plugins/mcp-call-recorder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* ```
1717
*/
1818
export {
19-
createChainAuditor as createMCPCallRecorder,
19+
createMCPCallRecorder,
2020
createChainAuditor,
2121
type ChainEntry,
2222
type ChainEntry as MCPCallRecord,

packages/governance/src/plugins/mcp-chain-audit.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,35 @@ export interface PatternMatch {
5555

5656
// ─── Implementation ─────────────────────────────────────────
5757

58+
let chainAuditorDeprecationWarned = false;
59+
60+
/**
61+
* Deprecated since 0.12, removed in 1.0. Prefer `createMCPCallRecorder`
62+
* (same behaviour, honest name — records caller-reported MCP calls, does
63+
* not auto-propagate across MCP server boundaries).
64+
*/
5865
export function createChainAuditor(config: ChainAuditConfig = {}) {
66+
if (!chainAuditorDeprecationWarned) {
67+
chainAuditorDeprecationWarned = true;
68+
// eslint-disable-next-line no-console
69+
console.warn(
70+
"[governance-sdk] createChainAuditor is deprecated since 0.12 — it is a caller-instrumented call recorder, not an automatic sub-call propagator. " +
71+
"Rename to createMCPCallRecorder (import path: governance-sdk/plugins/mcp-call-recorder). " +
72+
"The old name will be removed in 1.0.",
73+
);
74+
}
75+
return buildCallRecorder(config);
76+
}
77+
78+
/**
79+
* The honest name. Identical behaviour to `createChainAuditor` without the
80+
* deprecation warning. Use this in all new code.
81+
*/
82+
export function createMCPCallRecorder(config: ChainAuditConfig = {}) {
83+
return buildCallRecorder(config);
84+
}
85+
86+
function buildCallRecorder(config: ChainAuditConfig = {}) {
5987
const { maxChainLength = 50 } = config;
6088
const chains = new Map<string, ChainEntry[]>();
6189
const patterns = config.suspiciousPatterns ?? getDefaultPatterns();

packages/governance/src/plugins/mcp-trust.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,34 @@ export interface MCPTrustValidation {
5454

5555
// ─── Implementation ─────────────────────────────────────────
5656

57+
let trustRegistryDeprecationWarned = false;
58+
59+
/**
60+
* Deprecated since 0.12, removed in 1.0. Prefer `createMCPAllowlist`
61+
* (same behaviour, honest name).
62+
*/
5763
export function createMCPTrustRegistry(config: MCPTrustConfig = {}) {
64+
if (!trustRegistryDeprecationWarned) {
65+
trustRegistryDeprecationWarned = true;
66+
// eslint-disable-next-line no-console
67+
console.warn(
68+
"[governance-sdk] createMCPTrustRegistry is deprecated since 0.12 — it is a URI allowlist, not a cryptographic trust registry. " +
69+
"Rename to createMCPAllowlist (import path: governance-sdk/plugins/mcp-allowlist). " +
70+
"The old name will be removed in 1.0.",
71+
);
72+
}
73+
return buildAllowlist(config);
74+
}
75+
76+
/**
77+
* The honest name. Identical behaviour to `createMCPTrustRegistry` without
78+
* the deprecation warning. Use this in all new code.
79+
*/
80+
export function createMCPAllowlist(config: MCPTrustConfig = {}) {
81+
return buildAllowlist(config);
82+
}
83+
84+
function buildAllowlist(config: MCPTrustConfig = {}) {
5885
const { defaultTrust = "untrusted", blockUntrusted = false } = config;
5986
const registry = new Map<string, MCPServerEntry>();
6087

@@ -144,13 +171,14 @@ function normalizeUri(uri: string): string {
144171
return uri.toLowerCase().replace(/\/+$/, "");
145172
}
146173

147-
// ─── Honest-naming aliases (0.12) ───────────────────────────
174+
// ─── Honest-naming type aliases (0.12) ───────────────────────
148175
//
149176
// `createMCPTrustRegistry` is an allowlist — not a cryptographic trust
150-
// registry. The honest name is exported alongside so new code can adopt
151-
// it; the original export stays for backwards compatibility. The path
152-
// /plugins/mcp-allowlist in package.json exports re-exports these.
153-
export const createMCPAllowlist = createMCPTrustRegistry;
177+
// registry. The honest name `createMCPAllowlist` is defined above; these
178+
// are the matching type aliases so TypeScript users can write
179+
// `const gate: MCPAllowlistValidation = ...` without reaching back into
180+
// the legacy `MCPTrust*` names. The path /plugins/mcp-allowlist in
181+
// package.json exports re-exports these.
154182
export type MCPAllowlistLevel = MCPTrustLevel;
155183
export type MCPAllowlistConfig = MCPTrustConfig;
156184
export type MCPAllowlistEntry = MCPServerEntry;

0 commit comments

Comments
 (0)