NAME
App::karr::Cmd::Repair - Migrate an old board off double-encoded UTF-8 and off impossible start stamps
VERSION
version 0.500
SYNOPSIS
karr repair # report what would change
karr repair --yes # rewrite the affected refs
karr repair --json
DESCRIPTION
Two migrations of old board data, reported and applied together but kept apart in the output, because a board can need either one without the other.
Neither of them touches updated: a migration is not an edit, and bumping the stamp on every card it rewrites would destroy the very history it is repairing.
Double-encoded UTF-8
karr up to and including 0.402 handed YAML::XS::Dump output — UTF-8 octets — around as if it were characters, so every board written by those versions carries UTF-8 encoded twice in its task frontmatter, its board config, and its activity log. Task bodies are not affected: they were concatenated onto the Markdown document verbatim and are correctly encoded.
Such a board is still read correctly, because refs/karr/meta/encoding is absent and everything that loads board state undoes the second encoding on the way in (see "repair_mojibake" in App::karr::Encoding). This command makes that permanent: it rewrites the affected refs once and stamps the marker, after which nothing guesses at the board's bytes again.
It is safe to run on any board:
a board already at the current version is left completely alone;
a ref whose payload is pure ASCII is skipped, so an ASCII-only board comes out bit-identical apart from the new marker ref;
running it twice changes nothing the second time.
Start stamps that precede their own card
karr wrote started as a bare YYYY-MM-DD date until ticket #68 made it a full timestamp. A bare date reads as midnight UTC, so every card filed and picked up on the same day carries a started that is earlier than its own created — by up to a day. On karr's own board that is 75 of 116 finished cards. Nothing can be measured from such a stamp: counted, its cycle time exceeds its lead time, which is why App::karr::Cmd::Metrics leaves those cards out of the averages entirely.
This command raises started to the card's own created — the only defensible value, since the work cannot have begun before the card existed.
What that costs, stated because it is not recoverable afterwards:
A clamped card asserts that the work began the instant the card was filed, i.e. zero queue time. For a card filed in the morning and picked up at night that is false, and the true start is not recorded anywhere else — the activity log does not cover the boards whose history is oldest.
After the clamp nothing on the card marks the stamp as having been day-granular. A migrated card is indistinguishable from one that really was started the second it was created, so the migration cannot be undone or audited from the data.
Those cards become measurable for
karr metrics, with a cycle time equal to their lead time and a flow efficiency of 100%. That is arithmetic on the clamped value, not a finding about how the work ran.The same old karr wrote
completedday-granular too, and this command does not touch it. Where such acompletedfalls before the card's owncreated, raisingstartedtocreatedsteps over it, and the card's cycle time reads negative afterwards where before it was merely absent. That is 42 of the 75 clamped cards on karr's own board. They are counted on a line of their own in the report and instarted_clamped_over_completedunder--json, because the clamp creates that state rather than inheriting it (ticket #139).
The criterion is deliberately narrow. A card is clamped only when its started is a bare YYYY-MM-DD date and its created is a full YYYY-MM-DDTHH:MM:SSZ stamp of karr's own writing and midnight of that date really does precede that created. A started that precedes created in any other shape is a different, unknown fault — a hand edit, a clock skew, an import from another tool — and clamping it blind would erase the evidence for it, so it is reported and left alone.
Stamps this command does not repair
Every card is checked for the other orderings that cannot be true either (completed before started, completed before created, updated before created), and for stamps in a shape karr cannot compare at all. Those are counted and reported, never rewritten: this command clamps started and nothing else (ticket #138), and a repair that quietly normalised the rest would be the same mistake in a larger size. The counts describe the board as the run leaves it, so a dry run shows what --yes would produce rather than what is there now. completed is the one with a ticket of its own (#139): karr wrote it day-granular before #68 as well, on more cards than started, and unlike started it has no single defensible value to be raised to.
OPTIONS
--yesActually rewrite the refs. Without it the command only reports what it would change.
--jsonEmit the report as JSON instead of text.
up_to_dateanswers for the encoding migration alone, as it always has, so it is not on its own an answer to "does this board need repairing" — readstarted_clampedbeside it.applieddistinguishes a dry run'srepaired/started_clamped("would") from a--yesrun's ("did").stamp_anomaliescarries the findings above, and its lists are never acted on.
SEE ALSO
karr, App::karr, App::karr::Encoding, App::karr::Cmd::Backup, App::karr::Cmd::Import, App::karr::Cmd::Metrics, App::karr::Task
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/karr/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.