Generated from cat-harness/skills/conduct/conduct-core/communication-language.md — do not edit here.

✎ Edit this page’s source

The language you communicate in

Why this exists

The owner, 2026-09-21: “part of agent first task is to determine appropriate language when speaking/communicating regardless of context”.

Searched before this skill was written: this repository had a great deal about the language its content is in — translation-manager, defaultLocale, per-locale .po resolution, coverage badges — and nothing at all about the language an agent SPEAKS IN.

So the language of every turn was decided by accident. The skills are written in English, the corpus is English, and an agent therefore answered in English without ever having asked whether that was right for the person in front of it. A default masquerading as a decision — the same shape as every other defect this repository names, where the absence of a determination is indistinguishable from a determination that happened to agree.

“Regardless of context” is load-bearing. This is not “detect the language of the request and mirror it”. That answers the easy case and fails silently for the person who writes a terse instruction in one language while wanting the report in another, or who can type a language more easily than they can read a wall of it.

Make the determination before the first substantive turn

Beside the other opening steps — the session-start sweep and opening-brief. The sweep prints the interaction preferences first, before the work plan, because they change the form of every question that follows; this is the same argument one step further.

Four inputs, and the order is not negotiable

rankinputwhere it lives
1the person’s stated preferencecat-harness/memory/interaction.json, per user
2the language of their own turnsthe conversation
3the model’s declared languagesbootstrap/models/models.json
4the instance’s defaultLocaleharness.config.json → translation.defaultLocale

A stated preference wins outright. It is the only input that is a decision rather than a signal, and a person who has said it once must not be asked again — that is WCAG 2.2 SC 3.3.7 (Redundant Entry), and for a user who types with difficulty “just ask again” is not a small cost.

Their own turns rank above the model’s languages, and this is the rule an implementation gets backwards. The owner was explicit: “the model’s languages are an INPUT, never the determination”. A model strong in a language the person cannot read is worse than the fallback, not better. Rank 3 exists to say “this pairing may be better served by something other than the corpus’s language” — it never selects one on its own.

Only human-validated entries are read at all. validatedLanguages() in bootstrap-tools/schemas/model-registry.ts returns nothing for self-reported or unverified, because a model’s own claim about which languages it handles well is a generated assertion about a generated system. self-reported is not a weaker human-validated; it is a different kind of claim.

The boundary: SPEAKING versus WRITING

The rule above governs what you say to the person. It does not govern what you write into the repository.

artefactlanguage
chat replies, questions, status reportsthe determination
commit messages, PR bodies, issue commentsthe instance’s source language
code comments, skills, beans, schemasthe instance’s source language
folio CONTENTtranslation-manager, not this skill

The reason is the reader, not the author. A commit message has reviewers, a future bisector and an agent six months from now; an issue comment has everyone subscribed. Writing those in one person’s preferred language narrows who can read the record to the person who least needed it written down.

This boundary is stated because it will otherwise be read as covering everything, and an agent that translated its commit messages would be obeying the letter of a rule that never meant it.

Record it where a sibling session reads it

cat-harness/memory/interaction.json, in the user’s entry, beside profiles:

"someone@example.org": {
  "profiles": ["low-dexterity"],
  "language": "fr",
  "note": "…",
  "source": "stated by the user"
}

source already carries the declared-versus-assumed distinction, so a language recorded there says both what and on what authority. The session-start sweep prints the whole entry, so the next session reads it rather than re-deriving it.

Never write a preference the person did not state. The file is context — read at session start, never written by a process. An inferred language recorded as though stated is worse than no record: it is a wrong answer wearing the authority of a right one, and the next session will not re-ask.

When to re-ask

  • Never, on a recorded preference, unless the person changes it.
  • When the person’s turns move to another language and no preference is recorded — ask once, offer to record it, and proceed on the observed language if no answer comes.
  • When a determination was never made — the sweep reports that, and it is a finding rather than a silence.

What “could not determine” looks like

defaultLocale, said out loud. An agent that falls through to rank 4 says so once rather than presenting the fallback as a choice — the third-state rule this repository applies everywhere: an unknown rendered as a determination is indistinguishable from a real one.

  • interaction-modality — the FORM a question takes; this is the LANGUAGE it takes. Both are read from the same file.
  • translation-manager — the language of content, which this skill does not govern.
  • bootstrap-tools/schemas/model-registry.ts — why a model’s own word is not evidence.

folio-assistant — code under the Apache License 2.0, documentation under CC BY 3.0. Generated schema and API references are rebuilt from source on every push to main.

deployed b08c961 · 2026-10-23T23:11:09Z