feat(core): dry-run mode for reclassify, and a per-kind tally
CI / Build, lint & test (push) Successful in 3m7s

Production is frozen and sits on schema 19, so the content_kind correction it
needs cannot be applied yet. `--dry-run` runs the real UPDATEs and rolls the
transaction back, so an operator can see exactly what a run would touch before
touching a database they cannot easily restore — the counts are measured, not
predicted.

`updated_by_kind` reports the SHAPE of the change ("consumable 17, staff 3"),
which is the thing worth sanity-checking: a different shape means the source
profile moved and the mapping needs regenerating.

Measured against a copy of prod-core.db: 20 rows would change (17 consumable,
3 staff), 1966 already correct, 0 unmatched, and the copy was verified unchanged
afterwards.
This commit is contained in:
funman300
2026-08-21 21:23:43 +00:00
parent 30fae1a2f9
commit 233df1d99d
3 changed files with 107 additions and 8 deletions
+10 -6
View File
@@ -57,20 +57,24 @@ async fn main() -> Result<()> {
return Ok(());
}
// `openfut-core reclassify <request.json>` — correct the content_kind of
// already-imported owned rows. A profile import is once-only, so a taxonomy
// fix cannot arrive by re-importing; the adapter supplies card_id -> kind
// because only it can map its own taxonomy. Idempotent.
// `openfut-core reclassify <request.json> [--dry-run]` — correct the
// content_kind of already-imported owned rows. A profile import is
// once-only, so a taxonomy fix cannot arrive by re-importing; the adapter
// supplies card_id -> kind because only it can map its own taxonomy.
// Idempotent. `--dry-run` runs the same statements and rolls back, so an
// operator can see what a production run would touch before it touches it.
if std::env::args().nth(1).as_deref() == Some("reclassify") {
let path = std::env::args()
.nth(2)
.context("usage: openfut-core reclassify <request.json>")?;
.context("usage: openfut-core reclassify <request.json> [--dry-run]")?;
let dry_run = std::env::args().any(|a| a == "--dry-run");
let pool = db::init_pool(&cfg.database_url, cfg.max_connections).await?;
db::run_migrations(&pool).await?;
let raw = std::fs::read_to_string(&path)
.with_context(|| format!("read reclassify request {path}"))?;
let req: openfut_core::services::import::ReclassifyRequest =
let mut req: openfut_core::services::import::ReclassifyRequest =
serde_json::from_str(&raw).context("parse reclassify request JSON")?;
req.dry_run |= dry_run;
let outcome = openfut_core::services::import::reclassify_owned_content(&pool, &req).await?;
println!("{}", serde_json::to_string_pretty(&outcome)?);
return Ok(());