diff --git a/src/content/blog/2026-08-24-phonological-layer-shipped.adoc b/src/content/blog/2026-08-24-phonological-layer-shipped.adoc
index da9a945..7bdc9c3 100644
--- a/src/content/blog/2026-08-24-phonological-layer-shipped.adoc
+++ b/src/content/blog/2026-08-24-phonological-layer-shipped.adoc
@@ -1,4 +1,4 @@
-= The phonological layer: ten models, three wrong turns, and one correction
+= The phonological layer: eleven models, three wrong turns, and one correction
Ronald Tse
v1.0, 2026-08-24
:doctype: book
@@ -13,7 +13,7 @@ haraqat a scribe left out. Turning unwritten Thai into phonemes.
Reading Urdu aloud. Those are learned — and for two years they lived
in a research branch, promising and unshipped.
-This August we shipped the phonological layer: ten neural models under
+This August we shipped the phonological layer: eleven neural models under
the same discipline as the maps — one artifact format, checksums
verified on every load, identical output from Ruby, Python, and
TypeScript, and a measured number next to every claim. This post is
@@ -32,11 +32,13 @@ The catalog now spans four languages and two tiers:
|urd-g2p-1.0 |grapheme→phoneme |CER 14.77 |fp32, 1.3 GiB
|urd-diac-1.0 |diacritization |CER 3.74 |fp32, 1.3 GiB
|heb-diac-1.0 |diacritization |DER 29.0 greedy / 17.5 beam-4 |fp32, parts
-|tha-g2p-base-1.0 |grapheme→phoneme |PER 9.19 (teacher 4.43) |fp32, parts
-|fas-g2p-1.0 |grapheme→phoneme |CER ≈1.6 — above published SOTA on SentenceBench |fp32, parts
+|tha-g2p-base-1.0 |grapheme→phoneme |PER 3.53 greedy (teacher 1.25) |fp32, parts
+|ara-diac-1.0 |diacritization |DER 2.58 — best dedicated on SadeedDiac-25 |fp32, parts
+|fas-g2p-1.0 |grapheme→phoneme |CER ≈1.6 — above the published best on the SentenceBench homograph benchmark |fp32, parts
|tha-g2p-small-1.0 |grapheme→phoneme |PER 2.85 greedy |int8, 246 MiB
|tha-g2p-small-1.0-int4 |same student, 4-bit |byte-identical decode |int4, 193 MiB
|heb-diac-small-1.0 |diacritization |DER 30.37 (teacher 24.79) |fp32, 1.3 GiB
+|ara-diac-small-1.0 |diacritization |DER 8.26 full-set (see update below) |fp32, 1.3 GiB
|===
Every student was distilled from a frozen, independently evaluated
@@ -131,3 +133,31 @@ Try it: `pip install secryst` —
Python, the same in TypeScript. The catalog lives at
https://github.com/interscript/interscript-ml[interscript-ml]; the
measured story behind every number lives in its results log.
+
+== Update, 2026-08-26: two Arabic releases and one subset lesson
+
+The catalog grew to eleven: `ara-diac-1.0` (server) and
+`ara-diac-small-1.0` (client) shipped for Arabic haraqat restoration.
+
+*ara-diac-1.0* is the r6 teacher — and on the full SadeedDiac-25
+benchmark, under the benchmark's own Misraj evaluator protocol, it is
+the best dedicated model measured: 2.5793% DER, ahead of GLM-5.2
+(2.6911), Gemini Flash (3.1926), GPT-4 (3.8645), and Sadeed's own
+1.5B model (7.2915); only Claude 3.7 Sonnet's published 1.3941 sits
+above it. At 580M parameters, runnable locally from a checksummed zip.
+
+*ara-diac-small-1.0* taught us a measurement lesson the hard way. Its
+first published number, 3.66% DER, came from the benchmark's first 300
+paragraphs; the full 1,200-paragraph run scores 8.26%. The teacher
+reproduces its documented value on the same run (2.5815 vs 2.5793), so
+the harness was sound — the subset simply sat in the student's
+training-domain neighborhood and hid a domain-generalization gap. The
+subset figure is withdrawn, the full-set number ships in the metadata,
+and two standing rules follow: student-tier numbers publish from full
+benchmark sets only, and a subset figure is labeled as such at first
+publication.
+
+Also corrected above: the Thai server-tier row now carries the greedy
+numbers (3.53, teacher 1.25) measured under the runtime protocol — the
+decode section earlier in this post explains why the beam-4 figures
+overstated the error.
diff --git a/src/pages/ml.astro b/src/pages/ml.astro
index c1c03fb..99e9c3b 100644
--- a/src/pages/ml.astro
+++ b/src/pages/ml.astro
@@ -38,7 +38,15 @@ const serverModels = [
id: "tha-g2p-base-1.0",
task: "g2p",
pair: "Thai → IPA",
- metric: "PER 9.19 (teacher 4.43)",
+ metric: "PER 3.53 greedy (teacher 1.25)",
+ artifact: "fp32 · 2.6 GiB · parts",
+ status: "released",
+ },
+ {
+ id: "ara-diac-1.0",
+ task: "diacritization",
+ pair: "Arabic → haraqat",
+ metric: "DER 2.58 — best dedicated model on SadeedDiac-25",
artifact: "fp32 · 2.6 GiB · parts",
status: "released",
},
@@ -73,10 +81,18 @@ const clientModels = [
id: "fas-g2p-1.0",
task: "g2p",
pair: "Persian → IPA",
- metric: "CER ≈1.6 · homograph 77.34%",
+ metric: "CER ≈1.6 · SentenceBench homograph 77.34 (published best 76.89)",
artifact: "fp32 · 2.6 GiB · parts",
status: "released",
},
+ {
+ id: "ara-diac-small-1.0",
+ task: "diacritization",
+ pair: "Arabic → haraqat",
+ metric: "DER 8.26 full-set (300-para subset had read 3.66)",
+ artifact: "fp32 · 1.3 GiB",
+ status: "released",
+ },
]
---
@@ -94,12 +110,11 @@ const clientModels = [
Where maps end,
models begin.
- The 289 authority-backed maps cover romanization systems that a - committee has published. Some conversions have no committee: restoring - the haraqat a scribe left out, turning unwritten Thai into phonemes, - reading Urdu aloud. Those are learned — and they ship under the same - discipline as the maps: one artifact format, checksums verified on - every load, byte-identical output from every runtime. + The 289 authority-backed maps cover romanization systems that a committee has published. + Some conversions have no committee: restoring the haraqat a scribe left out, turning + unwritten Thai into phonemes, reading Urdu aloud. Those are learned — and they ship under + the same discipline as the maps: one artifact format, checksums verified on every load, + byte-identical output from every runtime.
@@ -113,12 +128,11 @@ const clientModels = [The catalogue
- No model is published without a measured metric next to its - teacher's, on the same harness, in the open. Students are distilled - from frozen teachers and gated at a pre-agreed error budget. Each - entry resolves from the models.yaml index; artifacts above GitHub's - 2 GiB cap ship as sha256-verified parts that the runtimes - reassemble transparently. + No model is published without a measured metric next to its teacher's, on the same + harness, in the open. Students are distilled from frozen teachers and gated at a + pre-agreed error budget. Each entry resolves from the models.yaml index; artifacts above + GitHub's 2 GiB cap ship as sha256-verified parts that the runtimes reassemble + transparently.
@@ -162,8 +176,11 @@ const clientModels = [ }- “Releasing” = passing its parity gate now, entering the release - pipeline. Distillation budgets and per-model provenance: interscript-ml/docs/RESULTS.md. + “Releasing” = passing its parity gate now, entering the release pipeline. Distillation + budgets and per-model provenance: interscript-ml/docs/RESULTS.md.
@@ -177,12 +194,11 @@ const clientModels = [The contract
- A model is a zip — the Interscript Model Format, IMF v1. Anything - that can read a zip, hash a file, and run two ONNX sessions can - serve it; no Interscript training code required. Every member is - sha256-verified against metadata.yaml on load; tampering raises - loudly. The tokenizer is raw UTF-8 bytes — no vocabulary to - download, no sentencepiece to drift. + A model is a zip — the Interscript Model Format, IMF v1. Anything that can read a zip, + hash a file, and run two ONNX sessions can serve it; no Interscript training code + required. Every member is sha256-verified against metadata.yaml on load; tampering raises + loudly. The tokenizer is raw UTF-8 bytes — no vocabulary to download, no sentencepiece to + drift.
The crystals
- The neural layer is served by secryst — the same - cross-runtime contract as the map layer. Ruby, Python, and - TypeScript resolve a model id against the index, fetch (or reuse - the cache), verify every checksum, and decode with the KV-cache - graph. Golden sets pin the three implementations to each other. + The neural layer is served by secryst — the same cross-runtime contract as the map + layer. Ruby, Python, and TypeScript resolve a model id against the index, fetch (or reuse the + cache), verify every checksum, and decode with the KV-cache graph. Golden sets pin the three + implementations to each other.
require "secryst"
model = Secryst::Model.load("tha-g2p-small-1.0")
model.translate("สวัสดี")
-# => "sa˨˩.wat̚˨˩.diː˧"
+# => "sa˨˩.wat̚˨˩.diː˧"
- Point deployments at a mirror with SECRYST_INDEX; pin the
- cache with SECRYST_CACHE. Both are read at call time in
- all three runtimes.
+ Point deployments at a mirror with SECRYST_INDEX; pin the cache with SECRYST_CACHE. Both are read at call time in all three runtimes.
Provenance
- Teachers are frozen before distillation and never LLM-generated — - language-model teachers hallucinate diacritics. Every number in the - ledger links to a protocol in the open results log. BSD-3-Clause, - code and weights. + Teachers are frozen before distillation and never LLM-generated — language-model teachers + hallucinate diacritics. Every number in the ledger links to a protocol in the open results + log. BSD-3-Clause, code and weights.