{"repo": "biafra23/jako", "files": {"java-to-kotlin/orchestrator/src/main/kotlin/jako/Config.kt": {"8ab47177ba3090f2f435f0e2ddfd61508c791319": {"t": 1785745716.9366114, "n": 7436, "h": "c5b2cbf5418b005c", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val convert: ConvertCfg = ConvertCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 20,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n /** Maximum simultaneous `claude -p` subprocesses Phase 2 will run when\n * refining a batch (SCC or `--only` set). 1 = serial. Going much above\n * 6 risks tripping the Claude subscription per-minute rate limit. */\n val concurrency: Int = 4,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlin :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class ConvertCfg(\n /**\n * If false, leave `*Test.java` files un-converted in\n * `src/jvmTest/java`. Useful as a regression net during the\n * Java→Kotlin migration: the un-touched test suite (still\n * exercising assertions against the original behaviour) keeps\n * working against the converted Kotlin main code, surfacing\n * conversion bugs that would otherwise hide in a all-Kotlin\n * rewrite.\n *\n * Scaffold still moves the test .java into the KMP layout\n * (`src/jvmTest/java`) so the generated `build.gradle.kts`\n * source-set redirect picks them up.\n */\n @SerialName(\"include_tests\") val includeTests: Boolean = true,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n /**\n * If true, skip the gradle compile + refine#2 pass during per-batch\n * conversion and instead run a single final compile + per-file\n * refine#2 across the whole module after every batch has been\n * J2K'd + refine#1'd. Defaults to true because the per-batch\n * compile is doomed while .java files remain in the module —\n * Java references to types whose signatures changed on the .kt\n * side won't resolve until those .java files are also Kotlin.\n * The current flow triple-retries refine#2 on every batch, which\n * burns ~6× the LLM tokens for the same eventual outcome.\n */\n @SerialName(\"deferred_compile\") val deferredCompile: Boolean = true,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}, "a8c30a35920189a5d804ca959719192dc72b350e": {"t": 1785755898.516403, "n": 7436, "h": "c5b2cbf5418b005c", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val convert: ConvertCfg = ConvertCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 20,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n /** Maximum simultaneous `claude -p` subprocesses Phase 2 will run when\n * refining a batch (SCC or `--only` set). 1 = serial. Going much above\n * 6 risks tripping the Claude subscription per-minute rate limit. */\n val concurrency: Int = 4,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlin :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class ConvertCfg(\n /**\n * If false, leave `*Test.java` files un-converted in\n * `src/jvmTest/java`. Useful as a regression net during the\n * Java→Kotlin migration: the un-touched test suite (still\n * exercising assertions against the original behaviour) keeps\n * working against the converted Kotlin main code, surfacing\n * conversion bugs that would otherwise hide in a all-Kotlin\n * rewrite.\n *\n * Scaffold still moves the test .java into the KMP layout\n * (`src/jvmTest/java`) so the generated `build.gradle.kts`\n * source-set redirect picks them up.\n */\n @SerialName(\"include_tests\") val includeTests: Boolean = true,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n /**\n * If true, skip the gradle compile + refine#2 pass during per-batch\n * conversion and instead run a single final compile + per-file\n * refine#2 across the whole module after every batch has been\n * J2K'd + refine#1'd. Defaults to true because the per-batch\n * compile is doomed while .java files remain in the module —\n * Java references to types whose signatures changed on the .kt\n * side won't resolve until those .java files are also Kotlin.\n * The current flow triple-retries refine#2 on every batch, which\n * burns ~6× the LLM tokens for the same eventual outcome.\n */\n @SerialName(\"deferred_compile\") val deferredCompile: Boolean = true,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}, "6b570b2c0960ffae70362c0e6ea69dc0e630b3a4": {"t": 1785755900.1171484, "n": 6694, "h": "bdaef378c605c5f9", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 20,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n /** Maximum simultaneous `claude -p` subprocesses Phase 2 will run when\n * refining a batch (SCC or `--only` set). 1 = serial. Going much above\n * 6 risks tripping the Claude subscription per-minute rate limit. */\n val concurrency: Int = 4,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlin :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n /**\n * If true, skip the gradle compile + refine#2 pass during per-batch\n * conversion and instead run a single final compile + per-file\n * refine#2 across the whole module after every batch has been\n * J2K'd + refine#1'd. Defaults to true because the per-batch\n * compile is doomed while .java files remain in the module —\n * Java references to types whose signatures changed on the .kt\n * side won't resolve until those .java files are also Kotlin.\n * The current flow triple-retries refine#2 on every batch, which\n * burns ~6× the LLM tokens for the same eventual outcome.\n */\n @SerialName(\"deferred_compile\") val deferredCompile: Boolean = true,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}, "b4612d90808303db3517039c3d69054ab63d3bb9": {"t": 1785755901.5682988, "n": 5997, "h": "61109b2685d92027", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 20,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n /** Maximum simultaneous `claude -p` subprocesses Phase 2 will run when\n * refining a batch (SCC or `--only` set). 1 = serial. Going much above\n * 6 risks tripping the Claude subscription per-minute rate limit. */\n val concurrency: Int = 4,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlin :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}, "60e206f8c8a41f769c8850b0e11a6cef8b6808ee": {"t": 1785755902.8952074, "n": 5739, "h": "547471aea27e9099", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 20,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlin :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}, "2df34cd6aec2281d62536d816b716b510bc9deb7": {"t": 1785755904.8923328, "n": 5739, "h": "2c9f477749cb4883", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 10,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlin :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}, "8d2a2844ffab45392582d9c685fe2646ee6ddcef": {"t": 1785755906.4730837, "n": 5738, "h": "9c329dac54d55495", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 3,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlin :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}, "79b438a166f5086d753763dd3e8786ccb913b66e": {"t": 1785755907.8308587, "n": 5741, "h": "4db7c898384cf5ff", "c": "package jako\n\nimport com.charleskorn.kaml.Yaml\nimport com.charleskorn.kaml.YamlConfiguration\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport kotlin.io.path.absolute\n\n/**\n * Top-level YAML config. snake_case in the file, camelCase in Kotlin; the\n * @SerialName annotations bridge the two.\n *\n * `base` is set after loading to the directory the YAML lives in so relative\n * paths inside the file (e.g. `scripts/passthrough-j2k.sh`) resolve against\n * the config's location rather than the JVM's working directory.\n */\n@Serializable\ndata class Config(\n val project: ProjectCfg = ProjectCfg(),\n val claude: ClaudeCfg = ClaudeCfg(),\n val j2k: J2KCfg = J2KCfg(),\n val skills: SkillsCfg = SkillsCfg(),\n val gradle: GradleCfg = GradleCfg(),\n val analysis: AnalysisCfg = AnalysisCfg(),\n val verify: VerifyCfg = VerifyCfg(),\n val git: GitCfg = GitCfg(),\n @SerialName(\"local_model\") val localModel: LocalModelCfg = LocalModelCfg(),\n val fallback: FallbackCfg = FallbackCfg(),\n val state: StateCfg = StateCfg(),\n) {\n @kotlinx.serialization.Transient\n var base: Path = Path.of(\".\").toAbsolutePath().normalize()\n\n fun resolve(raw: String): Path {\n val expanded = if (raw.startsWith(\"~\")) {\n Path.of(System.getProperty(\"user.home\") + raw.removePrefix(\"~\"))\n } else {\n Path.of(raw)\n }\n return if (expanded.isAbsolute()) expanded.normalize()\n else base.resolve(expanded).normalize()\n }\n\n fun stateDir(): Path = resolve(state.dir)\n fun projectRoot(): Path = resolve(project.root)\n fun skillPath(name: String): Path = resolve(skills.root).resolve(name)\n}\n\n@Serializable\ndata class ProjectCfg(\n val root: String = \"\",\n val module: String = \"\",\n)\n\n@Serializable\ndata class ClaudeCfg(\n val cli: String = \"\",\n @SerialName(\"default_model\") val defaultModel: String = \"claude-sonnet-4-6\",\n val models: Map = mapOf(\n \"LOW\" to \"claude-haiku-4-5-20251001\",\n \"MEDIUM\" to \"claude-sonnet-4-6\",\n \"HIGH\" to \"claude-opus-4-7\",\n ),\n @SerialName(\"max_turns\") val maxTurns: Int = 3,\n @SerialName(\"permission_mode\") val permissionMode: String = \"acceptEdits\",\n @SerialName(\"extra_args\") val extraArgs: List = emptyList(),\n @SerialName(\"pause_on_rate_limit\") val pauseOnRateLimit: Boolean = true,\n)\n\n@Serializable\ndata class J2KCfg(\n /** \"external\" | \"headless_idea\" | \"passthrough\". */\n val strategy: String = \"passthrough\",\n val command: String = \"\",\n val args: List = emptyList(),\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class SkillsCfg(\n val root: String = \"vendored-skills/upstream/skills\",\n @SerialName(\"java_to_kotlin\") val javaToKotlin: String = \"kotlin-tooling-java-to-kotlin/SKILL.md\",\n @SerialName(\"agp9_migration\") val agp9Migration: String = \"kotlin-tooling-agp9-migration/SKILL.md\",\n)\n\n@Serializable\ndata class GradleCfg(\n val wrapper: String = \"./gradlew\",\n @SerialName(\"test_command\") val testCommand: String = \":{module}:compileKotlinJvm :{module}:test\",\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n)\n\n@Serializable\ndata class AnalysisCfg(\n val parser: String = \"javaparser\",\n @SerialName(\"separate_tests\") val separateTests: Boolean = true,\n @SerialName(\"reflection_imports\") val reflectionImports: List = listOf(\n \"java.lang.reflect\", \"sun.misc.Unsafe\",\n ),\n @SerialName(\"high_threshold\") val highThreshold: Int = 3,\n @SerialName(\"medium_threshold\") val mediumThreshold: Int = 1,\n)\n\n@Serializable\ndata class VerifyCfg(\n @SerialName(\"max_retries\") val maxRetries: Int = 3,\n)\n\n@Serializable\ndata class GitCfg(\n @SerialName(\"commit_per_file\") val commitPerFile: Boolean = true,\n val author: String = \"\",\n)\n\n/**\n * Local OpenAI-compatible endpoint (LM Studio / Ollama / vLLM / llama.cpp).\n * Used only for LOW-risk files when enabled = true and the reachability\n * probe at phase-2 start succeeds.\n */\n@Serializable\ndata class LocalModelCfg(\n val enabled: Boolean = false,\n @SerialName(\"base_url\") val baseUrl: String = \"http://localhost:1234/v1\",\n val model: String = \"google/gemma-4-31b\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 1800,\n @SerialName(\"probe_timeout_seconds\") val probeTimeoutSeconds: Long = 3,\n)\n\n@Serializable\ndata class DeepSeekCfg(\n @SerialName(\"base_url\") val baseUrl: String = \"https://api.deepseek.com\",\n @SerialName(\"api_key_env\") val apiKeyEnv: String = \"DEEPSEEK_API_KEY\",\n val model: String = \"deepseek-chat\",\n val temperature: Double = 0.1,\n @SerialName(\"max_tokens\") val maxTokens: Int = 16384,\n @SerialName(\"timeout_seconds\") val timeoutSeconds: Long = 600,\n)\n\n@Serializable\ndata class FallbackCfg(\n val enabled: Boolean = false,\n @SerialName(\"return_to_claude_after_window\") val returnToClaudeAfterWindow: Boolean = true,\n val backend: String = \"deepseek\",\n val deepseek: DeepSeekCfg = DeepSeekCfg(),\n)\n\n@Serializable\ndata class StateCfg(\n val dir: String = \"state\",\n)\n\n// ----- loader ------------------------------------------------------------\n\nprivate val yaml = Yaml(\n configuration = YamlConfiguration(\n strictMode = false,\n encodeDefaults = true,\n )\n)\n\nfun loadConfig(path: Path): Config {\n val text = Files.readString(path)\n val cfg = yaml.decodeFromString(Config.serializer(), text)\n cfg.base = path.absolute().parent.normalize()\n return cfg\n}\n"}}, "java-to-kotlin/config.yaml": {"8ab47177ba3090f2f435f0e2ddfd61508c791319": {"t": 1785746847.7699351, "n": 5048, "h": "379158be8514789e", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-haiku-4-5-20251001\n # Risk-tier -> model. Temporarily haiku-everywhere for debugging — the\n # 5-hour subscription window was burning down too fast on Sonnet/Opus.\n # Restore the tiered split (haiku/sonnet/opus by risk) once jako's\n # refine quality is debugged.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-haiku-4-5-20251001\n HIGH: claude-haiku-4-5-20251001\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n # Parallel `claude -p` calls inside a single refine pass. The Claude\n # subscription has a per-minute throughput cap; staying ≤ 6 keeps us\n # well under it. Set to 1 for serial / debugging.\n concurrency: 4\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive a real headless IntelliJ via the :j2k-plugin\n # ApplicationStarter. The orchestrator gets idiomatic\n # Kotlin as J2K's output, so the LLM refine step only\n # needs to polish. The orchestrator batches a whole\n # cycle into a single IDE invocation via the manifest\n # protocol — ~20s of IDE startup amortizes across the\n # batch, sub-second per file thereafter.\n # external: `command` is a fully-formed script that takes\n # (or `--manifest ` if it\n # supports batch mode) and writes the converted Kotlin.\n # strategy: passthrough\n # command: scripts/passthrough-j2k.sh\n # To opt into real J2K via the bundled IntelliJ plugin:\n strategy: headless_idea\n command: scripts/run-j2k-headless.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nconvert:\n # When false, leave *Test.java files unconverted (still as Java under\n # src/jvmTest/java) so the untouched test suite acts as a regression\n # net against bugs the Kotlin conversion might introduce. Set to true\n # for an all-Kotlin module.\n include_tests: false\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: false\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "a8c30a35920189a5d804ca959719192dc72b350e": {"t": 1785755896.4821346, "n": 5048, "h": "379158be8514789e", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-haiku-4-5-20251001\n # Risk-tier -> model. Temporarily haiku-everywhere for debugging — the\n # 5-hour subscription window was burning down too fast on Sonnet/Opus.\n # Restore the tiered split (haiku/sonnet/opus by risk) once jako's\n # refine quality is debugged.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-haiku-4-5-20251001\n HIGH: claude-haiku-4-5-20251001\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n # Parallel `claude -p` calls inside a single refine pass. The Claude\n # subscription has a per-minute throughput cap; staying ≤ 6 keeps us\n # well under it. Set to 1 for serial / debugging.\n concurrency: 4\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive a real headless IntelliJ via the :j2k-plugin\n # ApplicationStarter. The orchestrator gets idiomatic\n # Kotlin as J2K's output, so the LLM refine step only\n # needs to polish. The orchestrator batches a whole\n # cycle into a single IDE invocation via the manifest\n # protocol — ~20s of IDE startup amortizes across the\n # batch, sub-second per file thereafter.\n # external: `command` is a fully-formed script that takes\n # (or `--manifest ` if it\n # supports batch mode) and writes the converted Kotlin.\n # strategy: passthrough\n # command: scripts/passthrough-j2k.sh\n # To opt into real J2K via the bundled IntelliJ plugin:\n strategy: headless_idea\n command: scripts/run-j2k-headless.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nconvert:\n # When false, leave *Test.java files unconverted (still as Java under\n # src/jvmTest/java) so the untouched test suite acts as a regression\n # net against bugs the Kotlin conversion might introduce. Set to true\n # for an all-Kotlin module.\n include_tests: false\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: false\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "6b570b2c0960ffae70362c0e6ea69dc0e630b3a4": {"t": 1785755897.849977, "n": 4770, "h": "f33e895a1925a345", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-haiku-4-5-20251001\n # Risk-tier -> model. Temporarily haiku-everywhere for debugging — the\n # 5-hour subscription window was burning down too fast on Sonnet/Opus.\n # Restore the tiered split (haiku/sonnet/opus by risk) once jako's\n # refine quality is debugged.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-haiku-4-5-20251001\n HIGH: claude-haiku-4-5-20251001\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n # Parallel `claude -p` calls inside a single refine pass. The Claude\n # subscription has a per-minute throughput cap; staying ≤ 6 keeps us\n # well under it. Set to 1 for serial / debugging.\n concurrency: 4\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive a real headless IntelliJ via the :j2k-plugin\n # ApplicationStarter. The orchestrator gets idiomatic\n # Kotlin as J2K's output, so the LLM refine step only\n # needs to polish. The orchestrator batches a whole\n # cycle into a single IDE invocation via the manifest\n # protocol — ~20s of IDE startup amortizes across the\n # batch, sub-second per file thereafter.\n # external: `command` is a fully-formed script that takes\n # (or `--manifest ` if it\n # supports batch mode) and writes the converted Kotlin.\n # strategy: passthrough\n # command: scripts/passthrough-j2k.sh\n # To opt into real J2K via the bundled IntelliJ plugin:\n strategy: headless_idea\n command: scripts/run-j2k-headless.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: false\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "387d0fefc6082ed0315bf1c818483bde0b4ef48e": {"t": 1785755899.3363566, "n": 4613, "h": "df7d3cefc5b3e9ae", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n # Parallel `claude -p` calls inside a single refine pass. The Claude\n # subscription has a per-minute throughput cap; staying ≤ 6 keeps us\n # well under it. Set to 1 for serial / debugging.\n concurrency: 4\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive a real headless IntelliJ via the :j2k-plugin\n # ApplicationStarter. The orchestrator gets idiomatic\n # Kotlin as J2K's output, so the LLM refine step only\n # needs to polish. The orchestrator batches a whole\n # cycle into a single IDE invocation via the manifest\n # protocol — ~20s of IDE startup amortizes across the\n # batch, sub-second per file thereafter.\n # external: `command` is a fully-formed script that takes\n # (or `--manifest ` if it\n # supports batch mode) and writes the converted Kotlin.\n # strategy: passthrough\n # command: scripts/passthrough-j2k.sh\n # To opt into real J2K via the bundled IntelliJ plugin:\n strategy: headless_idea\n command: scripts/run-j2k-headless.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "c96a2851f55f97890d881909bfb760a50a848691": {"t": 1785755901.171492, "n": 4613, "h": "480c532a939599ff", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n # Parallel `claude -p` calls inside a single refine pass. The Claude\n # subscription has a per-minute throughput cap; staying ≤ 6 keeps us\n # well under it. Set to 1 for serial / debugging.\n concurrency: 4\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive a real headless IntelliJ via the :j2k-plugin\n # ApplicationStarter. The orchestrator gets idiomatic\n # Kotlin as J2K's output, so the LLM refine step only\n # needs to polish. The orchestrator batches a whole\n # cycle into a single IDE invocation via the manifest\n # protocol — ~20s of IDE startup amortizes across the\n # batch, sub-second per file thereafter.\n # external: `command` is a fully-formed script that takes\n # (or `--manifest ` if it\n # supports batch mode) and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n # To opt into real J2K via the bundled IntelliJ plugin:\n # strategy: headless_idea\n # command: scripts/run-j2k-headless.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "db81ef861c7b8d97c68f4f266148840acac8105d": {"t": 1785755902.5926347, "n": 4697, "h": "7fca0dc507298c69", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n # Parallel `claude -p` calls inside a single refine pass. The Claude\n # subscription has a per-minute throughput cap; staying ≤ 6 keeps us\n # well under it. Set to 1 for serial / debugging.\n concurrency: 4\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive a real headless IntelliJ via the :j2k-plugin\n # ApplicationStarter. The orchestrator gets idiomatic\n # Kotlin as J2K's output, so the LLM refine step only\n # needs to polish. Trade-off: ~20s of IDE startup per\n # file (a 26-file cycle = ~9 min of pure startup).\n # Worth it when refine quality matters more than wall\n # time. Use `external` + `scripts/run-j2k-headless.sh`\n # below — that wraps the right `./gradlew :j2k-plugin\n # :runIde --args=\"jakoConvert \"` invocation.\n # external: `command` is a fully-formed script that takes\n # and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n # To opt into real J2K via the bundled IntelliJ plugin:\n # strategy: external\n # command: scripts/run-j2k-headless.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "b4612d90808303db3517039c3d69054ab63d3bb9": {"t": 1785755904.3063164, "n": 4107, "h": "bc49313b00170d58", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n # Parallel `claude -p` calls inside a single refine pass. The Claude\n # subscription has a per-minute throughput cap; staying ≤ 6 keeps us\n # well under it. Set to 1 for serial / debugging.\n concurrency: 4\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive idea.sh in headless mode via a custom IntelliJ\n # ApplicationStarter plugin we ship. Requires building\n # that plugin separately.\n # external: `command` is a fully-formed script that takes\n # and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "60e206f8c8a41f769c8850b0e11a6cef8b6808ee": {"t": 1785755906.6258657, "n": 3896, "h": "c6633957fd61461d", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n # On a single leaf file Read .java + Read .kt + Edit .kt is ~3 turns.\n # Inside an SCC the model also reads sibling files in the cycle to\n # understand cross-file types, which can take 8-12 turns alone. 20\n # gives headroom for that plus a few self-verification reads without\n # tripping `error_max_turns`. M3 (6-file Bytes48 cycle) showed 10 was\n # too tight — half the files in the cycle never reached refinement.\n max_turns: 20\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive idea.sh in headless mode via a custom IntelliJ\n # ApplicationStarter plugin we ship. Requires building\n # that plugin separately.\n # external: `command` is a fully-formed script that takes\n # and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "2df34cd6aec2281d62536d816b716b510bc9deb7": {"t": 1785755907.9786837, "n": 3692, "h": "4c2cec47bbc3bb33", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n # Read .java + Read .kt + Edit/Write .kt + (optional) Read-back verify\n # is already 3-4 turns on any non-trivial file. 10 gives headroom for the\n # skill to chain a couple of edits without false `error_max_turns`.\n max_turns: 10\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive idea.sh in headless mode via a custom IntelliJ\n # ApplicationStarter plugin we ship. Requires building\n # that plugin separately.\n # external: `command` is a fully-formed script that takes\n # and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "48013d0fbb95a8e69e8ec65473dd4bd34327e3f0": {"t": 1785755909.2562673, "n": 3472, "h": "ef12c95dd92628a4", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n max_turns: 3\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive idea.sh in headless mode via a custom IntelliJ\n # ApplicationStarter plugin we ship. Requires building\n # that plugin separately.\n # external: `command` is a fully-formed script that takes\n # and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlin :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "e0c43a3d043193904fd7a5266ea27a1817b026fc": {"t": 1785755910.5834603, "n": 3475, "h": "53706a469d881c8a", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n max_turns: 3\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive idea.sh in headless mode via a custom IntelliJ\n # ApplicationStarter plugin we ship. Requires building\n # that plugin separately.\n # external: `command` is a fully-formed script that takes\n # and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlinJvm :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: true\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: true # DEEPSEEK_API_KEY env var must be set\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "79b438a166f5086d753763dd3e8786ccb913b66e": {"t": 1785755911.904361, "n": 3486, "h": "3a5a8aae743621df", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n# YAML uses snake_case; Kotlin maps it to camelCase via @SerialName.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n max_turns: 3\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n\nj2k:\n # Per-file backend chain step 1. See plan §Phase 2.\n # passthrough: copy .java verbatim into the .kt slot; the LLM step does\n # the whole conversion. The fallback when no J2K binary\n # is wired up. The orchestrator treats this as \"J2K not\n # available\" for chain-routing purposes.\n # headless_idea: drive idea.sh in headless mode via a custom IntelliJ\n # ApplicationStarter plugin we ship. Requires building\n # that plugin separately.\n # external: `command` is a fully-formed script that takes\n # and writes the converted Kotlin.\n strategy: passthrough\n command: scripts/passthrough-j2k.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlinJvm :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n parser: javaparser # only javaparser is supported in the Kotlin orchestrator\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# Per-file backend chain step 2: local OpenAI-compatible endpoint\n# (LM Studio / Ollama / vLLM / llama.cpp server). Used ONLY for LOW-risk\n# files when enabled and the reachability probe at phase-2 start passes.\nlocal_model:\n enabled: false\n base_url: http://localhost:1234/v1\n model: google/gemma-4-31b\n # Leave empty for typical local servers; set to env-var name (e.g.\n # \"OPENAI_API_KEY\") if your server requires a bearer token.\n api_key_env: \"\"\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 1800\n probe_timeout_seconds: 3\n\n# Per-file backend chain steps 4 & 5: when claude -p hits its 5-hour window,\n# the orchestrator falls through to DeepSeek (if enabled and credits) or\n# parks until the window resets.\nfallback:\n enabled: false # flip to true once DEEPSEEK_API_KEY is exported\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "6e640d84e110b6301c35f0c856edc0976beb04d5": {"t": 1785755913.1098044, "n": 3006, "h": "6a66a8c9a02079a0", "c": "# Plan 2 — thin orchestrator config.\n# Paths are resolved relative to this file unless absolute. `~` is expanded.\n\nproject:\n # Root of the Gradle/Maven project to convert.\n root: ~/tuweni-gemma4-pipe\n # Gradle subproject to operate on. The KMP scaffold and per-file\n # compile gate both use this module name.\n module: bytes\n\nclaude:\n # `claude` binary. Empty = look on PATH.\n cli: \"\"\n default_model: claude-sonnet-4-6\n # Risk-tier -> model. The skill is identical across tiers; only the model\n # backing the refinement step changes.\n models:\n LOW: claude-haiku-4-5-20251001\n MEDIUM: claude-sonnet-4-6\n HIGH: claude-opus-4-7\n max_turns: 3\n permission_mode: acceptEdits\n extra_args: []\n pause_on_rate_limit: true\n\nj2k:\n # Mechanical first-pass strategy. The orchestrator never inspects what J2K\n # produces — it just feeds the .kt file forward to the refinement step.\n # headless_idea: pass --command to `idea.sh` for headless conversion (Option A)\n # requires a custom IntelliJ plugin that registers an\n # ApplicationStarter; vanilla idea has no built-in J2K command.\n # vscode_engine: shell out to a Node wrapper around j2k-vscode (Option B)\n # j2k-vscode is currently editor-only; needs a fork to expose a CLI.\n # external: `command` is a fully-formed script that takes \n # scripts/passthrough-j2k.sh is a 3-line no-op shim that lets\n # the claude-skill refinement step do the whole conversion.\n strategy: external\n command: scripts/passthrough-j2k.sh\n args: []\n timeout_seconds: 1800\n\nskills:\n # Vendored JetBrains kotlin-agent-skills (git submodule). We never edit the\n # files under here; we just point claude -p at them.\n root: vendored-skills/upstream/skills\n java_to_kotlin: kotlin-tooling-java-to-kotlin/SKILL.md\n agp9_migration: kotlin-tooling-agp9-migration/SKILL.md\n\ngradle:\n wrapper: ./gradlew\n # {module} is templated in at run-time from project.module.\n test_command: \":{module}:compileKotlinJvm :{module}:test\"\n timeout_seconds: 1800\n\nanalysis:\n # javalang = proper AST; regex = no extra dep, less accurate.\n parser: javalang\n separate_tests: true\n # Files importing any of these are risk-bumped.\n reflection_imports:\n - java.lang.reflect\n - sun.misc.Unsafe\n high_threshold: 3\n medium_threshold: 1\n\nverify:\n max_retries: 3\n\ngit:\n commit_per_file: true\n author: \"\"\n\n# When claude -p hits the 5-hour subscription window, fall through to a\n# different API for the rest of that window. The orchestrator remembers when\n# the window resets and resumes claude automatically.\nfallback:\n enabled: false # flip to true once DEEPSEEK_API_KEY is exported\n return_to_claude_after_window: true\n backend: deepseek\n deepseek:\n base_url: https://api.deepseek.com\n api_key_env: DEEPSEEK_API_KEY\n model: deepseek-chat\n temperature: 0.1\n max_tokens: 16384\n timeout_seconds: 600\n\nstate:\n dir: state\n"}, "d8ff3fbafe1f1018d9e31515eaa7e315f4f94d58": {"t": 1785755914.3528345, "n": 2606, "h": "b7818cd872b6ec34", "c": "# Java -> Kotlin conversion pipeline config.\n# Paths are resolved relative to this file unless absolute.\n\nproject:\n # Root containing the Java source tree to convert.\n # For the tuweni smoke test we point at the bytes module.\n source_root: ~/tuweni-gemma4-pipe/bytes/src/main/java\n test_root: ~/tuweni-gemma4-pipe/bytes/src/test/java\n output_root: output\n\nllm:\n base_url: http://localhost:1234/v1\n model_name: google/gemma-4-31b\n temperature: 0.1\n # Hard upper bound on completion tokens per request. Set well below\n # LM Studio's loaded context length (64141 here) so input+output fits.\n # Big translations (Bytes.java is ~19K input tokens, ~17K output) need\n # the headroom. 48000 leaves ~16K for prompt, fine for files up to\n # ~1500 LOC of Java.\n max_tokens: 48000\n # Per-request HTTP/LLM timeout. A full translation of a ~1700-LOC file at\n # ~19 sec/LOC observed throughput (Gemma 31B Q4_K_M on M1, no_think) wants\n # ~55 min, so 3600 gives generous headroom.\n timeout_seconds: 3600\n # Transient-error retry (network/HTTP) — distinct from the semantic retry loop.\n transient_retries: 3\n transient_backoff_seconds: 5\n # Best-effort reasoning suppression. Each backend reads one of these at most;\n # sending all of them lets the OpenAI / vLLM / Qwen-style servers each pick\n # up the knob they honour. The prompt itself also instructs the model to\n # skip deliberation — that's the load-bearing layer.\n reasoning_effort: minimal\n enable_thinking: false\n no_think: true\n\ntranslate:\n # Max number of direct-dependency signature blocks to splice into the prompt.\n max_dependency_signatures: 12\n # Hard truncation on each dep signature blob to keep prompt size bounded.\n max_signature_chars: 4000\n\nverify:\n # per_file = compile each .kt against already-converted deps after translation.\n # per_module = compile the entire output tree once at the end.\n # both = per_file during the run + a final per_module sweep.\n mode: both\n # Bound on semantic retries when kotlinc rejects a file.\n max_retries: 3\n # Extra classpath JARs (absolute paths) used at per_file and per_module\n # compile time. Inline list, merged with any paths from classpath_file.\n extra_classpath: []\n # Optional file holding one absolute JAR path per line. Regenerated by\n # running scripts/resolve-classpath.sh against the source project.\n classpath_file: state/classpath.txt\n\ndiscover:\n # regex (default) or javalang (more accurate, requires `pip install javalang`).\n parser: regex\n # If true, test files are treated as a separate (later) batch.\n separate_tests: true\n\nstate:\n dir: state\n"}, "16ded820b766dbaf694ba7ed409f18d2b676d03a": {"t": 1785755915.691009, "n": 2170, "h": "eb11e00f358ac902", "c": "# Java -> Kotlin conversion pipeline config.\n# Paths are resolved relative to this file unless absolute.\n\nproject:\n # Root containing the Java source tree to convert.\n # For the tuweni smoke test we point at the bytes module.\n source_root: ~/tuweni-gemma4-pipe/bytes/src/main/java\n test_root: ~/tuweni-gemma4-pipe/bytes/src/test/java\n output_root: output\n\nllm:\n base_url: http://localhost:1234/v1\n model_name: google/gemma-4-31b\n temperature: 0.1\n # Cap; translate.py also sizes the request based on the input length.\n max_tokens: 16384\n timeout_seconds: 600\n # Transient-error retry (network/HTTP) — distinct from the semantic retry loop.\n transient_retries: 3\n transient_backoff_seconds: 5\n # Best-effort reasoning suppression. Each backend reads one of these at most;\n # sending all of them lets the OpenAI / vLLM / Qwen-style servers each pick\n # up the knob they honour. The prompt itself also instructs the model to\n # skip deliberation — that's the load-bearing layer.\n reasoning_effort: minimal\n enable_thinking: false\n no_think: true\n\ntranslate:\n # Max number of direct-dependency signature blocks to splice into the prompt.\n max_dependency_signatures: 12\n # Hard truncation on each dep signature blob to keep prompt size bounded.\n max_signature_chars: 4000\n\nverify:\n # per_file = compile each .kt against already-converted deps after translation.\n # per_module = compile the entire output tree once at the end.\n # both = per_file during the run + a final per_module sweep.\n mode: both\n # Bound on semantic retries when kotlinc rejects a file.\n max_retries: 3\n # Extra classpath JARs (absolute paths) used at per_file and per_module\n # compile time. Inline list, merged with any paths from classpath_file.\n extra_classpath: []\n # Optional file holding one absolute JAR path per line. Regenerated by\n # running scripts/resolve-classpath.sh against the source project.\n classpath_file: state/classpath.txt\n\ndiscover:\n # regex (default) or javalang (more accurate, requires `pip install javalang`).\n parser: regex\n # If true, test files are treated as a separate (later) batch.\n separate_tests: true\n\nstate:\n dir: state\n"}, "a7f693d0c9774ba3e4b64ff7bcce7732691ee9d3": {"t": 1785755916.9549055, "n": 1812, "h": "7a12ff71f01129ef", "c": "# Java -> Kotlin conversion pipeline config.\n# Paths are resolved relative to this file unless absolute.\n\nproject:\n # Root containing the Java source tree to convert.\n # For the tuweni smoke test we point at the bytes module.\n source_root: ~/tuweni-gemma4-pipe/bytes/src/main/java\n test_root: ~/tuweni-gemma4-pipe/bytes/src/test/java\n output_root: output\n\nllm:\n base_url: http://localhost:1234/v1\n model_name: google/gemma-4-31b\n temperature: 0.1\n # Cap; translate.py also sizes the request based on the input length.\n max_tokens: 16384\n timeout_seconds: 600\n # Transient-error retry (network/HTTP) — distinct from the semantic retry loop.\n transient_retries: 3\n transient_backoff_seconds: 5\n\ntranslate:\n # Max number of direct-dependency signature blocks to splice into the prompt.\n max_dependency_signatures: 12\n # Hard truncation on each dep signature blob to keep prompt size bounded.\n max_signature_chars: 4000\n\nverify:\n # per_file = compile each .kt against already-converted deps after translation.\n # per_module = compile the entire output tree once at the end.\n # both = per_file during the run + a final per_module sweep.\n mode: both\n # Bound on semantic retries when kotlinc rejects a file.\n max_retries: 3\n # Extra classpath JARs (absolute paths) used at per_file and per_module\n # compile time. Inline list, merged with any paths from classpath_file.\n extra_classpath: []\n # Optional file holding one absolute JAR path per line. Regenerated by\n # running scripts/resolve-classpath.sh against the source project.\n classpath_file: state/classpath.txt\n\ndiscover:\n # regex (default) or javalang (more accurate, requires `pip install javalang`).\n parser: regex\n # If true, test files are treated as a separate (later) batch.\n separate_tests: true\n\nstate:\n dir: state\n"}, "a4c10e0ed89b7cbd50b3a82ce5af843026be20e2": {"t": 1785755918.260649, "n": 1557, "h": "fde47463395d74fc", "c": "# Java -> Kotlin conversion pipeline config.\n# Paths are resolved relative to this file unless absolute.\n\nproject:\n # Root containing the Java source tree to convert.\n # For the tuweni smoke test we point at the bytes module.\n source_root: ~/tuweni-gemma4-pipe/bytes/src/main/java\n test_root: ~/tuweni-gemma4-pipe/bytes/src/test/java\n output_root: output\n\nllm:\n base_url: http://localhost:1234/v1\n model_name: google/gemma-4-31b\n temperature: 0.1\n # Cap; translate.py also sizes the request based on the input length.\n max_tokens: 16384\n timeout_seconds: 600\n # Transient-error retry (network/HTTP) — distinct from the semantic retry loop.\n transient_retries: 3\n transient_backoff_seconds: 5\n\ntranslate:\n # Max number of direct-dependency signature blocks to splice into the prompt.\n max_dependency_signatures: 12\n # Hard truncation on each dep signature blob to keep prompt size bounded.\n max_signature_chars: 4000\n\nverify:\n # per_file = compile each .kt against already-converted deps after translation.\n # per_module = compile the entire output tree once at the end.\n # both = per_file during the run + a final per_module sweep.\n mode: both\n # Bound on semantic retries when kotlinc rejects a file.\n max_retries: 3\n # Extra classpath JARs (absolute paths) used at per_file compile time.\n extra_classpath: []\n\ndiscover:\n # regex (default) or javalang (more accurate, requires `pip install javalang`).\n parser: regex\n # If true, test files are treated as a separate (later) batch.\n separate_tests: true\n\nstate:\n dir: state\n"}, "16a9a7aa185ca967be77cd9d1b8e64b5a70e7cb2": {"t": 1785755919.5011258, "n": 1561, "h": "da03a24d27cf2fca", "c": "# Java -> Kotlin conversion pipeline config.\n# Paths are resolved relative to this file unless absolute.\n\nproject:\n # Root containing the Java source tree to convert.\n # For the tuweni smoke test we point at the bytes module.\n source_root: ~/tuweni-gemma4-pipe/bytes/src/main/java\n test_root: ~/tuweni-gemma4-pipe/bytes/src/test/java\n output_root: output\n\nllm:\n base_url: http://localhost:1234/v1\n model_name: google/gemma-4-26b-a4b\n temperature: 0.1\n # Cap; translate.py also sizes the request based on the input length.\n max_tokens: 16384\n timeout_seconds: 600\n # Transient-error retry (network/HTTP) — distinct from the semantic retry loop.\n transient_retries: 3\n transient_backoff_seconds: 5\n\ntranslate:\n # Max number of direct-dependency signature blocks to splice into the prompt.\n max_dependency_signatures: 12\n # Hard truncation on each dep signature blob to keep prompt size bounded.\n max_signature_chars: 4000\n\nverify:\n # per_file = compile each .kt against already-converted deps after translation.\n # per_module = compile the entire output tree once at the end.\n # both = per_file during the run + a final per_module sweep.\n mode: both\n # Bound on semantic retries when kotlinc rejects a file.\n max_retries: 3\n # Extra classpath JARs (absolute paths) used at per_file compile time.\n extra_classpath: []\n\ndiscover:\n # regex (default) or javalang (more accurate, requires `pip install javalang`).\n parser: regex\n # If true, test files are treated as a separate (later) batch.\n separate_tests: true\n\nstate:\n dir: state\n"}}}}