console: commit the dump before opening
The snapshot commit pins gpgsign off, so an ambient gitconfig
cannot sign or break the machine's own commit.
Assisted-by: GLM-5.3 via pi
diff --git a/AGENTS.md b/AGENTS.md
index 7663869..6be7c68 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -25,7 +25,8 @@ rake dev # Dev server, seeded from test/fixtures/cards into a
# throwaway tmpdir on every start, reloading on change
# (needs fd and entr)
rake console # irb with the store open on the configured database
- # (console.rb loads it; the operations no screen has)
+ # (console.rb loads it), after committing the dump as a
+ # safety net for what the session might do there
rake import:macos:plan # Plan importing this Mac's iCloud contacts
# (LIMIT=n; reads Contacts, so leave it to the user)
rake import:macos:remove # Delete from this Mac the contacts the oldest
diff --git a/tasks/console.rake b/tasks/console.rake
index 96a3342..ce53cb6 100644
--- a/tasks/console.rake
+++ b/tasks/console.rake
@@ -1,9 +1,40 @@
+require "open3"
+require "pathname"
+
# For the operations no screen has: deleting a contact outright,
# dissolving a group. The session loads console.rb at the root, which
# opens the store; exec rather than sh, so the console is the process
# the terminal talks to and Ctrl-D ends it rather than a rake around
# it.
-desc "Open an irb console on the configured database (console.rb loads it)"
+#
+# Before the prompt, the dump is taken and committed, so a session
+# starts from a recorded snapshot of the three things the store cannot
+# rebuild (cards, birthdays, groups — the tables tasks/db.rake dumps)
+# and a mistake at the prompt is a revert in the dump's repository
+# rather than a restore from backup. This is the one place the tooling
+# runs git — the app still runs none
+# (docs/plans/2026-09-12-database-dump.md) — and the dump directory is
+# its own repository beside the code's, `data/` being ignored here.
+desc "Open an irb console on the configured database, the dump committed first"
task :console do
+ Rake::Task["db:dump"].invoke
+
+ require "pro_tacts"
+ dump = Pathname.new(ENV.fetch("DUMP") { (ProTacts.config.data_dir / "dump").to_s })
+ sh "git", "-C", dump.to_s, "init", "-b", "main" unless (dump / ".git").exist?
+ sh "git", "-C", dump.to_s, "add", "--all"
+
+ # Porcelain is the whole answer: empty means nothing to commit. A
+ # failure here must stop the console from opening rather than start
+ # a session its safety net never armed.
+ staged, error, = Open3.capture3("git", "-C", dump.to_s, "status", "--porcelain")
+ raise error unless error.empty?
+ unless staged.empty?
+ sh "git", "-C", dump.to_s,
+ "-c", "user.name=pro-tacts console", "-c", "user.email=console@localhost",
+ "-c", "commit.gpgsign=false",
+ "commit", "--message", "console snapshot #{Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")}"
+ end
+
exec "bundle", "exec", "irb", "-I", "lib", "-r", "./console"
end