Skip to content

Bind the translation figure to its obligations (D272) - #123

Merged
kvlonge merged 5 commits into
mainfrom
feat/d272-obligations
Nov 6, 2026
Merged

kvlonge merged 5 commits into
mainfrom
feat/d272-obligations

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Decision D272 (with D272a/D272b): the translation figure is now bound to the translation obligations. Before this, nothing read coverage/translation/obligations/*.toml.

What changes

  • syntax-coverage results loads every obligation file fail-closed. These are load errors that fail the command:
    • an unknown key
    • a form the catalogue does not hold, or that is absent from its source
    • a file named for a family that isn't the form's
    • an id that isn't <form>.t<NNN>, or that is declared twice
    • a boundary without a known subkind, or a subkind on another kind
    • a case outside cases/translation/<family>/…toml#, a case cited twice, or a case no recorded statement is
    • a required obligation with neither cases nor a non-empty held
    • a blank held or reason; held with cases or on required = false; reason on a required obligation
    • in retired-obligations.toml: a retired id that is reused or retired twice, an entry without a form and reason, or a replaced_by entry that isn't a live obligation
  • An obligation is met only when it cites at least one case and every cited case is matching or matching_error. A cited case file must exist and its construct must be the obligation's form.
  • A form is matching only if, in addition, it declares at least one required obligation and every one is met. Otherwise it gets one of the new states: unmet_obligations, or no_obligations when it declares no required obligation.
  • Held (required = true, cases = [], held = "…") is never met and keeps its form out of matching. Unmeasured (required = false, cases = [], reason = "…") is never met and doesn't block its form.
  • The JSON report gains:
    • obligations (declared, met, unmet, held, unmeasured)
    • obligation_states
    • held and unmeasured (id → reason), as their own buckets
    • uncited_cases: recorded translation cases no obligation cites. These aren't an error, because converter PRs add cases before their obligations exist, and they can't make a form match.
  • The summary prints an obligations line. The baseline records each obligation's state and the retired ids. --against flags met → anything else as a regression, and so is dropping any obligation that retired-obligations.toml doesn't list. A missing obligations/ directory is a load error.
  • Negative tests run through the loader, the measure, the CLI JSON and the diff. Among them: held counted as met, a form with a held obligation counted as matching, required = false without a reason, and an obligation half-met by two cases. That last test fails if "every cited case" is weakened to "any".

Figure (D272b: accepted as honest)

Measured on the landing head: main 9ecf9fe merged in (4c021f8), then the baseline re-recorded (bfecd63). Obligations: 3,407 declared, 1,034 met, 2,354 unmet, 19 held, 0 unmeasured, 0 load errors, 0 uncited cases.

  • before (main 9ecf9fe baseline): 87 of 1,187 translatable forms matching
  • after: 57 matching. 30 forms move to unmet_obligations; none are left at no_obligations.
  • statements: 0 outcome changes (11,870 recorded).

The --against exception list (D272c: auditor-confirmed 30, record 2bd0e02)

translation-results --against main 9ecf9fe reports exactly these 30 form regressions, all Matching → UnmetObligations, and nothing else regresses. The other 3,407 changes are the obligation states the baseline didn't hold before (None → Met/Unmet/Held), plus 24 retired ids. The re-recorded baseline then checks clean: 0 regressions, 0 other changes.

Cause of the unmet case Forms
Refused: CAST('…' AS BINARY), "a cast target type has no name" (12) sf.functions.concat-ws, concat.concat, concat.concat-subscript-dot, left, right, length.len, length.length, lpad, rpad, position.position-in, substr.substr, substr.substring
Refused: the same BINARY cast and declared divergence json_fidelity at PARSE_JSON (7) sf.functions.is-array, is-binary, is-boolean, is-integer, is-object, is-time, is-timestamp.is-timestamp-tz
Refused: unknown Snowflake type DECFLOAT (2) sf.functions.ln, sqrt
Refused: TIMESTAMP_LTZ(9) → DATE has no proven conversion (2) sf.functions.current-date.current-date, current-date.current-date.syntax
Declared divergence json_fidelity at ARRAY_CONSTRUCT (4) sf.functions.array-compact, array-flatten, array-max, array-min
Obligations C landed after the first measure; at least one is unmet (3) sf.functions.nullif (t003 held: it differs from Snowflake, fix on the post-launch list), sf.expressions.data-types-datetime.time, sf.expressions.data-types-logical

What would bring them back, ranked (candidate items)

  1. A receipt-backed VARCHAR → BINARY cast rule (hex decoding), now in BINARY: the type, the VARCHAR→BINARY cast and byte-wise text functions (D284) #129 (approved, queued). This unblocks 19 cases: 12 forms come back outright, and the 7 IS_* forms then need only item 2.
  2. json_fidelity at PARSE_JSON / ARRAY_CONSTRUCT for the shapes these cases use: 11 forms (7 also need item 1).
  3. DECFLOAT as an input type: 2 forms.
  4. TIMESTAMP_LTZ → DATE under a fixed session time zone: 2 forms.

Landing notes

  • C's fix/translation-absent-forms is on main (df143dd), so the loader reports 0 load errors.
  • Gate (D94) on 4c021f8, the exact merge with main 9ecf9fe:
    • make check-merge: exit 0, with the static checks and all 23 test binaries passing.
    • sqlquack verify --require-verified --strict-errors: 11,870 cases, 7,829 passed, 1,032 diverged as expected, 2,972 refused, 37 typed, 0 pending, 0 unproven, 0 failed; 220 of 220 equivalences and rules verified.
    • translation-results --against main: the 30 forms above and nothing else.
  • bfecd63 only re-records coverage/translation/results-baseline.json. No test reads it, and results --against it gives 0 regressions and 0 other changes.
  • Review note (auditor, round 2): retiring an unmet obligation can turn its form matching, so a retirement in retired-obligations.toml needs the same review as deleting the obligation.
  • GitHub Actions is blocked by billing, so the local D94 gate counts.

Load coverage/translation/obligations/<family>.toml fail-closed, with
held and unmeasured obligations and retired-obligations.toml. A form
matches only when every required obligation is met by all its cited
cases; held and unmeasured obligations are never met and are reported
in their own buckets, with the recorded cases no obligation cites.
kvlonge added a commit that referenced this pull request Nov 6, 2026
A cited case file whose construct is another form, or that is missing,
is a load error, and so is a missing obligations directory. A form
declaring no required obligation is no_obligations, never matching. The
baseline records retired ids, and dropping any other obligation from
the head regresses.
…72c)

The 30 auditor-confirmed forms move from matching to unmet_obligations;
the figure is 57 of 1,187 translatable forms. No statement outcome changes.
@kvlonge
kvlonge merged commit bfecd63 into main Nov 6, 2026
@kvlonge
kvlonge deleted the feat/d272-obligations branch November 7, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant