diff --git a/packages/kilo-vscode/tests/unit/agent-manager-i18n.test.ts b/packages/kilo-vscode/tests/unit/agent-manager-i18n.test.ts
index 8edb485de35..691b8a7ab67 100644
--- a/packages/kilo-vscode/tests/unit/agent-manager-i18n.test.ts
+++ b/packages/kilo-vscode/tests/unit/agent-manager-i18n.test.ts
@@ -222,6 +222,105 @@ function findViolations(): Violation[] {
return violations
}
+/**
+ * Detects arrow-function parameters that shadow the `t` i18n function, and
+ * JSX expressions that render `{t}` (the translation function itself) instead
+ * of the intended loop variable.
+ *
+ * Catches bugs like:
+ * .map((token) => {t}) — renders the i18n function, not `token`
+ * .map((t) => {t}) — shadows outer `t`, works by accident
+ */
+interface ShadowViolation {
+ file: string
+ line: number
+ text: string
+ context: string
+}
+
+function findTranslationShadowViolations(): ShadowViolation[] {
+ const project = new Project({
+ compilerOptions: {
+ jsx: 4, // JsxEmit.Preserve
+ jsxImportSource: "solid-js",
+ allowJs: true,
+ strict: false,
+ noEmit: true,
+ },
+ skipAddingFilesFromTsConfig: true,
+ })
+
+ const results: ShadowViolation[] = []
+
+ for (const filePath of TSX_FILES) {
+ const source = project.addSourceFileAtPath(filePath)
+ const basename = path.basename(filePath)
+
+ // Find arrow functions whose parameter list includes `t` AND whose body
+ // contains JSX — this shadows the outer `t` from useLanguage() in a
+ // rendering context, which is almost certainly a bug.
+ // Non-JSX callbacks (e.g. `.map((t) => t.id)`) are harmless data transforms.
+ source.getDescendantsOfKind(SyntaxKind.ArrowFunction).forEach((arrow) => {
+ for (const param of arrow.getParameters()) {
+ if (param.getName() === "t") {
+ const parent = arrow.getParent()
+ if (!parent) continue
+ // Allow top-level destructuring like `const { t } = useLanguage()`
+ if (Node.isVariableDeclaration(parent)) continue
+ // Only flag if the arrow body contains JSX elements
+ const hasJsx =
+ arrow.getDescendantsOfKind(SyntaxKind.JsxElement).length > 0 ||
+ arrow.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement).length > 0
+ if (!hasJsx) continue
+ results.push({
+ file: basename,
+ line: param.getStartLineNumber(),
+ text: param.getText(),
+ context: "arrow parameter shadows i18n t() function in JSX context",
+ })
+ }
+ }
+ })
+
+ // Find .map() calls where the callback has a parameter but JSX inside
+ // references `{t}` instead of the parameter name
+ source.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
+ const expr = call.getExpression()
+ if (!Node.isPropertyAccessExpression(expr)) return
+ if (expr.getName() !== "map") return
+
+ const args = call.getArguments()
+ if (args.length === 0) return
+ const callback = args[0]
+ if (!Node.isArrowFunction(callback)) return
+
+ const params = callback.getParameters()
+ if (params.length === 0) return
+ const paramName = params[0].getName()
+
+ // If the param is already `t`, it's caught by the shadow check above.
+ // Here we check: param is NOT `t`, but JSX inside uses `{t}` — meaning
+ // the callback renders the i18n function instead of the loop variable.
+ if (paramName === "t") return
+
+ callback.getDescendantsOfKind(SyntaxKind.JsxExpression).forEach((jsx) => {
+ const inner = jsx.getExpression()
+ if (!inner) return
+ if (Node.isIdentifier(inner) && inner.getText() === "t") {
+ results.push({
+ file: basename,
+ line: jsx.getStartLineNumber(),
+ text: `{t} inside .map((${paramName}) => ...) — should be {${paramName}}`,
+ context: "JSX renders i18n function instead of map callback parameter",
+ })
+ }
+ })
+ })
+ }
+
+ return results
+}
+
describe("Agent Manager i18n — no hardcoded strings", () => {
const violations = findViolations()
@@ -233,3 +332,15 @@ describe("Agent Manager i18n — no hardcoded strings", () => {
expect(violations).toEqual([])
})
})
+
+describe("Agent Manager i18n — no t() shadowing", () => {
+ const shadows = findTranslationShadowViolations()
+
+ it("should not shadow the t() translation function in callbacks", () => {
+ if (shadows.length > 0) {
+ const report = shadows.map((v) => ` ${v.file}:${v.line} [${v.context}] ${v.text}`).join("\n")
+ expect(shadows, `Found ${shadows.length} t() shadow/misuse(s):\n${report}`).toEqual([])
+ }
+ expect(shadows).toEqual([])
+ })
+})
diff --git a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx
index fd94ba96a83..440db6d1632 100644
--- a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx
+++ b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx
@@ -1100,7 +1100,7 @@ const AgentManagerContent: Component = () => {
{t("agentManager.worktree.new")}
@@ -1109,8 +1109,8 @@ const AgentManagerContent: Component = () => {
{t("agentManager.dialog.advanced")}