721ba656a3a3
pin now fails loudly with 'not implemented yet' instead of silently no-op'ing through the editFlakeLock stub and reporting fake success; unpin (which genuinely runs nix flake update) replaces pin in help/man since pin needs real flake.lock input-source resolution out of scope for a mechanical fix (NINA-008)
- time
- 2026-07-02 13:41 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- fable audit fix — commands.zig
- parent
- a2c2b67bd83a
↓ nina-721ba656.tar.gzatlas at this save
files changed5 files
src/commands.zig+17 −4src/errors.zig+2 −0src/exec.zig+0 −7src/man.zig+1 −1src/output.zig+1 −5src/commands.zig+17 −4
@@ -2007,17 +2007,30 @@20072007 20082008 // --- Pin / Unpin ---20092009 2010+// `nina pin` is not implemented — editFlakeLock (exec.zig) was a stub that2011+// silently did nothing while this printed a success line implying an input2012+// had been pinned (NINA-008). Pinning an input to a commit requires resolving2013+// that input's existing source (github/gitlab/generic git, each with its own2014+// flake-ref shape) from flake.lock before nix can override it, which is real2015+// scope beyond a mechanical fix — so this fails loudly instead of lying.20102016 pub fn pin(ctx: Ctx) !void {2011- const input = if (ctx.args.len > 0) ctx.args[0] else return error.Internal;2012- const commit = if (ctx.args.len > 1) ctx.args[1] else return error.Internal;2013- exec.editFlakeLock(ctx.gpa, input, commit) catch return error.ProcessFailed;2014- output.pinDone(input, commit);2017+ _ = ctx;2018+ errors.error_info.setMessage("nina pin is not implemented yet", .{});2019+ errors.error_info.setSuggestion("override manually: nix flake lock --override-input <input> <flake-ref>", .{});2020+ return error.NotImplemented;20152021 }20162022 20172023 pub fn unpin(ctx: Ctx) !void {20182024 const input = if (ctx.args.len > 0) ctx.args[0] else return error.Internal;20192025 _ = try exec.flakeUpdate(ctx.io, &ctx.machine, ctx.gpa, input, &.{});20202026 output.unpinDone(input);2027+}2028+2029+test "pin reports NotImplemented instead of a fake success" {2030+ // pin() ignores every field of ctx before returning, so an undefined2031+ // value is safe here and avoids constructing a real std.Io/machine/config.2032+ const ctx: Ctx = undefined;2033+ try std.testing.expectError(error.NotImplemented, pin(ctx));20212034 }20222035 20232036 // --- Hash / Fetch ---src/errors.zig+2 −0
@@ -19,6 +19,7 @@1919 AccessDenied,2020 SystemResources,2121 Unexpected,22+ NotImplemented,2223 };2324 2425 // ─── ErrorInfo (used for non-build errors) ───────────────────────────────────@@ -83,6 +84,7 @@8384 error.PackageNotFound => "package not found",8485 error.GenerationNotFound => "generation not found",8586 error.BuildFailed => "build failed",87+ error.NotImplemented => "not implemented yet",8688 else => "internal error",8789 };8890 }src/exec.zig+0 −7
@@ -1572,13 +1572,6 @@15721572 return r.stdout;15731573 }15741574 1575-// --- Pin ---1576-1577-pub fn editFlakeLock(_: std.mem.Allocator, _: []const u8, _: []const u8) !void {1578- // Stub: full implementation would parse and edit flake.lock JSON1579- // The pin/unpin flow works via nix flake update which regenerates flake.lock1580-}1581-15821575 pub fn openEditor(io: std.Io, gpa: std.mem.Allocator, editor: []const u8, path: []const u8, line: ?u32) !u8 {15831576 if (line) |l| {15841577 const line_arg = try std.fmt.allocPrint(gpa, "+{d}", .{l});src/man.zig+1 −1
@@ -185,7 +185,7 @@185185 .{ .cmd_block = "$ nina install neovim\n:: neovim 0.10.4\n\n [i] profile install instant no rebuild\n [s] system install opens editor requires apply\n [t] try now exits when done\n\n > i\n:: installing neovim profile\n -> nix profile install nixpkgs#neovim\n:: neovim installed available now" },186186 .{ .para = "explain why a package is installed:" },187187 .{ .cmd_block = "$ nina why ripgrep\n:: why ripgrep kyoshi\n\n ripgrep is a direct profile package" },188- .{ .para = "pin or unpin a flake input to a specific commit. `nina unpin` runs `nix flake update` on that input to release the pin." },188+ .{ .para = "`nina unpin <input>` releases a pinned flake input by running `nix flake update` on it." },189189 },190190 },191191 .{src/output.zig+1 −5
@@ -975,7 +975,7 @@975975 .{ "option <q>", "search nixos options" },976976 .{ "pkg", "inspect packages" },977977 .{ "why <pkg>", "what pulled a package in" },978- .{ "pin", "pin flake inputs" },978+ .{ "unpin <input>", "release a pinned flake input (nix flake update)" },979979 .{ "hash", "compute nix hash" },980980 .{ "fetch", "fetch and hash a url" },981981 .{ "goodbye", "uninstall nina" },@@ -1283,10 +1283,6 @@12831283 pub fn setupNixosNeedsRebuild(conf_path: []const u8) void {12841284 p("{s}already declared in {s}\n", .{ hdr(), conf_path });12851285 p(" just run {s}sudo nixos-rebuild switch{s} to activate it\n", .{ c(BOLD), c(RESET) });1286-}1287-1288-pub fn pinDone(input: []const u8, commit: []const u8) void {1289- p("{s}pinned {s} -> {s}\n", .{ hdr(), input, commit });12901286 }12911287 12921288 pub fn unpinDone(input: []const u8) void {