Skip to content

Commit 650d8dd

Browse files
hyperpolymathclaude
andcommitted
feat(adr-0006): migrate 5 cartridges to 5-symbol ABI (Pattern A batch 2)
Pattern A migration (container-mcp, dap-mcp, database-mcp, feedback-mcp, fleet-mcp). boj_cartridge_invoke dispatch added via cartridge_shim. Tests passing per cartridge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ad7e016 commit 650d8dd

10 files changed

Lines changed: 445 additions & 0 deletions

File tree

cartridges/container-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 ctr_mod = b.addModule("container_ffi", .{
1325
.root_source_file = b.path("container_ffi.zig"),
1426
.target = target,
1527
.optimize = optimize,
1628
});
1729

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

cartridges/container-mcp/ffi/container_ffi.zig

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,45 @@ pub export fn boj_cartridge_version() [*:0]const u8 {
200200
return "0.1.0";
201201
}
202202

203+
// ═══════════════════════════════════════════════════════════════════════
204+
// ADR-0006 dispatch (boj_cartridge_invoke, 5th standard symbol)
205+
// ═══════════════════════════════════════════════════════════════════════
206+
207+
const shim = @import("cartridge_shim");
208+
209+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha — each arm
210+
/// returns a stub JSON body shaped to the tool's intended response.
211+
export fn boj_cartridge_invoke(
212+
tool_name: [*c]const u8,
213+
json_args: [*c]const u8,
214+
out_buf: [*c]u8,
215+
in_out_len: [*c]usize,
216+
) callconv(.c) i32 {
217+
_ = json_args;
218+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
219+
220+
const body: []const u8 = if (shim.toolIs(tool_name, "container_build"))
221+
"{\"result\":{\"status\":\"stub\"}}"
222+
else if (shim.toolIs(tool_name, "container_create"))
223+
"{\"result\":{\"status\":\"stub\"}}"
224+
else if (shim.toolIs(tool_name, "container_start"))
225+
"{\"result\":{\"status\":\"stub\"}}"
226+
else if (shim.toolIs(tool_name, "container_stop"))
227+
"{\"result\":{\"status\":\"stub\"}}"
228+
else if (shim.toolIs(tool_name, "container_remove"))
229+
"{\"result\":{\"status\":\"stub\"}}"
230+
else if (shim.toolIs(tool_name, "container_list"))
231+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
232+
else if (shim.toolIs(tool_name, "container_logs"))
233+
"{\"result\":{\"status\":\"stub\"}}"
234+
else if (shim.toolIs(tool_name, "container_inspect"))
235+
"{\"result\":{\"status\":\"stub\"}}"
236+
else
237+
return shim.RC_UNKNOWN_TOOL;
238+
239+
return shim.writeResult(out_buf, in_out_len, body);
240+
}
241+
203242
// ═══════════════════════════════════════════════════════════════════════
204243
// Tests
205244
// ═══════════════════════════════════════════════════════════════════════
@@ -278,3 +317,42 @@ test "max containers enforced" {
278317
// Next build should fail
279318
try std.testing.expectEqual(@as(c_int, -1), ctr_build(@intFromEnum(ContainerRuntime.podman), "overflow:latest"));
280319
}
320+
321+
// ═══════════════════════════════════════════════════════════════════════
322+
// ADR-0006 invoke dispatch tests
323+
// ═══════════════════════════════════════════════════════════════════════
324+
325+
test "invoke: each declared tool succeeds" {
326+
var buf: [256]u8 = undefined;
327+
const tools = [_][]const u8{
328+
"container_build",
329+
"container_create",
330+
"container_start",
331+
"container_stop",
332+
"container_remove",
333+
"container_list",
334+
"container_logs",
335+
"container_inspect",
336+
};
337+
for (tools) |t| {
338+
var len: usize = buf.len;
339+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
340+
try std.testing.expectEqual(@as(i32, 0), rc);
341+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
342+
}
343+
}
344+
345+
test "invoke: unknown tool returns -1" {
346+
var buf: [64]u8 = undefined;
347+
var len: usize = buf.len;
348+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
349+
try std.testing.expectEqual(@as(i32, -1), rc);
350+
}
351+
352+
test "invoke: buffer too small returns -3" {
353+
var buf: [4]u8 = undefined;
354+
var len: usize = buf.len;
355+
const rc = boj_cartridge_invoke("container_build", "{}", &buf, &len);
356+
try std.testing.expectEqual(@as(i32, -3), rc);
357+
try std.testing.expect(len > 4);
358+
}

