Skip to content

Commit ad7e016

Browse files
hyperpolymathclaude
andcommitted
feat(adr-0006): migrate 5 cartridges to 5-symbol ABI (Pattern A batch 1)
Pattern A migration (agent-mcp, bsp-mcp, cloud-mcp, codeseeker-mcp, comms-mcp). Each cartridge already exported the 4 baseline ADR-0005 symbols; this batch adds boj_cartridge_invoke dispatching cartridge.json tools to Grade-D-Alpha stub bodies via cartridge_shim. Tests passing per cartridge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0093051 commit ad7e016

10 files changed

Lines changed: 425 additions & 5 deletions

File tree

cartridges/agent-mcp/ffi/agent_ffi.zig

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,43 @@ pub export fn boj_cartridge_version() [*:0]const u8 {
186186
return "0.2.0";
187187
}
188188

189+
// ═══════════════════════════════════════════════════════════════════════
190+
// ADR-0006 dispatch (boj_cartridge_invoke, 5th standard symbol)
191+
// ═══════════════════════════════════════════════════════════════════════
192+
193+
const shim = @import("cartridge_shim");
194+
195+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha — each arm
196+
/// returns a stub JSON body shaped to the tool's intended response.
197+
export fn boj_cartridge_invoke(
198+
tool_name: [*c]const u8,
199+
json_args: [*c]const u8,
200+
out_buf: [*c]u8,
201+
in_out_len: [*c]usize,
202+
) callconv(.c) i32 {
203+
_ = json_args;
204+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
205+
206+
const body: []const u8 = if (shim.toolIs(tool_name, "agent_new_session"))
207+
"{\"result\":{\"session_id\":0,\"status\":\"stub\"}}"
208+
else if (shim.toolIs(tool_name, "agent_end_session"))
209+
"{\"result\":{\"ended\":true,\"status\":\"stub\"}}"
210+
else if (shim.toolIs(tool_name, "agent_transition"))
211+
"{\"result\":{\"transitioned\":true,\"status\":\"stub\"}}"
212+
else if (shim.toolIs(tool_name, "agent_state"))
213+
"{\"result\":{\"state\":\"unknown\",\"status\":\"stub\"}}"
214+
else if (shim.toolIs(tool_name, "agent_loop_count"))
215+
"{\"result\":{\"count\":0,\"status\":\"stub\"}}"
216+
else if (shim.toolIs(tool_name, "agent_validate_ooda"))
217+
"{\"result\":{\"valid\":true,\"status\":\"stub\"}}"
218+
else if (shim.toolIs(tool_name, "agent_reset"))
219+
"{\"result\":{\"reset\":true,\"status\":\"stub\"}}"
220+
else
221+
return shim.RC_UNKNOWN_TOOL;
222+
223+
return shim.writeResult(out_buf, in_out_len, body);
224+
}
225+
189226
// ═══════════════════════════════════════════════════════════════════════
190227
// Protocol Types (from proven-agentic, added in v0.2.0)
191228
// ═══════════════════════════════════════════════════════════════════════
@@ -404,3 +441,37 @@ test "validation matches transitions" {
404441
try std.testing.expectEqual(@as(c_int, 0), agent_validate_ooda(1, 4)); // Obs -> Act
405442
try std.testing.expectEqual(@as(c_int, 0), agent_validate_ooda(3, 1)); // Dec -> Obs
406443
}
444+
445+
// ═══════════════════════════════════════════════════════════════════════
446+
// ADR-0006 invoke dispatch tests
447+
// ═══════════════════════════════════════════════════════════════════════
448+
449+
test "invoke: each declared tool succeeds" {
450+
var buf: [256]u8 = undefined;
451+
const tools = [_][]const u8{
452+
"agent_new_session", "agent_end_session", "agent_transition",
453+
"agent_state", "agent_loop_count", "agent_validate_ooda",
454+
"agent_reset",
455+
};
456+
for (tools) |t| {
457+
var len: usize = buf.len;
458+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
459+
try std.testing.expectEqual(@as(i32, 0), rc);
460+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
461+
}
462+
}
463+
464+
test "invoke: unknown tool returns -1" {
465+
var buf: [64]u8 = undefined;
466+
var len: usize = buf.len;
467+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
468+
try std.testing.expectEqual(@as(i32, -1), rc);
469+
}
470+
471+
test "invoke: buffer too small returns -3" {
472+
var buf: [4]u8 = undefined;
473+
var len: usize = buf.len;
474+
const rc = boj_cartridge_invoke("agent_new_session", "{}", &buf, &len);
475+
try std.testing.expectEqual(@as(i32, -3), rc);
476+
try std.testing.expect(len > 4);
477+
}

cartridges/agent-mcp/ffi/build.zig

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@ pub fn build(b: *std.Build) void {
99
const target = b.standardTargetOptions(.{});
1010
const optimize = b.standardOptimizeOption(.{});
1111

12+
// Shared ADR-0006 invoke-shim module (relative path up to boj-server trunk).
13+
const shim_mod = b.addModule("cartridge_shim", .{
14+
.root_source_file = b.path("../../../ffi/zig/src/cartridge_shim.zig"),
15+
.target = target,
16+
.optimize = optimize,
17+
});
18+
1219
const agent_mod = b.addModule("agent_ffi", .{
1320
.root_source_file = b.path("agent_ffi.zig"),
1421
.target = target,
1522
.optimize = optimize,
1623
});
24+
agent_mod.addImport("cartridge_shim", shim_mod);
1725

1826
// ── Tests ────────────────────────────────────────────────────────
1927
const agent_tests = b.addTest(.{
@@ -26,13 +34,16 @@ pub fn build(b: *std.Build) void {
2634
test_step.dependOn(&run_tests.step);
2735

2836
// ── Shared library ──────────────────────────────────────────────
37+
const lib_mod = b.createModule(.{
38+
.root_source_file = b.path("agent_ffi.zig"),
39+
.target = target,
40+
.optimize = optimize,
41+
});
42+
lib_mod.addImport("cartridge_shim", shim_mod);
43+
2944
const lib = b.addLibrary(.{
3045
.name = "agent_mcp",
31-
.root_module = b.createModule(.{
32-
.root_source_file = b.path("agent_ffi.zig"),
33-
.target = target,
34-
.optimize = optimize,
35-
}),
46+
.root_module = lib_mod,
3647
.linkage = .dynamic,
3748
});
3849
b.installArtifact(lib);

cartridges/bsp-mcp/ffi/bsp_ffi.zig

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,47 @@ pub export fn boj_cartridge_version() [*:0]const u8 {
306306
return "0.1.0";
307307
}
308308

309+
// ═══════════════════════════════════════════════════════════════════════
310+
// ADR-0006 dispatch (boj_cartridge_invoke, 5th standard symbol)
311+
// ═══════════════════════════════════════════════════════════════════════
312+
313+
const shim = @import("cartridge_shim");
314+
315+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha — each arm
316+
/// returns a stub JSON body shaped to the tool's intended response.
317+
export fn boj_cartridge_invoke(
318+
tool_name: [*c]const u8,
319+
json_args: [*c]const u8,
320+
out_buf: [*c]u8,
321+
in_out_len: [*c]usize,
322+
) callconv(.c) i32 {
323+
_ = json_args;
324+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
325+
326+
const body: []const u8 = if (shim.toolIs(tool_name, "bsp_start"))
327+
"{\"result\":{\"status\":\"stub\"}}"
328+
else if (shim.toolIs(tool_name, "bsp_initialize"))
329+
"{\"result\":{\"status\":\"stub\"}}"
330+
else if (shim.toolIs(tool_name, "bsp_targets"))
331+
"{\"result\":{\"status\":\"stub\"}}"
332+
else if (shim.toolIs(tool_name, "bsp_compile"))
333+
"{\"result\":{\"status\":\"stub\"}}"
334+
else if (shim.toolIs(tool_name, "bsp_test"))
335+
"{\"result\":{\"status\":\"stub\"}}"
336+
else if (shim.toolIs(tool_name, "bsp_run"))
337+
"{\"result\":{\"status\":\"stub\"}}"
338+
else if (shim.toolIs(tool_name, "bsp_clean"))
339+
"{\"result\":{\"status\":\"stub\"}}"
340+
else if (shim.toolIs(tool_name, "bsp_diagnostics"))
341+
"{\"result\":{\"status\":\"stub\"}}"
342+
else if (shim.toolIs(tool_name, "bsp_stop"))
343+
"{\"result\":{\"status\":\"stub\"}}"
344+
else
345+
return shim.RC_UNKNOWN_TOOL;
346+
347+
return shim.writeResult(out_buf, in_out_len, body);
348+
}
349+
309350
// ═══════════════════════════════════════════════════════════════════════
310351
// Tests
311352
// ═══════════════════════════════════════════════════════════════════════
@@ -370,3 +411,43 @@ test "max BSP sessions enforced" {
370411
}
371412
try std.testing.expectEqual(@as(c_int, -1), bsp_init());
372413
}
414+
415+
// ═══════════════════════════════════════════════════════════════════════
416+
// ADR-0006 invoke dispatch tests
417+
// ═══════════════════════════════════════════════════════════════════════
418+
419+
test "invoke: each declared tool succeeds" {
420+
var buf: [256]u8 = undefined;
421+
const tools = [_][]const u8{
422+
"bsp_start",
423+
"bsp_initialize",
424+
"bsp_targets",
425+
"bsp_compile",
426+
"bsp_test",
427+
"bsp_run",
428+
"bsp_clean",
429+
"bsp_diagnostics",
430+
"bsp_stop",
431+
};
432+
for (tools) |t| {
433+
var len: usize = buf.len;
434+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
435+
try std.testing.expectEqual(@as(i32, 0), rc);
436+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
437+
}
438+
}
439+
440+
test "invoke: unknown tool returns -1" {
441+
var buf: [64]u8 = undefined;
442+
var len: usize = buf.len;
443+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
444+
try std.testing.expectEqual(@as(i32, -1), rc);
445+
}
446+
447+
test "invoke: buffer too small returns -3" {
448+
var buf: [4]u8 = undefined;
449+
var len: usize = buf.len;
450+
const rc = boj_cartridge_invoke("bsp_start", "{}", &buf, &len);
451+
try std.testing.expectEqual(@as(i32, -3), rc);
452+
try std.testing.expect(len > 4);
453+
}

cartridges/bsp-mcp/ffi/build.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,26 @@ pub fn build(b: *std.Build) void {
99
const target = b.standardTargetOptions(.{});
1010
const optimize = b.standardOptimizeOption(.{});
1111

12+
// Shared ADR-0006 invoke-shim module (relative path up to boj-server trunk).
13+
14+
const shim_mod = b.addModule("cartridge_shim", .{
15+
16+
.root_source_file = b.path("../../../ffi/zig/src/cartridge_shim.zig"),
17+
18+
.target = target,
19+
20+
.optimize = optimize,
21+
22+
});
23+
1224
const bsp_mod = b.addModule("bsp_ffi", .{
1325
.root_source_file = b.path("bsp_ffi.zig"),
1426
.target = target,
1527
.optimize = optimize,
1628
});
1729

30+
bsp_mod.addImport("cartridge_shim", shim_mod);
31+
1832
// ── Tests ────────────────────────────────────────────────────────
1933
const bsp_tests = b.addTest(.{
2034
.root_module = bsp_mod,

cartridges/cloud-mcp/ffi/build.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,26 @@ pub fn build(b: *std.Build) void {
99
const target = b.standardTargetOptions(.{});
1010
const optimize = b.standardOptimizeOption(.{});
1111

12+
// Shared ADR-0006 invoke-shim module (relative path up to boj-server trunk).
13+
14+
const shim_mod = b.addModule("cartridge_shim", .{
15+
16+
.root_source_file = b.path("../../../ffi/zig/src/cartridge_shim.zig"),
17+
18+
.target = target,
19+
20+
.optimize = optimize,
21+
22+
});
23+
1224
const cloud_mod = b.addModule("cloud_ffi", .{
1325
.root_source_file = b.path("cloud_ffi.zig"),
1426
.target = target,
1527
.optimize = optimize,
1628
});
1729

30+
cloud_mod.addImport("cartridge_shim", shim_mod);
31+
1832
// ── Tests ────────────────────────────────────────────────────────
1933
const cloud_tests = b.addTest(.{
2034
.root_module = cloud_mod,

cartridges/cloud-mcp/ffi/cloud_ffi.zig

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,37 @@ pub export fn boj_cartridge_version() [*:0]const u8 {
190190
return "0.1.0";
191191
}
192192

193+
// ═══════════════════════════════════════════════════════════════════════
194+
// ADR-0006 dispatch (boj_cartridge_invoke, 5th standard symbol)
195+
// ═══════════════════════════════════════════════════════════════════════
196+
197+
const shim = @import("cartridge_shim");
198+
199+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha — each arm
200+
/// returns a stub JSON body shaped to the tool's intended response.
201+
export fn boj_cartridge_invoke(
202+
tool_name: [*c]const u8,
203+
json_args: [*c]const u8,
204+
out_buf: [*c]u8,
205+
in_out_len: [*c]usize,
206+
) callconv(.c) i32 {
207+
_ = json_args;
208+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
209+
210+
const body: []const u8 = if (shim.toolIs(tool_name, "cloud_authenticate"))
211+
"{\"result\":{\"status\":\"stub\"}}"
212+
else if (shim.toolIs(tool_name, "cloud_logout"))
213+
"{\"result\":{\"status\":\"stub\"}}"
214+
else if (shim.toolIs(tool_name, "cloud_state"))
215+
"{\"result\":{\"status\":\"stub\"}}"
216+
else if (shim.toolIs(tool_name, "cloud_execute"))
217+
"{\"result\":{\"status\":\"stub\"}}"
218+
else
219+
return shim.RC_UNKNOWN_TOOL;
220+
221+
return shim.writeResult(out_buf, in_out_len, body);
222+
}
223+
193224
// ═══════════════════════════════════════════════════════════════════════
194225
// Vercel Provider (provider code 7)
195226
// Grade D Alpha — stub implementations
@@ -1202,3 +1233,38 @@ test "verpex wrong-provider rejection" {
12021233

12031234
_ = cloud_logout(slot);
12041235
}
1236+
1237+
// ═══════════════════════════════════════════════════════════════════════
1238+
// ADR-0006 invoke dispatch tests
1239+
// ═══════════════════════════════════════════════════════════════════════
1240+
1241+
test "invoke: each declared tool succeeds" {
1242+
var buf: [256]u8 = undefined;
1243+
const tools = [_][]const u8{
1244+
"cloud_authenticate",
1245+
"cloud_logout",
1246+
"cloud_state",
1247+
"cloud_execute",
1248+
};
1249+
for (tools) |t| {
1250+
var len: usize = buf.len;
1251+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
1252+
try std.testing.expectEqual(@as(i32, 0), rc);
1253+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
1254+
}
1255+
}
1256+
1257+
test "invoke: unknown tool returns -1" {
1258+
var buf: [64]u8 = undefined;
1259+
var len: usize = buf.len;
1260+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
1261+
try std.testing.expectEqual(@as(i32, -1), rc);
1262+
}
1263+
1264+
test "invoke: buffer too small returns -3" {
1265+
var buf: [4]u8 = undefined;
1266+
var len: usize = buf.len;
1267+
const rc = boj_cartridge_invoke("cloud_authenticate", "{}", &buf, &len);
1268+
try std.testing.expectEqual(@as(i32, -3), rc);
1269+
try std.testing.expect(len > 4);
1270+
}

cartridges/codeseeker-mcp/ffi/build.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,26 @@ pub fn build(b: *std.Build) void {
99
const target = b.standardTargetOptions(.{});
1010
const optimize = b.standardOptimizeOption(.{});
1111

12+
// Shared ADR-0006 invoke-shim module (relative path up to boj-server trunk).
13+
14+
const shim_mod = b.addModule("cartridge_shim", .{
15+
16+
.root_source_file = b.path("../../../ffi/zig/src/cartridge_shim.zig"),
17+
18+
.target = target,
19+
20+
.optimize = optimize,
21+
22+
});
23+
1224
const codeseeker_mod = b.addModule("codeseeker_ffi", .{
1325
.root_source_file = b.path("codeseeker_ffi.zig"),
1426
.target = target,
1527
.optimize = optimize,
1628
});
1729

30+
codeseeker_mod.addImport("cartridge_shim", shim_mod);
31+
1832
// ── Tests ────────────────────────────────────────────────────────
1933
const codeseeker_tests = b.addTest(.{
2034
.root_module = codeseeker_mod,

0 commit comments

Comments
 (0)