const std = @import("std"); const config = @import("../config.zig"); const db = @import("../db.zig"); const core = @import("../core.zig"); const manifest_lib = @import("../manifest.zig"); pub fn execute(allocator: std.mem.Allocator, args: []const []const u8) !void { var flags = core.flags.CommonFlags{}; var command_args = try core.flags.parseCommon(allocator, args, &flags); defer command_args.deinit(allocator); core.output.setMode(if (flags.json) .json else .text); if (flags.help) { return printUsage(); } if (command_args.items.len < 1) { std.log.err("Usage: ml annotate [options]", .{}); return error.MissingArgument; } const run_id = command_args.items[0]; // Parse metadata options const text = core.flags.parseKVFlag(command_args.items, "text"); const hypothesis = core.flags.parseKVFlag(command_args.items, "hypothesis"); const outcome = core.flags.parseKVFlag(command_args.items, "outcome"); const confidence = core.flags.parseKVFlag(command_args.items, "confidence"); const privacy = core.flags.parseKVFlag(command_args.items, "privacy"); const author = core.flags.parseKVFlag(command_args.items, "author"); // Check that at least one option is provided if (text == null and hypothesis == null and outcome == null and privacy == null) { std.log.err("No metadata provided. Use --text, --hypothesis, --outcome, or --privacy", .{}); return error.MissingMetadata; } const cfg = try config.Config.load(allocator); defer { var mut_cfg = cfg; mut_cfg.deinit(allocator); } // Get DB path const db_path = try cfg.getDBPath(allocator); defer allocator.free(db_path); var database = try db.DB.init(allocator, db_path); defer database.close(); // Verify run exists const check_sql = "SELECT 1 FROM ml_runs WHERE run_id = ?;"; const check_stmt = try database.prepare(check_sql); defer db.DB.finalize(check_stmt); try db.DB.bindText(check_stmt, 1, run_id); const has_row = try db.DB.step(check_stmt); if (!has_row) { std.log.err("Run not found: {s}", .{run_id}); return error.RunNotFound; } // Add text note as a tag if (text) |t| { try addTag(allocator, &database, run_id, "note", t, author); } // Add hypothesis if (hypothesis) |h| { try addTag(allocator, &database, run_id, "hypothesis", h, author); } // Add outcome if (outcome) |o| { try addTag(allocator, &database, run_id, "outcome", o, author); if (confidence) |c| { try addTag(allocator, &database, run_id, "confidence", c, author); } } // Add privacy level if (privacy) |p| { try addTag(allocator, &database, run_id, "privacy", p, author); } // Checkpoint WAL database.checkpointOnExit(); if (flags.json) { std.debug.print("{{\"success\":true,\"run_id\":\"{s}\",\"action\":\"note_added\"}}\n", .{run_id}); } else { std.debug.print("Added note to run {s}\n", .{run_id[0..8]}); } } fn addTag( allocator: std.mem.Allocator, database: *db.DB, run_id: []const u8, key: []const u8, value: []const u8, author: ?[]const u8, ) !void { const full_value = if (author) |a| try std.fmt.allocPrint(allocator, "{s} (by {s})", .{ value, a }) else try allocator.dupe(u8, value); defer allocator.free(full_value); const sql = "INSERT INTO ml_tags (run_id, key, value) VALUES (?, ?, ?);"; const stmt = try database.prepare(sql); defer db.DB.finalize(stmt); try db.DB.bindText(stmt, 1, run_id); try db.DB.bindText(stmt, 2, key); try db.DB.bindText(stmt, 3, full_value); _ = try db.DB.step(stmt); } fn printUsage() !void { std.debug.print("Usage: ml annotate [options]\n\n", .{}); std.debug.print("Add metadata annotations to a run.\n\n", .{}); std.debug.print("Options:\n", .{}); std.debug.print("\t--text \t\tFree-form annotation\n", .{}); std.debug.print("\t--hypothesis \tResearch hypothesis\n", .{}); std.debug.print("\t--outcome \tOutcome: validates/refutes/inconclusive\n", .{}); std.debug.print("\t--confidence <0-1>\tConfidence in outcome\n", .{}); std.debug.print("\t--privacy \tPrivacy: private/team/public\n", .{}); std.debug.print("\t--author \t\tAuthor of the annotation\n", .{}); std.debug.print("\t--help, -h\t\tShow this help\n", .{}); std.debug.print("\t--json\t\t\tOutput structured JSON\n\n", .{}); std.debug.print("Examples:\n", .{}); std.debug.print("\tml annotate abc123 --text \"Try lr=3e-4 next\"\n", .{}); std.debug.print("\tml annotate abc123 --hypothesis \"LR scaling helps\"\n", .{}); std.debug.print("\tml annotate abc123 --outcome validates --confidence 0.9\n", .{}); }