import: status reports the plans filed away done
A done: section under the in-flight list, whole counts, and nothing
about next steps when nothing is in flight.
Assisted-by: GLM-5.3 via pi
diff --git a/AGENTS.md b/AGENTS.md
index 9092477..5843f81 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -39,8 +39,9 @@ rake import:macos:finalize # Finalize the contacts the oldest
rake import:execute # Land the oldest plan still to land on a host
# (host in data/import/config.yml; PLAN=dir for
# another)
-rake import:status # Summarize the plans in data/import and what
- # execute and finalize would carry next
+rake import:status # Summarize the plans in data/import — in
+ # flight and filed away done — and what execute
+ # and finalize would carry next
rake profile:install # Download carddav.mobileconfig from the app and
# stage it for approval
```
diff --git a/docs/plans/2026-09-19-import-under-data-import.md b/docs/plans/2026-09-19-import-under-data-import.md
index a0c396f..ac467b6 100644
--- a/docs/plans/2026-09-19-import-under-data-import.md
+++ b/docs/plans/2026-09-19-import-under-data-import.md
@@ -48,6 +48,6 @@ still finishes and files away; nothing is rewritten on disk.
A plan whose every contact is done is finished (`Plan#done?`). The
finalize task files one away into `done/` as its last step, and the
status read sweeps any a dead run left in `active/` before listing —
-so what status lists is what still has work, and the finished plans
-are out of the way but kept, with their cards and sources, for as
-long as `data/` is.
+so its in-flight list is what still has work, and the finished plans
+appear under a `done:` section below, kept with their cards and
+sources for as long as `data/` is.
diff --git a/tasks/import.rake b/tasks/import.rake
index f78c211..a8d558b 100644
--- a/tasks/import.rake
+++ b/tasks/import.rake
@@ -95,37 +95,51 @@ namespace :import do
end
end
- desc "Summarize the plans in data/import and what execute and finalize would carry next"
+ desc "Summarize the plans in data/import — in flight and filed away done — and what execute and finalize would carry next"
task :status do
require "pro_tacts/import/plan"
# A finished plan files away; this sweeps any a dead run left in
- # active/, so what is listed is what still has work — which is also
- # why nothing here is ever finished: the sweep took it.
+ # active/, so the in-flight list below is what still has work —
+ # which is also why nothing in it is ever finished: the sweep took
+ # it.
ProTacts::Import::Plan.all(ImportTasks::Config::ACTIVE).each do |plan|
ImportTasks.file_away(plan) if plan.done?
end
plans = ProTacts::Import::Plan.all(ImportTasks::Config::ACTIVE)
- if plans.empty?
+ filed = ProTacts::Import::Plan.all(ImportTasks::Config::DONE)
+ if plans.empty? && filed.empty?
puts "no plans in #{ImportTasks::Config::ACTIVE}; run rake import:macos:plan"
next
end
- # One line per plan, as far along as it is: the fraction counts
- # everything past “planned”, because from “landed” on the card is
- # on the host — which is the state a glance wants. The plan names
- # are the directories', copy-pasteable into PLAN=.
- plans.each do |plan|
- done = plan.contacts.size - plan.with_status(nil).size
- puts "#{plan.dir.basename} #{done}/#{plan.contacts.size} contacts"
- end
+ # One line per plan in flight, as far along as it is: the fraction
+ # counts everything past “planned”, because from “landed” on the
+ # card is on the host — which is the state a glance wants. The
+ # plan names are the directories', copy-pasteable into PLAN=.
+ in_flight = plans.map {
+ carried = it.contacts.size - it.with_status(nil).size
+ "#{it.dir.basename} #{carried}/#{it.contacts.size} contacts"
+ }
- puts ""
landing = plans.find(&ImportTasks::TO_LAND)
finalizing = plans.find(&ImportTasks::TO_FINALIZE)
- puts "next: import:execute would land #{landing.dir.basename}" if landing
- puts "next: import:macos:finalize would finish #{finalizing.dir.basename}" if finalizing
+ next_up = [
+ ("next: import:execute would land #{landing.dir.basename}" if landing),
+ ("next: import:macos:finalize would finish #{finalizing.dir.basename}" if finalizing),
+ ].compact
+
+ # What was carried through, whole counts because every contact
+ # reached the final state — the fraction above can never appear
+ # here.
+ done = filed.map { "#{it.dir.basename} #{it.contacts.size} contacts" }
+
+ # Sections separated by a blank line only where one follows
+ # another, so a status with no plans in flight says nothing of
+ # next steps and still reports its history.
+ sections = [in_flight, next_up, done.empty? ? [] : ["done:", *done]].reject(&:empty?)
+ puts sections.map { it.join("\n") }.join("\n\n")
end
desc "Land the oldest plan in data/import/active still to land, or the one in PLAN, on the pro-tacts at the host in data/import/config.yml, a base URL such as https://contacts"
diff --git a/test/tasks/test_import.rb b/test/tasks/test_import.rb
index 51e426e..e8c780a 100644
--- a/test/tasks/test_import.rb
+++ b/test/tasks/test_import.rb
@@ -35,9 +35,22 @@ class ImportTasksTest < Minitest::Test
assert_includes out, "macos-20260916T180412Z 0/1 contacts\n"
assert_includes out, "next: import:execute would land macos-20260916T180412Z\n"
assert_includes out, "next: import:macos:finalize would finish macos-20260901T000000Z\n"
+ assert_includes out, "done:\nmacos-20260831T000000Z 2 contacts\n"
assert (root / "data/import/done/macos-20260831T000000Z/plan.yml").file?
refute (plans / "macos-20260831T000000Z").directory?
- refute_includes out, "macos-20260831T000000Z "
+ refute_includes out, "macos-20260831T000000Z 2/2 contacts"
+ end
+ end
+
+ def test_status_with_only_done_plans_reports_them_alone
+ Dir.mktmpdir do |root|
+ root = Pathname.new(root)
+ plan = Plan.write(root / "data/import/done/macos-20260831T000000Z", source: "macos", created_at: Time.utc(2026, 8, 31), entries: [entry("qxqmqmqmqmqm")])
+ plan.contacts.each { it.status or plan.record(it.id, Plan::DONE) }
+
+ out, = run_status(root)
+
+ assert_equal "done:\nmacos-20260831T000000Z 1 contacts\n", out
end
end