yandex/yatagan/yatagan-2-migration
Claude Code and agent skill by yandex/yatagan
- community
Checking that the SKILL.md file is still in the repository.
Use when upgrading a project from Yatagan 1.x to Yatagan 2.0 — bumping the dependency version, or hitting 2.0 errors such as "Missing binding" for a child component, @Provides(Conditional(...)) not compiling, setupReflectionBackend/setThreadAsserter missing, or Yatagan$ generated class names not found.
Install
npx vskill@latest install yandex/yatagan/yatagan-2-migrationSKILL.md
Yatagan 1.x → 2.0 Migration
Migrate a codebase from Yatagan 1.x (typically 1.6.2) to 2.0. Work through every step below; each is independent — skip a step only after its search (Step 0 inventory) proves the project has nothing to migrate for it.
Ground rules
- Full cleanup. Deprecated-but-still-working Yatagan APIs (legacy
@Conditionfamily,api-compiledartifact) MUST be migrated too. Only warnings unrelated to Yatagan are out of scope. - Redirect search output to files. For every
grep/rg, redirect raw output to a file and read the file (e.g.rg <pattern> > /tmp/yatagan-scan.txt 2>&1). Inline tool output may be truncated or rewritten by context-saving wrappers, producing wrong match counts. On very large reposrgcan time out or silently produce an empty output file — if a search is slow or a result file is suspiciously empty, fall back to plaingrep -r --include=... --exclude-dir=buildand cross-check. - Distrust zero hits — and huge hit counts. If a search returns nothing, re-run with a simpler substring and different quoting before concluding the pattern is absent. In particular, mind
$escaping per tool:grep -F 'Yatagan$'(literal, no escape) orgrep -E 'Yatagan\$'(escaped) —-Fwith\$matches a literal backslash and silently finds nothing. Conversely, excludebuild/,.gradle/, and other generated-output directories, or generated sources will drown the real matches. - Compiling is not evidence of cleanup. Yatagan 2.0 still ships the deprecated public symbols (
@Conditionfamily,Yatagan.setThreadAsserter, theThreadAssertertype,api-compiled), so a green build does not prove steps 2b/2d/2i were done — only a zero-hit re-scan does. Internal machinery behind them may be gone, though (e.g.com.yandex.yatagan.internal.ThreadAssertions.setAsserterno longer exists) — profile/keep-rule entries naming internals are dead references to delete. - Build per repo rules. Find how this repository verifies a build (its own docs / agent rules — and check for project-specific CLI wrappers on
PATH, not just Gradle/Maven invocations named in docs) and use that command for the baseline and final verification. A transient toolchain or build-daemon crash on a first/cold build is environmental — retry once before treating it as a migration failure.
Step 0 — Recon and baseline
- Locate the Yatagan version declaration (version catalog, dependency manifest, or build scripts) and all Yatagan artifact dependencies (
api-public,api-compiled,api-dynamic,api-common, processor artifacts). - Run the repo's build check to confirm a green baseline before changing anything.
- Inventory usages (redirect each to a file, then read the file):
@Provides(— conditional provides (step 2a)@Condition(,@AllConditions(,@AnyCondition(,@AnyConditions(— legacy conditions (step 2b). Use these paren-anchored literals (withgrep -F), not\bword boundaries —\bis a GNU extension that BSD grep may silently ignore. Also search for stale imports with an end-anchored pattern —grep -E 'yatagan\.(Condition|AllConditions|AnyCondition|AnyConditions)$'— or the search drowns inConditional/Conditionals/ConditionExpression, which share the prefix. Beware the near-miss `@Conditionals` (repeatable container of@Conditional): it looks like the legacy@AnyConditionsbut is a current API and is NOT legacy — exclude it from 2b hits.isRoot = false/ component interfaces — child components (step 2c; triage per 2c, do not audit every injection site up front)ThreadAsserter(the type — this also catches baseline-profile/keep-rule entries likeLcom/yandex/yatagan/ThreadAsserter;andThreadAssertions;->setAsserter(...)that asetThreadAssertersearch misses), plussetThreadAsserterandyatagan.experimental.omitThreadChecks(step 2d)setupReflectionBackend,resetReflectionBackend,useCompiledImplementationIfAvailable(step 2e)DynamicValidationDelegateimplementations (step 2f)- SPI usage:
ValidationPluginProvider,lang-api,core-model-api,core-graph-api,validation-api,validation-spi(step 2g) Yatagan\$in ProGuard/R8 rules,Class.forName, baseline profiles, serialized names, tooling (step 2h)api-common,api-compileddependencies (step 2i)yatagan.experimental.reportDuplicateAliasesAsErrorsprocessor option (step 2j)- Build logic:
buildSrc/build-logic/convention plugins that setyatagan.*processor options, construct generated class names (string concatenation withYatagan), or register bytecode transforms. Build tooling that hard-codes the 1.x naming scheme is the highest-risk find — and it may sit behind a default-off build flag, so a green default build proves nothing about it (see Step 3). Record the names and invocation syntax of any build flags gating Yatagan-related code paths (e.g.-Pflag=true, env vars) — Step 3 needs them to verify coverage.
Step 1 — Bump the version
Set the Yatagan version to 2.0.0 wherever it is declared.
Step 2 — Code migrations
2a. @Provides no longer accepts Conditional arguments
@Provides is a plain marker in 2.0. Move each Conditional(...) argument out into a separate @Conditional(...) annotation on the same method:
Watch for all argument forms: positional @Provides(Conditional(A::class)), multiple @Provides(Conditional(A::class), Conditional(B::class)) (becomes repeated @Conditional annotations), and named/array @Provides(value = [Conditional(A::class)]) (Java uses {...}). Inside @Provides(...) you may also find a Conditionals(...) container argument — handle it here; this is unrelated to the class-level @Conditionals annotation, which is unchanged and must not be touched. Arguments may span multiple lines — rewrite by matching parentheses, not by line.
Formatting after the rewrite — check every rewritten site against the project's per-language line-length limits (.editorconfig limits often differ between Java and Kotlin):
- extraction lengthens lines even without collapsing (the added
@can push a previously-legal line over the limit); - a hoisted multi-line argument was nested one level deeper inside
@Provides(...)— dedent its continuation lines and closing parenthesis by one level.
onlyIn = [...] and repeated @Conditional work the same on methods as on classes — in 2.0 @Conditional is repeatable (@JvmRepeatable(Conditionals::class)) and targets functions and property getters, so the repeated-annotation form is legal in both Kotlin and Java. @Conditional on classes is unchanged — no migration.
2b. Legacy conditions → @ConditionExpression
@Condition, @AllConditions, @AnyCondition, @AnyConditions are deprecated. Replace with @ConditionExpression:
@ConditionExpression supports full boolean expressions — &, |, !, feature references (@OtherFeature), multiple imports with optional importAs aliases — so repeated @Condition/@AnyCondition stacking (CNF) collapses into one expression:
The legacy condition string is already valid @ConditionExpression syntax, including the ! negation prefix and dotted access paths — copy it verbatim; do not re-derive getter/INSTANCE spellings that were already correct in 1.x. (For new expressions: access paths use Java-view names — property getters as getFoo, Kotlin objects via INSTANCE, companions via Companion.)
Reference syntax rules: the short (unqualified) form ("isAliceProEnabled") is legal only when the annotation has exactly one import; with two or more imports every variable must use the qualified form Cls::path (or an importAs alias). Since a legacy @Condition only ever named one class, every mechanically-converted site has exactly one import — prefer the short positional form @ConditionExpression("expr", X::class): it is legal there and length-neutral with the legacy annotation, whereas the qualified form ("X::expr") and the named form (imports = [X::class]) each add characters that can push hundreds of single-line sites over the limit. Decide the form (and check the project's per-language line-length limits, as in 2a) before rewriting a large batch. Non-static providers are resolved from the graph, same as 1.x. @ConditionExpression exists in 1.6.2 with the same syntax, so this step can't break a 1.x build.
2c. Direct injection of child components is forbidden
Injecting a child (non-root) component instance into parent-graph bindings no longer works — it produces Missing binding for ... with the note "A dependency seems to be a child component, try injecting its factory instead."
Triage first — most components need no work. For each non-root component, check whether it declares its own @Component.Builder/@Component.Factory: those are already in the 2.0 shape, skip them. Audit only:
- children without an explicit builder/factory;
- parent-side abstract methods returning a child component type (now factory methods — see below);
- bindings or
@Injectconstructors taking a child component type (includingLazy<Child>/Provider<Child>) as a parameter.
Expect categories 2 and 3 to be dominated by false positives; dismissal rules:
- methods returning
Child.Builder/Child.Factoryare already the 2.0 shape — a builder return type is not a component return type; @BindsInstanceparameters of a child component type are legal (the instance is supplied externally, nothing is missing);@Bindsmethods inside the child's own modules — a component binding itself into its own graph is still legal;- plain non-DI code that merely mentions the type (tests, mocks, utilities).
Keep this audit at grep level — do not deep-inspect thousands of candidates. Step 3's build is the backstop: any missed site fails loudly with Missing binding ... seems to be a child component. For real direct-injection sites, the fix is to inject the child's builder and create the instance lazily:
Also: every abstract method of a component interface that returns a non-root component is now a factory method (in 1.x, parameterless ones were entry points). A child must expose either an explicit @Component.Builder or factory methods in its parent — both together is an error ("Child components can't have a factory methods declared for them in their parents…"). If the child has an explicit builder, remove parent-side factory methods (or drop the builder).
2d. Yatagan.setThreadAsserter() → build-time thread checker
setThreadAsserter(...) is a deprecated (ERROR level) no-op. Replace each asserter with a checker class and a processor option:
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/yatagan-2-migration/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/yandex/yatagan/yatagan-2-migration)