From e95100515eaea144297eb703f4aa8942bf285664 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 21 Oct 2026 12:35:25 +0800 Subject: [PATCH 1/3] =?UTF-8?q?ci(WO71):=20conformance=20matrix=20?= =?UTF-8?q?=E2=80=94=20frozen=20fixtures=20byte-exact=20in=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A conformance-matrix job downloads the frozen vectors + model zips from the models Releases (index sidecar verified) and replays each fixture byte-exactly through the runtime. urd-g2p (split parts) joins via the resolver in a later pass. --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e616ae6..c55f05d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,3 +81,43 @@ jobs: with: name: coverage-report path: coverage/ + + conformance-matrix: + name: Conformance matrix (frozen fixtures) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - name: Download fixtures (GitHub Releases channel) + run: | + mkdir -p fixtures && cd fixtures + BASE="https://github.com/interscript/interscript-models/releases/download" + curl -fsSL -o models-index.yaml "$BASE/index-v12/models-index.yaml" + curl -fsSL -o models-index.yaml.sha256 "$BASE/index-v12/models-index.yaml.sha256" + sha256sum -c models-index.yaml.sha256 + for v in urd-diac-base fas-diac-base urd-g2p-base; do + curl -fsSL -O "$BASE/index-v12/$v-vectors.json" + done + # the two single-zip models; urd-g2p (split parts) resolves + # via the runtime resolver in a later matrix pass + for m in urd-diac-base-1.0 fas-diac-base-1.0; do + url=$(node -e "const y=require('./models-index.yaml')" 2>/dev/null || node -e " + const fs=require('fs');const t=fs.readFileSync('models-index.yaml','utf8'); + const m=t.match(new RegExp('$m:[\\\\s\\\\S]*?url:\\\\s*(\\\\S+)')); + console.log(m[1])") + curl -fsSL -o "$m.zip" "$url" + done + - name: Run the matrix + run: | + for pair in "urd-diac-base-1.0 urd-diac-base" "fas-diac-base-1.0 fas-diac-base"; do + set -- $pair + echo "::group::$1" + SECRYST_E2E_ZIP="$PWD/fixtures/$1.zip" \ + CONFORMANCE_VECTORS="$PWD/fixtures/$2-vectors.json" \ + npx vitest run test/conformance.test.ts + echo "::endgroup::" + done From a9fc1ab8431e28dfe78e3bdd6b2effe8dfd0c27c Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 21 Oct 2026 12:36:36 +0800 Subject: [PATCH 2/3] fix(ci): per-model index url extraction in the conformance matrix --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c55f05d..3517f0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,10 +105,7 @@ jobs: # the two single-zip models; urd-g2p (split parts) resolves # via the runtime resolver in a later matrix pass for m in urd-diac-base-1.0 fas-diac-base-1.0; do - url=$(node -e "const y=require('./models-index.yaml')" 2>/dev/null || node -e " - const fs=require('fs');const t=fs.readFileSync('models-index.yaml','utf8'); - const m=t.match(new RegExp('$m:[\\\\s\\\\S]*?url:\\\\s*(\\\\S+)')); - console.log(m[1])") + url=$(sed -n "/^ $m:/,/^ [a-z]/p" models-index.yaml | grep -m1 'url:' | awk '{print $2}') curl -fsSL -o "$m.zip" "$url" done - name: Run the matrix From 7f85d79d5ec20560f7c3860db5c81e96927fcbf0 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 21 Oct 2026 14:01:20 +0800 Subject: [PATCH 3/3] fix(WO79): single-thread decode sessions (byte-exact contract across environments) --- src/ml/session/onnx-node.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ml/session/onnx-node.ts b/src/ml/session/onnx-node.ts index 08f083f..044568f 100644 --- a/src/ml/session/onnx-node.ts +++ b/src/ml/session/onnx-node.ts @@ -62,7 +62,11 @@ class NodeInferenceSession implements InferenceSession { static async create(modelData: ArrayBuffer | Uint8Array): Promise { const ort = await loadOrt() - const session = await ort.InferenceSession.create(modelData) + // single-thread decode: the conformance contract is byte-exact + // across environments (WO79); multi-thread fp16 ties flip rows + const session = await ort.InferenceSession.create(modelData, { + intraOpNumThreads: 1, + }) return new NodeInferenceSession(session, ort) }