[FEAT] CLI: help from the catalogs, capsule show, and the capture-time repair (#413) - #450
Open
justin13888 wants to merge 9 commits into
Open
Conversation
…ogs-show-repair-413
Help text was the one user-facing surface the i18n contract could not reach: clap renders doc comments itself, and i18n-guard records the gap as a blind spot. This adds the seam (S-I8). - `cli::help::localize` walks a built `clap::Command` tree and replaces every about/long_about/help/long_help with the catalog message under a key derived from the command path and argument id (`cli.help.<path>.about`, `cli.help.<path>.arg.<id>`, ...). A missing key leaves the derive text in place, so a partial translation never prints a raw key. - `run()` builds the parser through the rewriter under the bundle negotiated from the process locale; `command_tree()` resolves through an explicitly pinned `en` bundle so `cli-surface.json` stays locale-proof and unchanged. - The 59 `en` entries are the derive text verbatim, and a unit test asserts that for every string in the tree plus byte-identical rendered help under `en`. That test is the gate i18n-guard cannot be for this surface; its blind-spot comment now says so. - The i18n design doc records the decision (help is localized) and the residual: ValueEnum variant help, which clap 4 cannot re-word without discarding the typed parser. Catalogs regenerated with `mise run i18n`.
Deploying capsule with
|
| Latest commit: |
e765feb
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://f5012b53.capsule-22k.pages.dev |
| Branch Preview URL: | https://feat-cli-help-catalogs-show.capsule-22k.pages.dev |
`asset_row_from_state` indexed `capture_timestamp`/`capture_utc` from the
in-memory `AssetState::capture_utc` shard, while `rebuild_index` projects
them from the signed sidecar — and the rebuild's own comment says the two
must agree. They were equal at import, so nothing observed the difference;
a capture-time correction (S-B17) is exactly the write that separates them,
because it re-signs the sidecar and deliberately leaves the
`media/{YYYY}/{YYYY-MM}` shard where the files are. Without this, a correct
repair would be invisible to the timeline until the next rebuild.
Behaviour-neutral for every asset written today; an unparseable sidecar
timestamp indexes as the epoch, as the rebuild already does.
Nothing in the CLI printed what the importer wrote into an asset's signed sidecar, so a user could not verify the Takeout enrichment (S-B10) that `--provider takeout` exists to deliver, and the migration guide had to say so instead of instructing the check (S-B18). - `capsule show <ASSET> --library <PATH>` resolves an asset id or a hex prefix (>= 8 chars) of the content hash — the SHA-256 a user already has from the guide's spot-hash step — and prints the sidecar projection: album, content type, hash, dimensions, capture and import instants, caption, rating, user and AI tags, the fix with its datum and source, cull flag, hidden, stack placement, LQIP presence, and the provenance record count. Every absent value is spelled out as unset. - An ambiguous prefix is refused with the match count; a 32-hex-digit prefix that parses as a bare UUID still reaches the prefix arm. - Every line is a `cli.show.*` catalog key (40 keys), including the list separator and the datum names, so a GCJ-02 fix stored verbatim is never read as WGS-84. - Smoke tests spawn the binary over a synthesized EXIF JPEG and over the Takeout fixture; the guide's metadata-sampling step is now executable and is asserted as written. `cli-surface.json` gains the verb; catalogs regenerated with `mise run i18n`.
Every asset imported before S-B16 carries its import time as its capture
time inside the signed sidecar; the correct value is recoverable from the
original's EXIF, but the wrong one is under signature, so correcting it is
a `metadata-update` issued by a key-holding client, not an edit (S-B17).
- `Workspace::set_capture_timestamp(asset_id, Timestamp)` appends one
signed `metadata-update` through `append_lifecycle`: sidecar re-signed,
blob re-sealed under a fresh nonce, binding self-checked, artifacts
rewritten, index row re-projected. It takes a `jiff::Timestamp` so an
out-of-range instant is unrepresentable at the call site.
- The media bundle is deliberately not relocated: the sidecar is
authoritative for the date and the `media/{YYYY}/{YYYY-MM}` directory is
only the shard fixed at import; the design records bucket-vs-timestamp
drift after a capture correction as expected, and `Workspace::open`
already reconciles it by keeping the directory.
- `Workspace::original_path(asset_id)` exposes the original's on-disk path
so the repair pass can re-read EXIF without decrypting anything.
The test imports, corrects, reopens from disk, and rebuilds the index,
asserting a two-record chain that verifies, an unmoved bundle, and the same
corrected instant from the live row and the rebuilt one.
…ogs-show-repair-413
Every asset imported before S-B16 carries its import time as its capture time inside the signed sidecar. This is the pass that goes back to the original and asks (S-B17). - `capsule repair capture-time --library <PATH> [--apply] [--limit N]` re-reads each original's EXIF under the importer's own resolution (`resolve_timezone` over `extract_exif`). An instant that disagrees with the sidecar is affected; a floating time or no EXIF resolves to nothing and is skipped rather than guessed as UTC, exactly as the importer skips it, which makes the pass a no-op on a post-S-B16 library by construction and leaves Takeout-folded captures alone. It never compares capture to import time. An unreadable original is reported as such, never as "no EXIF". - Dry run is the default: `push`/`sync` default to writing because they write to a re-drivable server; this appends an irreversible signed record per asset. `--apply` corrects each affected asset as its own `metadata-update` through `Workspace::set_capture_timestamp`, so an interrupted run leaves completed assets correct and a re-run skips them. `--limit` bounds one run's corrections; the report always covers the whole library. - Every printed line is a `cli.repair.*` key (12 keys). Unit tests cover the verdicts over synthesized EXIF, detect/apply/idempotence over a workspace, and the limit; the smoke tests spawn the binary through dry-run, apply, `show`, `library rebuild` and a second run. `cli-surface.json` gains the verb; catalogs regenerated with `mise run i18n`.
…s remainder S-I8: help is localized via the cli.help.* keys; the ValueEnum residual stays English. S-B18: `capsule show`, selected by asset id or content-hash prefix; the guide's sampling step is executable. S-B17: `capsule repair capture-time`, dry run by default, over the importer's own resolution rule, with the index-projection precondition and the expected post-repair drift recorded. S-B11 stays done* and links #452, which carries the real-archive run this machine cannot perform.
…ogs-show-repair-413
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Issue #413, stacked on #443 (
docs/reference-generation-415):--helprendered from the catalogs (S-I8), acapsule showread surface for what the importer wrote (S-B18), andcapsule repair capture-timefor sidecars stamped with import time before S-B16 (S-B17), plus the migration guide's sampling step made executable (S-B11's doc half).Summary
capsule_cli::cli::help::localizerewrites everyabout/long_about/help/long_helpof a builtclap::Commandtree fromcli.help.<path>.about/cli.help.<path>.arg.<id>catalog keys.run()applies it under the bundle negotiated fromLC_ALL/LC_MESSAGES/LANG;command_tree()applies it under an explicitly pinnedenbundle socli-surface.jsonis locale-proof and byte-unchanged. A missing key leaves the derive text (never a raw key). 59enentries are the derive text verbatim, gated by a unit test that walks the whole tree and by a byte-identical rendered-help comparison. The i18n design doc records the decision and theValueEnumresidual;i18n-guard's blind-spot comment now points at that test.Validation
Run inside the worktree
/var/mnt/scratch/golem/dev/Capsulsaurus/Capsule.worktrees/Capsule-feat-cli-help-catalogs-show-repair-413.cargo nextest run -p capsule-cli --libcli::helptests)mise run cli-surface-checkmise run i18n-checkmise run i18n-guardcargo clippy -p capsule-cli -p xtask --all-targets -- $CLIPPY_FLAGSproc-macro-error2future-incompat note from a dependency)cargo fmt --checkcargo fmt -p capsule-cli(the full gate set —
mise run check-rust,mise run test-rust,mise run check-docs-truth,check-docs/check-md— is recorded here when the last slice lands)Risks and rollout
run()now parses throughCommand::get_matches+FromArgMatches::from_arg_matches_mutinstead ofParser::parse; the two are the same code path inside clap, anddispatchis untouched, so every library-driven test keeps exercising the same arms.cli.help.*entries (every non-enlocale today) the rendered help is byte-identical to before. Translators fillcli.help.*through the documentedlocales/pull-request flow; nothing here translates.help.rs, therun()rewiring, thecli.help.*keys, regenerated catalogs).Related Issues
Refs #413. Stacked on #443 (targets its branch).
Decisions taken
Unresolved review notes
(append-only; empty)