cartridges/dap-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 dap_mod = b.addModule("dap_ffi", .{
1325
.root_source_file = b.path("dap_ffi.zig"),
1426
.target = target,
1527
.optimize = optimize,
1628
});
1729

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

cartridges/dap-mcp/ffi/dap_ffi.zig

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,53 @@ pub export fn boj_cartridge_version() [*:0]const u8 {
283283
return "0.1.0";
284284
}
285285

286+
// ═══════════════════════════════════════════════════════════════════════
287+
// ADR-0006 dispatch (boj_cartridge_invoke, 5th standard symbol)
288+
// ═══════════════════════════════════════════════════════════════════════
289+
290+
const shim = @import("cartridge_shim");
291+
292+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha — each arm
293+
/// returns a stub JSON body shaped to the tool's intended response.
294+
export fn boj_cartridge_invoke(
295+
tool_name: [*c]const u8,
296+
json_args: [*c]const u8,
297+
out_buf: [*c]u8,
298+
in_out_len: [*c]usize,
299+
) callconv(.c) i32 {
300+
_ = json_args;
301+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
302+
303+
const body: []const u8 = if (shim.toolIs(tool_name, "dap_start"))
304+
"{\"result\":{\"status\":\"stub\"}}"
305+
else if (shim.toolIs(tool_name, "dap_initialize"))
306+
"{\"result\":{\"status\":\"stub\"}}"
307+
else if (shim.toolIs(tool_name, "dap_launch"))
308+
"{\"result\":{\"status\":\"stub\"}}"
309+
else if (shim.toolIs(tool_name, "dap_attach"))
310+
"{\"result\":{\"status\":\"stub\"}}"
311+
else if (shim.toolIs(tool_name, "dap_set_breakpoints"))
312+
"{\"result\":{\"status\":\"stub\"}}"
313+
else if (shim.toolIs(tool_name, "dap_continue"))
314+
"{\"result\":{\"status\":\"stub\"}}"
315+
else if (shim.toolIs(tool_name, "dap_step_over"))
316+
"{\"result\":{\"status\":\"stub\"}}"
317+
else if (shim.toolIs(tool_name, "dap_step_in"))
318+
"{\"result\":{\"status\":\"stub\"}}"
319+
else if (shim.toolIs(tool_name, "dap_step_out"))
320+
"{\"result\":{\"status\":\"stub\"}}"
321+
else if (shim.toolIs(tool_name, "dap_stack_trace"))
322+
"{\"result\":{\"status\":\"stub\"}}"
323+
else if (shim.toolIs(tool_name, "dap_variables"))
324+
"{\"result\":{\"status\":\"stub\"}}"
325+
else if (shim.toolIs(tool_name, "dap_stop"))
326+
"{\"result\":{\"status\":\"stub\"}}"
327+
else
328+
return shim.RC_UNKNOWN_TOOL;
329+
330+
return shim.writeResult(out_buf, in_out_len, body);
331+
}
332+
286333
// ═══════════════════════════════════════════════════════════════════════
287334
// Tests
288335
// ═══════════════════════════════════════════════════════════════════════
@@ -346,3 +393,46 @@ test "breakpoint management" {
346393
const bp = dap_add_breakpoint(slot, @intFromEnum(BreakpointKind.function));
347394
try std.testing.expect(bp >= 0);
348395
}
396+
397+
// ═══════════════════════════════════════════════════════════════════════
398+
// ADR-0006 invoke dispatch tests
399+
// ═══════════════════════════════════════════════════════════════════════
400+
401+
test "invoke: each declared tool succeeds" {
402+
var buf: [256]u8 = undefined;
403+
const tools = [_][]const u8{
404+
"dap_start",
405+
"dap_initialize",
406+
"dap_launch",
407+
"dap_attach",
408+
"dap_set_breakpoints",
409+
"dap_continue",
410+
"dap_step_over",
411+
"dap_step_in",
412+
"dap_step_out",
413+
"dap_stack_trace",
414+
"dap_variables",
415+
"dap_stop",
416+
};
417+
for (tools) |t| {
418+
var len: usize = buf.len;
419+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
420+
try std.testing.expectEqual(@as(i32, 0), rc);
421+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
422+
}
423+
}
424+
425+
test "invoke: unknown tool returns -1" {
426+
var buf: [64]u8 = undefined;
427+
var len: usize = buf.len;
428+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
429+
try std.testing.expectEqual(@as(i32, -1), rc);
430+
}
431+
432+
test "invoke: buffer too small returns -3" {
433+
var buf: [4]u8 = undefined;
434+
var len: usize = buf.len;
435+
const rc = boj_cartridge_invoke("dap_start", "{}", &buf, &len);
436+
try std.testing.expectEqual(@as(i32, -3), rc);
437+
try std.testing.expect(len > 4);
438+
}

cartridges/database-mcp/ffi/build.zig

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

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

31+
db_mod.addImport("cartridge_shim", shim_mod);
32+
1933
// ── Tests ────────────────────────────────────────────────────────
2034
const db_tests = b.addTest(.{
2135
.root_module = db_mod,

cartridges/database-mcp/ffi/database_ffi.zig

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,41 @@ pub export fn boj_cartridge_version() [*:0]const u8 {
871871
return "0.1.0";
872872
}
873873

874+
// ═══════════════════════════════════════════════════════════════════════
875+
// ADR-0006 dispatch (boj_cartridge_invoke, 5th standard symbol)
876+
// ═══════════════════════════════════════════════════════════════════════
877+
878+
const shim = @import("cartridge_shim");
879+
880+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha — each arm
881+
/// returns a stub JSON body shaped to the tool's intended response.
882+
export fn boj_cartridge_invoke(
883+
tool_name: [*c]const u8,
884+
json_args: [*c]const u8,
885+
out_buf: [*c]u8,
886+
in_out_len: [*c]usize,
887+
) callconv(.c) i32 {
888+
_ = json_args;
889+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
890+
891+
const body: []const u8 = if (shim.toolIs(tool_name, "database_connect"))
892+
"{\"result\":{\"status\":\"stub\"}}"
893+
else if (shim.toolIs(tool_name, "database_query"))
894+
"{\"result\":{\"matches\":[],\"status\":\"stub\"}}"
895+
else if (shim.toolIs(tool_name, "database_execute"))
896+
"{\"result\":{\"status\":\"stub\"}}"
897+
else if (shim.toolIs(tool_name, "database_list_tables"))
898+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
899+
else if (shim.toolIs(tool_name, "database_describe"))
900+
"{\"result\":{\"status\":\"stub\"}}"
901+
else if (shim.toolIs(tool_name, "database_disconnect"))
902+
"{\"result\":{\"status\":\"stub\"}}"
903+
else
904+
return shim.RC_UNKNOWN_TOOL;
905+
906+
return shim.writeResult(out_buf, in_out_len, body);
907+
}
908+
874909
// ═══════════════════════════════════════════════════════════════════════
875910
// Tests
876911
// ═══════════════════════════════════════════════════════════════════════
@@ -1186,3 +1221,40 @@ test "verisimdb execute_vql rejects non-verisimdb slot" {
11861221
try std.testing.expectEqual(@as(c_int, @intFromEnum(ConnState.connected)), db_state(slot_i32));
11871222
try std.testing.expectEqual(@as(c_int, 0), db_disconnect(slot_i32));
11881223
}
1224+
1225+
// ═══════════════════════════════════════════════════════════════════════
1226+
// ADR-0006 invoke dispatch tests
1227+
// ═══════════════════════════════════════════════════════════════════════
1228+
1229+
test "invoke: each declared tool succeeds" {
1230+
var buf: [256]u8 = undefined;
1231+
const tools = [_][]const u8{
1232+
"database_connect",
1233+
"database_query",
1234+
"database_execute",
1235+
"database_list_tables",
1236+
"database_describe",
1237+
"database_disconnect",
1238+
};
1239+
for (tools) |t| {
1240+
var len: usize = buf.len;
1241+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
1242+
try std.testing.expectEqual(@as(i32, 0), rc);
1243+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
1244+
}
1245+
}
1246+
1247+
test "invoke: unknown tool returns -1" {
1248+
var buf: [64]u8 = undefined;
1249+
var len: usize = buf.len;
1250+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
1251+
try std.testing.expectEqual(@as(i32, -1), rc);
1252+
}
1253+
1254+
test "invoke: buffer too small returns -3" {
1255+
var buf: [4]u8 = undefined;
1256+
var len: usize = buf.len;
1257+
const rc = boj_cartridge_invoke("database_connect", "{}", &buf, &len);
1258+
try std.testing.expectEqual(@as(i32, -3), rc);
1259+
try std.testing.expect(len > 4);
1260+
}

cartridges/feedback-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 fb_mod = b.addModule("feedback_ffi", .{
1325
.root_source_file = b.path("feedback_ffi.zig"),
1426
.target = target,
1527
.optimize = optimize,
1628
});
1729

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

0 commit comments

Comments
 (0)