# Concepts

Targets in the examples are short forms from the SBL Greek New Testament (CC BY-SA 4.0): λόγος, θεός, ἐν ἀρχῇ. The calls are IPA strings. There is no audio in the documented examples.

## Proximity

> Calibrated score after the stress penalty. Unchanged by coaching and heard_as.

An exact phone match with no stress penalty is 1.0 (`exactLogos`). `real_edits` counts phones whose scoring status is `incorrect`, `missing`, or `extra`. It is 0 on that call and 1 when θεός is heard as `s e o s`.

The Python package and the Rust worker agree on these examples within 0.000005. That is the same tolerance the golden tests use.

`acceptable variant` (cost 0.02) is not a real edit. θεός heard as `tʰ e o s` has `phones[0].status` `acceptable variant`, `real_edits` 0, and `proximity` 0.988187.

A stress penalty of 0.10 is subtracted once, after the phone score, when it applies. λόγος heard as `l o g ˈo s` has `proximity` 0.9 and `real_edits` 0. `coaching` and `heard_as` do not change `proximity`. Frame GOP does not change it either. Duration is not in the score.

## Scoring status and learner status

`phones[].status` is the alignment label. The enum is `correct`, `acceptable`, `acceptable variant`, `incorrect`, `missing`, `extra`.

`coaching.sounds[].status` is the learner label:

> exact matches scoring status correct. acceptable_variant matches scoring status "acceptable variant" (the 0.02 taught-versus-historical pairs). close is scoring status acceptable, or an incorrect substitution whose closeness is at least 0.5, or a far substitution whose low_confidence flag is true. far is an incorrect substitution whose closeness is below 0.5 and whose confidence is not low. missing and extra match those scoring statuses.

On `thetaAsS`, the first sound is `close` with `error_code` `dental_fricative_to_alveolar_sibilant` and closeness 0.875. The scoring status of that phone is `incorrect`.

## Feature diffs and error codes

> Stable code for this phone difference. Null when status is exact. A consonant substitution names place and manner. Fricatives s, z, ʃ, and ʒ use sibilant in the code, so θ→s is dental_fricative_to_alveolar_sibilant. Voice, aspiration, and length words appear only when that feature differs. A vowel substitution names height, backness, and rounding on both sides. Missing and extra prefix the same token. Low confidence does not change the code.

> Features that differ, in order: kind, place, manner, voicing, aspiration, rounding, height, backness, length. Empty when status is exact.

Feature names, in order: `kind`, `place`, `manner`, `voicing`, `aspiration`, `rounding`, `height`, `backness`, `length`. The first sound of `thetaAsS` differs in:

```json
[
  {
    "feature": "place",
    "target": "dental",
    "heard": "alveolar"
  }
]
```

`coaching.summary.fixes` lists at most three repairs with `score_gain` above 0. The code on a fix is the same `error_code`. There is no learner sentence in the JSON.

## Stress

The penalty is 0.10 once per attempt. Monosyllables, unaccented words, and words whose only accent is varia are not penalized. A primary `ˈ` in the IPA selects the attempt syllable (`stress.method` `mark`), including a mark on a verse. Secondary `ˌ` is ignored. The phone alignment drops the mark, so `real_edits` does not move.

`stressShift` puts `ˈ` on the second syllable of λόγος. The written accent is the first.

- `stress.method` `mark`
- `stress.penalty` 0.1
- `stress.match` false
- `coaching.stress.error_code` `stress_shift`
- `coaching.stress.target_letters` λό
- `coaching.stress.detected_letters` γος

With no `ˈ` and no audio, `stress.method` is `unavailable` and `match` is null (`exactLogos`). Audio without a mark uses an energy share and a pitch share. A gap under 0.04 abstains (`match` null, penalty 0). A multi-word clip that does not frame-align does not pay 0.10. `match` can still be false when a compared polysyllable disagrees. A single word, and a `ˈ` mark, still pay 0.10.

## heard_as

> posterior when at least one candidate received a CTC forward score from the request posteriors. distance-softmax otherwise. Both paths order candidates with the same softmax. Published confidence is the Platt map, not that mass.

> Softmax temperature used to order candidates. The shipped value is 0.1. Published confidence is a separate Platt map of the top-1 gap, the normalized tutor cost, and a non-word indicator.

Rank order is the temperature-0.1 softmax. Published `confidence` is the Platt map, not that mass. Candidates sum to at most 1. `other` is a candidate kind. A verse puts one ranking in `heard_as.words` per target word. `verseArche` (`ἐν ἀρχῇ`, `e n a r x i`) has proximity 1.0, `real_edits` 0, and 2 word rankings.

`different_word` reports the closest one-edit neighbor. It does not change `proximity`. The examples send `different_word: false`, so `checked` is false.

## Versioning

`/v1` plus `api: tutor-v1` is the score contract. Adding a field is not a break. Removing or renaming one would be `/v2`. Error bodies changed from plain text to `application/problem+json` without a new path; `detail` still carries the old sentence. See [Errors](/docs/errors.md).
