fix(cli): label a deck with the language it declares - #226
Merged
Conversation
…hat sets one `deck create` and `import` wrote `--front-lang`/`--back-lang` into the manifest and stopped there, so the ordinary tags a declared pair is supposed to contribute — `"spanish"`, plus the `"language"` umbrella — were never derived. A deck published with `--front-lang ja-JP` was invisible to tag browse, to `tag trending` and to anyone on Nexus looking for Japanese decks, while the byte-identical deck published from a phone was not: both ViewModels route a language pick through `LanguageTags.retag`, and only the headless path skipped it. Nothing reported it. The deck published, `--json` said ok, and the only symptom was a search that came back empty somewhere else entirely. Bulk `.apkg` import is the job the CLI was built for (#46), which made this the path where the most decks arrived and the least were discoverable. All three write paths now go through `LanguageTags` where the apps go through it on the pick — `deckCreate`, `import`'s `newDeck`, and the `--resume` overlay, which also has to *drop* the previous pair's labels since a resumed run can move the pair. `deck edit` already did it, but only when the pair actually moved, which left no way to repair a deck already published without labels short of retyping it to a different *region* of the same language and back. It now reconciles whenever the invocation **names** a pair, so restating the one a deck already has is the repair — and reconciling nothing is still not a write, because `changedFieldsFrom` diffs. Naming no pair leaves the labels entirely alone, so `--clear-tags` on a language deck still empties it: these are ordinary author-removable tags, not a reserved family, and that gesture has to be believed. A deck that declares no pair gets nothing, umbrella included — most decks are not language decks, and `"language"` on a deck of capital cities would be a lie the index repeats. Closes #225 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0116NZ92Pe7aGQpqtiRkP4Ye
…ng run A "Language decks" section in the CLI README with the four rules — a deck with no pair gets nothing, the labels are ordinary removable tags, retyping swaps them, and naming the pair reconciles them (which is the repair for a deck published before this existed). Architecture §7.7 point 4 gains the headless paths beside the ViewModels, and §13.7's `deck edit` bullet is corrected from "only when the pair moves" to "whenever the invocation names a pair". `journeys/RESULTS.md` records the run against live staging: the issue's own probe re-run, the read-back in a fresh process, the non-language deck, `--clear-tags` still emptying the set, and the resume overlay both moving and not moving the pair. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0116NZ92Pe7aGQpqtiRkP4Ye
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.
Closes #225.
What was wrong
deck createandimportaccepted--front-lang/--back-lang, wrote them into the manifest, and stopped there. The ordinary tags a declared pair is supposed to contribute —"spanish", plus the"language"umbrella (LanguageTags, Architecture §7.7 point 4) — were never derived.The labels are the entire reason the pair contributes a tag at all: a tag record is the only thing Loopky publishes that a network-wide index can answer questions about. So a deck imported with
loopky import deck.apkg --front-lang ja-JP --back-lang en-USwas invisible to tag browse, totag trendingand to anyone on Nexus looking for Japanese decks — while the byte-identical deck published from a phone was not, becausePublishDeckViewModelandDeckEditorViewModelboth route a language pick throughLanguageTags.retag. Only the headless path skipped it.Nothing reported it. The deck published,
--jsonsaid ok,deck showprinted the pair, and the only symptom was a search that came back empty somewhere else entirely. Bulk.apkgimport is the job the CLI was built for (#46), which made this the path where the most decks arrived and the least were discoverable.What changed
Every path that sets a pair now goes through
LanguageTagswhere the apps go through it on the pick:Decks.kt→deckCreateImport.kt→newDeckImport.kt→overlaidWith, the--resumeoverlay — which also has to drop the previous pair's labels, since a resumed run can move the pairdeck editalready did it, but only when the pair actually moved. It now reconciles whenever the invocation names a pair. That is the answer to the first thing the issue left open:Restating the pair a deck already has is the repair, so the workaround the issue described — retype the deck to a different region of the same language and back — is gone rather than documented. Reconciling nothing is still not a write, because
changedFieldsFromdiffs.And the second:
It still does, and the two commands now agree out loud: naming a pair reconciles the labels, naming none leaves them alone. So
--tag verbson a language deck drops them,--clear-tagsreally does empty the set, and passing the pair alongside either one puts them back. They are ordinary author-removable tags, not a reserved family, so those gestures have to be believed.Not all decks are language decks, and that is the invariant with the most tests on it:
LanguageTags.forPairreturns nothing for a deck that declares no pair, umbrella included, so"language"never lands on a deck of capital cities.Verification
11 new tests in
DeckLanguageTagsTestacross all four paths, plus a run against live staging with a real session, recorded injourneys/RESULTS.md:[language, english, spanish]— was[]deck showin a fresh processdeck create --tag geografia, no pair[geografia]— no umbrelladeck edit --clear-tagson a language deck[]deck editrestating the pair it already hadimport --tag core --front-lang ja-JP --back-lang en-US[core, language, japanese, english]import --resume --back-lang pt-BRenglishdropped,portugueseaddedimport --resumewith no flagstag trending --env staginglanguage/english/portuguese/spanishrank on NexusThree throwaway decks, created and deleted in the same run.
./gradlew :cli:test :shared:jvmTest detektAllgreen. No UI touched, so nojourneys/*.xmlneeded re-running.🤖 Generated with Claude Code
https://claude.ai/code/session_0116NZ92Pe7aGQpqtiRkP4Ye