Skip to main content

Brand voice profile

A voice profile describes how a project's content should sound: its tone, its style rules, the vocabulary it prefers and forbids, and worked examples of the voice being got right and wrong. Brand checks score content against it, and generation steps are steered by it.

It is the one serialization in this family that is YAML rather than JSON.

Implemented in Go at core/brand.

Why YAML

Every other format here is machine-produced — exported from a store, packed by a command, canonicalized for hashing. A voice profile is the opposite: it is authored and edited by a person, and it is mostly prose. Tone descriptions, style rules, and the worked examples are multi-line English text.

YAML carries multi-line prose without escaping it, allows comments explaining why a rule exists, and diffs a reworded paragraph legibly. JSON does none of those well. The type also carries JSON tags, so the same profile serializes to JSON when it moves over an API or into a store — the YAML is the authoring form, not the only form.

Resolution ladder

When a command needs a profile and no --profile, --profile-file, or --pack flag is given, kapi resolves one in this order:

  1. The recipe bindingdefaults.brand_voice in kapi.yaml, which may name a profile_file, a pack, or a profile in the local brand store.
  2. <project-root>/brand.yaml
  3. <project-root>/.kapi/brand.yaml

The binding wins. Rungs 2 and 3 are conventions that make kapi brand check DRAFT.md work flag-free inside a project, not the primary mechanism — a project that has bound a profile never reaches them.

--profile, --profile-file, and --pack are mutually exclusive; passing more than one is an error rather than a silent precedence.

Fields

FieldTypeNotes
namestringRequired.
idstringOptional identity.
descriptionstringOptional.
toneobjectThe tone profile.
styleobjectStyle rules.
vocabularyobjectPreferred, forbidden, and cautioned terms.
examplesarrayWorked examples of the voice.
localesmapPer-locale overrides, keyed by locale id.
channelsmapPer-channel overrides.
personasmapPer-persona overrides.
autonomyobjectHow far automated steps may act on the profile.
min_scoreintThe on-brand bar, 0–100.
version, version_noteint, stringProfile revision and a note describing it.
created_at, updated_at, created_byProvenance.
workspace_idstringSet when the profile came from a workspace.

Overrides

locales, channels, and personas each hold partial overrides layered onto the base profile at resolution time. A profile can therefore say "this is the voice" once and then vary it — terser in a push notification, more formal in de-DE, warmer for a particular persona — without duplicating the whole document per variant.

Resolution applies the requested locale, channel, and persona to produce one effective profile; the file itself stays the single authored source.

The on-brand bar

min_score is the minimum brand-compliance score a block must reach to count as on-brand in roll-ups. When a profile does not set one, the default applies: a bar calibrated so that a single critical vocabulary hit already drops a block below it, while a handful of minor issues does not.

A profile that cannot be read resolves to the default bar, so roll-ups over historical scores stay comparable.

What is deliberately not in it

  • Scores and findings. Those are check results computed against the profile, stored with the content or the run — never written back here.
  • The compiled brand store. brand.db is derived; the profile is the source.
  • Locale-specific content. Overrides adjust voice, not copy. Translated strings belong in the deliverable.

See also

  • Overview — how this fits the wider family.
  • Terms — the other authored, committed artifact.