diff --git a/apps/website/i18n/ai-numbers.ru.json b/apps/website/i18n/ai-numbers.ru.json index 6d7bc3a3d2..0fb842ef13 100644 --- a/apps/website/i18n/ai-numbers.ru.json +++ b/apps/website/i18n/ai-numbers.ru.json @@ -151,55 +151,55 @@ "a-sign-is-one-bit": { "TITLE": "Знак — это один бит", "GOAL": "Как умножение чисел со знаком ставит знак произведения и какой тест замечает, когда знак не ставится никогда.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Модули с 1 по 4 были о том, как хранить числа: ячейка T27, байт масштаба OCP MX, тернарные веса с масштабом фи и TNF; этот модуль заставляет их считать. gft_smul.t27 использует биты tnf17.t27: 1.0 — это 20480, а -1.0 — это 86016, те же биты плюс бит знака 65536. smul ставит знак, когда знаки входов различаются, а величину отдаёт magmul. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись делает знак всегда равным 0, и падает ровно один тест, m2, который умножает 1.0 на -1.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "Модули с 1 по 4 были о том, как хранить числа: ячейка T27, байт масштаба OCP MX, тернарные веса с масштабом фи и TNF; этот модуль заставляет их считать. gft_smul.t27 использует биты tnf17.t27: 1.0 — это 20480, а -1.0 — это 86016, те же биты плюс бит знака 65536. smul ставит знак, когда знаки входов различаются, а величину отдаёт magmul. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который делает знак всегда равным 0, и снова t27c test-report: падает ровно один тест, m2, который умножает 1.0 на -1.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке m1 и m3 и поймите, почему знак, который всегда равен 0, не может их сломать." }, "opposite-signs-subtract": { "TITLE": "Разные знаки — вычитание", "GOAL": "Как сложение чисел со знаком превращается в вычитание, когда знаки разные, и что его 3 теста оставляют непроверенным.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. sadd в gft_sadd.t27 складывает два числа одного знака через magadd. Когда знаки разные, она вычитает меньшую величину из большей через magsub, берёт знак большего и при точном сокращении возвращает 0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Знаки смешивает только a2, 1.0 плюс -1.0, и они сокращаются: magsub сразу возвращает 0, потому что величины равны. Ни один тест не вычитает две разные величины, поэтому знак большего ни разу не решает результат. Запись складывает величины в той ветке, куда попадает a2, и падает ровно один тест, a2. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "sadd в gft_sadd.t27 складывает два числа одного знака через magadd. Когда знаки разные, она вычитает меньшую величину из большей через magsub, берёт знак большего и при точном сокращении возвращает 0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Знаки смешивает только a2, 1.0 плюс -1.0, и они сокращаются: magsub сразу возвращает 0, потому что величины равны. Ни один тест не вычитает две разные величины, поэтому знак большего ни разу не решает результат. Запись запускает t27c test-report, затем однострочный sed, который складывает величины в той ветке, куда попадает a2, и снова t27c test-report: падает ровно один тест, a2. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и значение, которого ждёт a2; затем найдите в спеке bsign и проверьте, зависит ли от него хоть один из a1, a2 и a3." }, "multiply-then-add": { "TITLE": "Умножить, потом сложить", "GOAL": "Что умножение с накоплением делает с двумя произведениями со знаком и как его ломает замена из урока 12.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Нейрон суммирует произведения весов на входы, и on_comb в gft_signed_mac.t27 — такая сумма из двух слагаемых: smul(a1, b1) плюс smul(a2, b2). Здесь smul ставит знак через XOR и не проверяет ноль. Нативный t27c запускает 4 теста, и все проходят; один из них, zero_clause, ждёт, что 0 x 1 + 0 x 1 даст 512, то есть 2^-39, а не 0, и комментарий называет это задуманным поведением. Браузер пропускает свои тесты, потому что его исполнитель пока не знает assert_eq. Запись повторяет ошибку урока 12 и меняет XOR на OR. Падает ровно один тест, pp: (-1.0) x (-1.0) теперь выходит отрицательным, и сумма равна 0 вместо 2.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "Нейрон суммирует произведения весов на входы, и on_comb в gft_signed_mac.t27 — такая сумма из двух слагаемых: smul(a1, b1) плюс smul(a2, b2). Здесь smul ставит знак через XOR и не проверяет ноль. Нативный t27c запускает 4 теста, и все проходят; один из них, zero_clause, ждёт, что 0 x 1 + 0 x 1 даст 512, то есть 2^-39, а не 0, и комментарий называет это задуманным поведением. Браузер пропускает свои тесты, потому что его исполнитель пока не знает assert_eq. Запись запускает t27c test-report, затем повторяет ошибку урока 12 однострочным sed, который меняет XOR на OR, и снова запускает t27c test-report. Падает ровно один тест, pp: (-1.0) x (-1.0) теперь выходит отрицательным, и сумма равна 0 вместо 2.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи тест, который ждёт 512, и тест, который ломает подложенная ошибка; затем сами замените в спеке XOR в smul на OR и посмотрите, поймает ли это браузер." }, "a-bend-at-zero": { "TITLE": "Излом в нуле", "GOAL": "Почему ReLU нужен тест с отрицательным входом, прежде чем можно сказать, что у неё есть излом.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Нейрон пропускает свою сумму через функцию активации, и в gft_relu.t27 это relu(x) = max(0, x). on_comb возвращает 0 для нуля и для любого входа с битом знака, а иначе сам x. Заголовок говорит, что у ReLU точный градиент 0/1 и что 2-слойная сеть с ReLU решает XOR, а линейная модель GF-T этого не может. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись пропускает отрицательные входы, и relu становится тождеством, прямой линией. Падает ровно один тест, negz; больше ни у одного теста нет отрицательного входа. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "Нейрон пропускает свою сумму через функцию активации, и в gft_relu.t27 это relu(x) = max(0, x). on_comb возвращает 0 для нуля и для любого входа с битом знака, а иначе сам x. Заголовок говорит, что у ReLU точный градиент 0/1 и что 2-слойная сеть с ReLU решает XOR, а линейная модель GF-T этого не может. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который пропускает отрицательные входы, и relu становится тождеством, прямой линией, и снова t27c test-report. Падает ровно один тест, negz; больше ни у одного теста нет отрицательного входа. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и значение, которого ждёт negz; затем найдите в спеке комментарий на этой строке и проверьте, описывает ли он код после замены." }, "two-to-the-x": { "TITLE": "Два в степени x", "GOAL": "Как exp2 делит x на целую часть и дробь и почему её тесты доходят только до целой части.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Заголовок gft_exp2.t27 называет exp2 недостающим кирпичиком для softmax на GF-T. on_comb делит x на целую часть k и дробь f: k задаёт смещение, k + 40, а pow2_frac превращает f в мантиссу. Заголовок обещает ошибку не больше 1 ULP, измеренную в прототипе, но все 4 теста подают целые входы, 0, 1.0, -1.0 и 2.0, поэтому f равна 0, и pow2_frac возвращает 0 при любых коэффициентах. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись теряет минус у k для отрицательного x, и падает ровно один тест, em1: 2^-1 выходит как 2.0, а не 0.5. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "Заголовок gft_exp2.t27 называет exp2 недостающим кирпичиком для softmax на GF-T. on_comb делит x на целую часть k и дробь f: k задаёт смещение, k + 40, а pow2_frac превращает f в мантиссу. Заголовок обещает ошибку не больше 1 ULP, измеренную в прототипе, но все 4 теста подают целые входы, 0, 1.0, -1.0 и 2.0, поэтому f равна 0, и pow2_frac возвращает 0 при любых коэффициентах. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который теряет минус у k для отрицательного x, и снова t27c test-report: падает ровно один тест, em1: 2^-1 выходит как 2.0, а не 0.5. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и значение, которого ждёт em1; затем найдите в спеке pow2_frac и выясните, что она возвращает, когда f равна 0." }, "pick-the-largest": { "TITLE": "Выбрать наибольшее", "GOAL": "Как argmax называет класс, сравнивая одни только биты, и какое правило решает ничью.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Классификатор в конце называет класс с наибольшей оценкой. gft_argmax4.t27 делает это без арифметики: gt ставит отрицательные ниже нуля, ноль ниже положительных, а затем сравнивает младшие 16 бит, растущие с величиной. По заголовку ничью выигрывает меньший индекс, сравнение строгое >. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись меняет > на >= для двух положительных оценок, и падает ровно один тест, tie_low: четыре оценки 1.0 теперь выбирают индекс 3, а не 0. Ни один тест не даёт решить ответ двум отрицательным оценкам: переворот их сравнения ничего не роняет. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "Классификатор в конце называет класс с наибольшей оценкой. gft_argmax4.t27 делает это без арифметики: gt ставит отрицательные ниже нуля, ноль ниже положительных, а затем сравнивает младшие 16 бит, растущие с величиной. По заголовку ничью выигрывает меньший индекс, сравнение строгое >. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который меняет > на >= для двух положительных оценок, и снова t27c test-report: падает ровно один тест, tie_low: четыре оценки 1.0 теперь выбирают индекс 3, а не 0. Ни один тест не даёт решить ответ двум отрицательным оценкам: переворот их сравнения ничего не роняет. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке строку, которая сравнивает две отрицательные оценки, и тот один тест, который до неё доходит." }, "a-loss-in-bits": { "TITLE": "Потеря в битах", "GOAL": "Как потеря -log2(p) оценивает догадку в битах и почему безошибочная догадка должна стоить ровно 0.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Обучение начинается с числа, которое говорит, насколько неверной была догадка. gft_nll.t27 берёт p, вероятность, которую модель дала верному классу, и возвращает -log2(p): p = 1.0 стоит 0, p = 0.5 стоит 1 бит, а p = 0.25 стоит 2. on_comb — это neg(log2v(p)), и neg переворачивает бит знака, но для 0 возвращает 0. Одна строка заголовка всё ещё говорит, что на выходе log2(x); код возвращает его с обратным знаком. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись заставляет neg возвращать 65536 для 0, ноль с поставленным битом знака, и падает ровно один тест, perfect. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "Обучение начинается с числа, которое говорит, насколько неверной была догадка. gft_nll.t27 берёт p, вероятность, которую модель дала верному классу, и возвращает -log2(p): p = 1.0 стоит 0, p = 0.5 стоит 1 бит, а p = 0.25 стоит 2. on_comb — это neg(log2v(p)), и neg переворачивает бит знака, но для 0 возвращает 0. Одна строка заголовка всё ещё говорит, что на выходе log2(x); код возвращает его с обратным знаком. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который заставляет neg возвращать 65536 для 0, ноль с поставленным битом знака, и снова t27c test-report: падает ровно один тест, perfect. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке строку заголовка, где сказано, что на выходе log2(x), и сравните её с on_comb." }, "one-step-downhill": { "TITLE": "Один шаг вниз по склону", "GOAL": "Как один шаг SGD сдвигает вес против градиента и почему тест с двумя равными входами не может их различить.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. gft_sgd_step.t27 сдвигает вес против градиента: w' = w - eta*g, где eta — скорость обучения. Её 3 теста: step, где w = 1.0, g = 0.5 и eta = 0.5; nograd, где g = 0 оставляет w как есть; и ascend, где g = -1.0 и eta = 1.0 поднимают w до 2.0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись умножает g на само себя вместо eta. В step g равно eta, а в nograd оба произведения равны 0. Поэтому падает ровно один тест, ascend, где вес падает до 0, вместо того чтобы подняться до 2.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "gft_sgd_step.t27 сдвигает вес против градиента: w' = w - eta*g, где eta — скорость обучения. Её 3 теста: step, где w = 1.0, g = 0.5 и eta = 0.5; nograd, где g = 0 оставляет w как есть; и ascend, где g = -1.0 и eta = 1.0 поднимают w до 2.0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который умножает g на само себя вместо eta, и снова t27c test-report. В step g равно eta, а в nograd оба произведения равны 0. Поэтому падает ровно один тест, ascend, где вес падает до 0, вместо того чтобы подняться до 2.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке step и решите, какой из его входов нужно изменить, чтобы он поймал ошибку." }, "xor-needs-a-bend": { "TITLE": "XOR нужен излом", "GOAL": "Почему сети нужен скрытый слой с ReLU, чтобы вычислить XOR, и что делает один пропавший излом.", - "TEXT": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. XOR равен 1, когда ровно один вход равен 1, и комментарий в gft_xornet.t27 говорит, что одиночная линейная модель не может его разделить. on_comb — 2-слойная сеть: две скрытые суммы, z0 и z1, каждая через relu, затем взвешенная сумма h0 и h1. Она не обучается: тесты подают веса, заданные вручную, аналитическое решение W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись убирает relu только у h1, и падает ровно один тест, x00: в (0,0) z1 равно -1.0, и выход становится 2.0 вместо 0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TEXT": "XOR равен 1, когда ровно один вход равен 1, и комментарий в gft_xornet.t27 говорит, что одиночная линейная модель не может его разделить. on_comb — 2-слойная сеть: две скрытые суммы, z0 и z1, каждая через relu, затем взвешенная сумма h0 и h1. Она не обучается: тесты подают веса, заданные вручную, аналитическое решение W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который убирает relu только у h1, и снова t27c test-report: падает ровно один тест, x00: в (0,0) z1 равно -1.0, и выход становится 2.0 вместо 0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "TASK": "Найдите в записи изменённую строку и упавший тест; затем вычислите в спеке z1 для x10, x01 и x11 и поймите, почему relu там ничего не меняет." }, "back-to-three-values": { diff --git a/apps/website/package.json b/apps/website/package.json index d11920a760..c65fffed69 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -121,7 +121,7 @@ "check:widgets": "node scripts/widgets-from-spec.mjs --check", "test:widgets-spec": "node --test scripts/widgets-from-spec.test.mjs", "check:course": "node scripts/course-from-spec.mjs --check", - "test:course": "node --test scripts/course-from-spec.test.mjs scripts/lesson-specs.test.mjs", + "test:course": "node --test scripts/course-from-spec.test.mjs scripts/lesson-specs.test.mjs scripts/t27c-mutation-casts.test.mjs", "gen:course-pages": "node scripts/course-pages.mjs", "check:course-pages": "node scripts/course-pages.mjs --check && node --test scripts/course-pages.test.mjs", "check:widget-pages": "node scripts/widget-pages-from-spec.mjs --check", diff --git a/apps/website/public/learn/a-bend-at-zero/card.png b/apps/website/public/learn/a-bend-at-zero/card.png index 36e4d723c2..140ebeb1c1 100644 Binary files a/apps/website/public/learn/a-bend-at-zero/card.png and b/apps/website/public/learn/a-bend-at-zero/card.png differ diff --git a/apps/website/public/learn/a-bend-at-zero/index.html b/apps/website/public/learn/a-bend-at-zero/index.html index 5c5099d1ec..3cbdbe9ad4 100644 --- a/apps/website/public/learn/a-bend-at-zero/index.html +++ b/apps/website/public/learn/a-bend-at-zero/index.html @@ -5,7 +5,7 @@ A bend at zero: AI numbers course, lesson 16 of 27 - + @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -60,11 +60,11 @@

A bend at zero

You will learn

Why a ReLU needs a test with a negative input before anyone can say it bends.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording lets negative inputs through, which turns relu into the identity, a straight line. Exactly one test fails, negz; no other test has a negative input. Every byte in the recording was printed by the command; only the typing is staged.

+

A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that lets negative inputs through, which turns relu into the identity, a straight line, and t27c test-report again. Exactly one test fails, negz; no other test has a negative input. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the value negz expects; then in the spec frame find the comment on that line and check whether it still describes the code after the change.

Open the interactive lesson →

-
gft_relu.t27: recording pending
gft_relu.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_relu.t27 -- a bend at zero, native
t27c on gft_relu.t27 -- a bend at zero, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; letting negative inputs through makes exactly one test fail, negz; git restores the spec.

specs/ternary/gft_relu.t27

module GftRelu;
 // #1764 + GF-T: a GF-T ReLU activation -- relu(x) = max(0, x). For a signed GF-T16
 // input, returns x if x > 0 else 0 (raw 0). Unlike the sign/trit quantizer, ReLU
diff --git a/apps/website/public/learn/a-loss-in-bits/card.png b/apps/website/public/learn/a-loss-in-bits/card.png
index 380684a03b..5a2c44f317 100644
Binary files a/apps/website/public/learn/a-loss-in-bits/card.png and b/apps/website/public/learn/a-loss-in-bits/card.png differ
diff --git a/apps/website/public/learn/a-loss-in-bits/index.html b/apps/website/public/learn/a-loss-in-bits/index.html
index 58013b88b6..38763cc920 100644
--- a/apps/website/public/learn/a-loss-in-bits/index.html
+++ b/apps/website/public/learn/a-loss-in-bits/index.html
@@ -5,7 +5,7 @@
 
 
 A loss in bits: AI numbers course, lesson 19 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

A loss in bits

You will learn

How the loss -log2(p) prices a guess in bits, and why a perfect guess must cost exactly 0.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes neg return 65536 for 0, a zero with the sign bit set, and exactly one test fails, perfect. Every byte in the recording was printed by the command; only the typing is staged.

+

Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes neg return 65536 for 0, a zero with the sign bit set, and t27c test-report again: exactly one test fails, perfect. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the test that fails; then in the spec frame find the header line that says the output is log2(x) and compare it with on_comb.

Open the interactive lesson →

-
gft_nll.t27: recording pending
gft_nll.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_nll.t27 -- a loss in bits, native
t27c on gft_nll.t27 -- a loss in bits, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; a zero with the sign bit set from neg makes exactly one test fail, perfect; git restores the spec.

specs/ternary/gft_nll.t27

module GftNll;
 // #1764 + GF-T: GF-T negative-log-likelihood (cross-entropy loss for a one-hot
 // label) -- given the softmax probability p of the true class, returns the loss
diff --git a/apps/website/public/learn/a-sign-is-one-bit/card.png b/apps/website/public/learn/a-sign-is-one-bit/card.png
index 17eff989ff..4f9b321e0d 100644
Binary files a/apps/website/public/learn/a-sign-is-one-bit/card.png and b/apps/website/public/learn/a-sign-is-one-bit/card.png differ
diff --git a/apps/website/public/learn/a-sign-is-one-bit/index.html b/apps/website/public/learn/a-sign-is-one-bit/index.html
index e12bfd18a1..f2575a1ea7 100644
--- a/apps/website/public/learn/a-sign-is-one-bit/index.html
+++ b/apps/website/public/learn/a-sign-is-one-bit/index.html
@@ -5,7 +5,7 @@
 
 
 A sign is one bit: AI numbers course, lesson 13 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

A sign is one bit

You will learn

How a signed multiply sets the sign of its product, and which test notices when it never does.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes the sign always 0, and exactly one test fails, m2, which multiplies 1.0 by -1.0. Every byte in the recording was printed by the command; only the typing is staged.

+

Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes the sign always 0, and t27c test-report again: exactly one test fails, m2, which multiplies 1.0 by -1.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the test that fails; then in the spec frame find m1 and m3 and work out why a sign that is always 0 cannot break them.

Open the interactive lesson →

-
gft_smul.t27: recording pending
gft_smul.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_smul.t27 -- a sign is one bit, native
t27c on gft_smul.t27 -- a sign is one bit, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; a sign that is always 0 makes exactly one test fail, m2; git restores the spec.

specs/ternary/gft_smul.t27

module GftSmul;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/learn/ai-numbers.t27 b/apps/website/public/learn/ai-numbers.t27
index d613543b84..660c63e9f0 100644
--- a/apps/website/public/learn/ai-numbers.t27
+++ b/apps/website/public/learn/ai-numbers.t27
@@ -243,15 +243,15 @@ pub const LESSON_TEXTS : [27]str = [
     "In the browser, setting MAX_TRITS to 3 made two checks fail. The native t27c is stricter: the 8 invariants of golden_sieve.t27 are proved while it compiles, so with a third trit the spec does not compile at all. The report says BLOCKED, and explains that a blocked spec never produced a binary, so it has no test results to count. A rule checked while compiling cannot ship broken. Then the recording plants a different bug: the rule against shift-register cells now always says yes, and exactly one test fails, the one that checks the formula. Every byte in the recording was printed by the command; only the typing is staged.",
     "The name TNF covers two different things. In the sieve, TNF(k, b) is an alphabet of weights with 3 or 9 levels. TNF17e in tnf17.t27 is a Ternary Network Float, which the spec calls the signed accumulator format: a sign bit, a 7-bit offset and a 9-bit mantissa, worth (-1)^s x (1 + m/512) x 2^(offset - 40). Its trits are in the exponent, not in the weights: the offset takes 81 values, exactly 3^4, so the exponent is four balanced trits, from -40 to +40. Four trits cost 7 bits on a binary chip, so the rung is 17 bits, not 16; the spec records that its source counted positions, and a trit is not a bit. The browser runs 20 of its 44 checks, all 10 invariants among them, and names the cast behind each skip.",
     "The browser skipped 24 checks of tnf17.t27; the native t27c runs all 34 tests, all pass, and none is vacuous. Negation flips the sign bit with XOR. The recording swaps XOR for OR, a one-character bug. Negating a positive number still looks right, so the test that negates one still passes. But negating a negative number now leaves it negative, so negating twice no longer gives the number back. Exactly one test fails, negate_is_an_involution, the one that states that property. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes the sign always 0, and exactly one test fails, m2, which multiplies 1.0 by -1.0. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording adds the sizes in the branch a2 takes, and exactly one test fails, a2. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording repeats the bug of lesson 12 and swaps XOR for OR. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording lets negative inputs through, which turns relu into the identity, a straight line. Exactly one test fails, negz; no other test has a negative input. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording drops the minus sign of k for negative x, and exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording turns > into >= for two positive scores, and exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes neg return 65536 for 0, a zero with the sign bit set, and exactly one test fails, perfect. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording multiplies g by itself instead of by eta. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. Every byte in the recording was printed by the command; only the typing is staged.",
-    "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording removes relu from h1 alone, and exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. Every byte in the recording was printed by the command; only the typing is staged.",
+    "Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes the sign always 0, and t27c test-report again: exactly one test fails, m2, which multiplies 1.0 by -1.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording runs t27c test-report, then a one-line sed that adds the sizes in the branch a2 takes, and t27c test-report again: exactly one test fails, a2. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording runs t27c test-report, then repeats the bug of lesson 12 with a one-line sed that swaps XOR for OR, and runs t27c test-report again. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that lets negative inputs through, which turns relu into the identity, a straight line, and t27c test-report again. Exactly one test fails, negz; no other test has a negative input. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that drops the minus sign of k for negative x, and t27c test-report again: exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that turns > into >= for two positive scores, and t27c test-report again: exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes neg return 65536 for 0, a zero with the sign bit set, and t27c test-report again: exactly one test fails, perfect. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that multiplies g by itself instead of by eta, and t27c test-report again. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
+    "XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that removes relu from h1 alone, and t27c test-report again: exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.",
     "A neuron in a ternary network adds up products of trits, and the sum can be any small integer. quantize in activation_quantizer.t27 squeezes it back: above the threshold it returns 2 (P), below minus the threshold 0 (N), and in between 1 (Z). The on_comb at the end makes it combinational: no register, the output follows the inputs. The browser skips all 7 tests because its runner does not know assert_eq yet; the native t27c runs all 7, all pass, none vacuous. The recording changes > to >= on line 6, so a sum equal to the threshold already counts as P. Exactly one test fails, quantize_boundary_hi, which asks quantize(10, 10) for Z. Every byte in the recording was printed by the command; only the typing is staged.",
     "maj3 in bitnet_majority.t27 packs three trits with pack3, takes dot27 against w_all_p, an all-P weight chunk, and quantizes at 0: the result is the sign of a + b + c. weighted_vote is the same neuron with weights pack3(2, 2, 0) and gives the sign of a + b - c, so the weights pick the function. The browser skips all 12 tests because it does not know assert_eq yet; the native t27c runs all 12, all pass, none vacuous. The recording lowers w_all_p by 16, which turns the weight on lane 2 from P to Z, so maj3 no longer sees c. Exactly one test fails, maj_p_n_n: +1 - 1 - 1 is N, but +1 - 1 is Z. Note that wv_p_z_p passes 0, which is N, as its middle input. Every byte in the recording was printed by the command; only the typing is staged.",
     "One packed chunk holds 27 trits, so a wider neuron needs several. neuronN in bitnet_neuron_nchunk.t27 takes up to 8 chunks of activations and weights, adds dot27 for nchunks pairs starting at chunk 0, and quantizes the total. The browser skips all 11 tests because it does not know assert_eq yet; the native t27c runs all 11, all pass, none vacuous. The recording changes the loop bound on line 36 from nchunks to 1, so the neuron always reads exactly one chunk. The other neuron tests fill every chunk alike, so one chunk lands on the same side of the threshold. Exactly one test fails, neuron_zero_chunks, which asks for no chunks and expects Z. Every byte in the recording was printed by the command; only the typing is staged.",
diff --git a/apps/website/public/learn/cards.json b/apps/website/public/learn/cards.json
index 907ba08596..0ceaef967f 100644
--- a/apps/website/public/learn/cards.json
+++ b/apps/website/public/learn/cards.json
@@ -1,14 +1,14 @@
 {
   "learn/": "4d54cc27d150bfa3cdffeb226bb8ab2ca123ce9e53e07b6f413789a1cc1b870b",
-  "learn/a-bend-at-zero/": "eb98e73ff0f2b18072c4f296a986c738f3aaa0c31f4e4d6d07cae7d8b138826a",
+  "learn/a-bend-at-zero/": "304f35f236649e02ffee68b93083557cc71795ffe38d65e4b618a5cd49d143f4",
   "learn/a-build-receipt/": "a9f6033644a5c80be2d2502b7dd787b44179c68d6a8782d2f072b778a619b3d3",
   "learn/a-dot-product-in-wires/": "b8c1bed3ff617584cb030d8d6aa8d07b05e283a9c73593e004cbec1279ec328b",
   "learn/a-float-cut-by-phi/": "9b6abd1d9c6730834e421ab2f561895d32fd8ff4bf0639230475658158ab5e0b",
-  "learn/a-loss-in-bits/": "edeadb44a1c720c96fb3e2d8609d8b070eeb679d240958bf27500f5176985a13",
+  "learn/a-loss-in-bits/": "a421c6068ae82704dfd29716373b66c06ea0e02a17506714cfaf5f8fa53a37ba",
   "learn/a-model-on-the-board/": "b9365e72b52c80e6aa910619129ab8c27e852be816b72502e6601176e62a6b83",
   "learn/a-neuron-in-chunks/": "a80bbd72c568fc1e67ec83194c971d0f826431931e6d5921f19856db016c43c9",
   "learn/a-pass-that-checked-nothing/": "ac621c2b8313caf55b562752930fea20b0b93f031e36c3ca808e118befd0ded7",
-  "learn/a-sign-is-one-bit/": "7d0ca9d90ab58002cbdb67dc5f793a6e459ab272c5f157b7c20409872036a9a7",
+  "learn/a-sign-is-one-bit/": "8a5aed2915cf9bb4b3e08c0409bbb3188f11be9c612cef3611dcf1cde46771c0",
   "learn/a-small-program/": "fba5eaeea36f43fc0a72cfe956a83e332e3317820097b101e4f2fa4a3d019421",
   "learn/a-test-block/": "aa77c8603e32ff654062e3c958d97c3be30a28db32cdf02ad06aa4633f25e7b8",
   "learn/a-whole-network/": "71a17cd60850a0f6e776c56fbc7e0d0fadf84cd19ba2f6fdb2b94cbe97d43159",
@@ -114,20 +114,20 @@
   "learn/modes-cpol-cpha/": "df02f5c2ebe60c79ddf540d17d8367be1ef7ffb16da9c916da0e9e0dd1a126b6",
   "learn/mtbf/": "4c378b8ae7169558433f861d673fa25eb5697f5668651159202958ad6b1ebfce",
   "learn/multiply-and-divide/": "04f563d0b073aa75285382837ffaa8bdccce23df956d9d61002e2691224f302c",
-  "learn/multiply-then-add/": "005b1da7bce17da578fa69fb62a65844aae8031cc2d8dfdeb872eb1e332401e5",
+  "learn/multiply-then-add/": "d6a0407858df178e9de0ccc24cded3864ac19f8aaa796466913a9f3713bf3fc7",
   "learn/negate-twice/": "d8723c28c60ec0e1b4bdc5255af07d23204acea7a126caa544c51aabc7153fed",
   "learn/one-edge-one-world/": "5c3e2b05491bd8ac0f6f829324c830f0c3e7c44f7ba83af1a85b25645a3bc395",
   "learn/one-lut-up-close/": "166f3d723e87272cc8dfa905eec2ff86dc16c2fdef28c6c15988a6925aa6bd65",
   "learn/one-outlier/": "21a1cab7337d9d7c8481a6b31ea0b3e1dc3647bb8e16589f6f5b7a3c1eb291af",
   "learn/one-scale-per-block/": "6f19f66e43e7e7bfefe1a05b99032ae1a4877ff74b3ee78c56fb3ad61eb6c327",
-  "learn/one-step-downhill/": "c394f83b3228ef8ad59c32b1016b3aaf0c8722bb723290bda62e008dd7119788",
-  "learn/opposite-signs-subtract/": "b8117891c96918764fd41ba48f2c39d0032c2e395032600d1baab4a69b7b6f86",
+  "learn/one-step-downhill/": "060b45a1ababf4d56f640e60d600dd955edf6559f8f8459891ecd604c513c837",
+  "learn/opposite-signs-subtract/": "2a067cd8add2ffd486c3bceca9dde5718d559ea05e2c98da89d272afba4746b4",
   "learn/period-and-jitter/": "4417739c2a8be60099196d5d8f21fe087d3f4db4a343d57b244a299494219179",
   "learn/phase-and-lock/": "b50b9c08c4a92c2ca46cbed1fa61ac9b517c45473a33c096e17133dd77801d9e",
   "learn/phi-as-a-ratio/": "68c557efb1a4c013635cad8a456cce50b90b906499bbf88f59039ee69d295038",
   "learn/phi-in-sixteen-bits/": "31fbbaef3eb5aaac576d993deafc778d20e97ed4f91e5130911bcc9c8bccca70",
   "learn/phi-scaled-ternary/": "a3bf7f88ac1c6355c89235335a2f8ff612ea5cdcfb6730916e850d34208e5fd6",
-  "learn/pick-the-largest/": "50638ab5fba8fecc83eef2ab4292f8ffd37c13f6f292d00ce2b158d24bbe0cc3",
+  "learn/pick-the-largest/": "cedc447727491e93c5498dce28c87b2ca22e4362509b62406354c3ee47fd03ab",
   "learn/pins-and-package/": "bfeb94d1228d99c22404d480d73f94b9cfb713a1f26a516d796a5dea0722ed15",
   "learn/placement/": "3972b02ae2342edf11920f424b75019f139becb43e64604a11165436bfb299a1",
   "learn/plan-before-code/": "bce366c9c328881943876a5f16ce2c963ad75807fb2e6218ec14e4898448af0f",
@@ -174,7 +174,7 @@
   "learn/true-false-and-text/": "a8302d71500c7264aa2cfab2823eb764d736c49726f7299c30bc9b5111f8785f",
   "learn/two-flop-sync/": "c3959e859f480b7370878aff377418e9722b32ea22965529830aeb6ad0a4f08d",
   "learn/two-products-one-sum/": "d389ecc3cde02d8a6887bf66ad877de2d2b5633ad3bc8a247c24c40d3a54beb5",
-  "learn/two-to-the-x/": "21739447e84d7a1d7e0f570a086890a2f9a1ba634bd0a9b61fce17105db85830",
+  "learn/two-to-the-x/": "8d0e428d1ff3a86ea4454b634a7f4f8ec7ec4208e52a92ad9ed90c3ab2803826",
   "learn/use-other-modules/": "f000786d3f6d44427693951e07545fb95ba3c7c7a0875f85cabd38e03f15112f",
   "learn/vcd-format/": "e640528793896dcabc21c5e6856989f05a84254f525461c1e9dfa121d128245d",
   "learn/verify-capstone/": "4d0587c2f051a226ad0df1cb03ad31fbe62cfe7f8b52df977a3262f75fba7ab6",
@@ -194,17 +194,17 @@
   "learn/why-bridges/": "41956d0a134a5aac06e05ff0220a0184a79d0e93cbd379327e7c89adfb5ddc79",
   "learn/why-the-split-is-phi/": "4d6b42f4ced79749e9f9f3ba64ee4e45d2c02339c3ab8fbc09a60640ce26afc9",
   "learn/wires-that-talk/": "169a2014a79be767f24fb172298d6e5e172fc5d1b54d79d0475f40be43b16eff",
-  "learn/xor-needs-a-bend/": "0be69f7a7ec16c326fc28f311ad8f69b734d2595f6069d46212594af295d0100",
+  "learn/xor-needs-a-bend/": "968d0f74575763cb546da521465ba8193f4b93fd473df08293f571e9601fdd79",
   "ru/learn/": "a9995b9648745b8be6063618ec0103bea67dafbb850ce40d7ac87fab1f9f1d91",
-  "ru/learn/a-bend-at-zero/": "a926c3947964ffa944998b51c438722a2d9b5f5092e420b357a6e91981864399",
+  "ru/learn/a-bend-at-zero/": "cfd85d458fd00c986dbd0b1dfbb87f9d78b4692f9ff69e672e2f6fc5e4c2bd6e",
   "ru/learn/a-build-receipt/": "5fd31459c1a61efbb7e893de7ed3826d0baeb3e2a2ca83bf0e07acb912e3939d",
   "ru/learn/a-dot-product-in-wires/": "e623d894b5e9d556b714ea7067c6a322de2d8f70d5cfcba9b135fe7e2e9254b8",
   "ru/learn/a-float-cut-by-phi/": "be7a9f649e648df28ab9a6c98f86ecccff7f66cecd2b2776d20853fd7f89629f",
-  "ru/learn/a-loss-in-bits/": "d008609de3024e826343f60b63051b46ed5c453a335a59949bbd7d0745a21c6b",
+  "ru/learn/a-loss-in-bits/": "4b064c3293a3be7767f59dfeba99ba2d5ffb8be53383291eb53cbd7cbb5efa80",
   "ru/learn/a-model-on-the-board/": "2285956627292be6c86d370ebee1ad16a5b07f62c8ebead82f96f490bd6b5bf9",
   "ru/learn/a-neuron-in-chunks/": "57e4dcdd5f09686a9cc7e2c19abbedc295dd5298e18073757e50a26abbbda461",
   "ru/learn/a-pass-that-checked-nothing/": "8ebe0c47613887dd80aa89f9727970c5953d360be26b70a0a166f7e8d2ae2df9",
-  "ru/learn/a-sign-is-one-bit/": "abf7126fc18168f54f49f8736e5f70be74bd36ae3f4bd0be1d414d0320c12622",
+  "ru/learn/a-sign-is-one-bit/": "aab0eefed96d2317d7a7cfb804a9d94fbd601946b1ce699f9cbdd601d26bfe51",
   "ru/learn/a-small-program/": "ad8c3ecea45c6f884776eab43bfd8669f3d7e50daadc2df7877aeec24d6d5f00",
   "ru/learn/a-test-block/": "e53ab31255bb2e1cccbc26aa35fadc89871c6cfd80caf5b1351237872beebb46",
   "ru/learn/a-whole-network/": "bf956bff7c76cb556bd5b072ae7f3187384f2fd0cbdc10d68373f0da7fc0251d",
@@ -310,20 +310,20 @@
   "ru/learn/modes-cpol-cpha/": "96d94cd7df315dec06357d08bf4f7f20022c0cee90d670e534beafe6793bd951",
   "ru/learn/mtbf/": "fa0c7a2797083eb6f7b44dcd1ea89b3ae560c111201ddb0376f899c235e3d2a6",
   "ru/learn/multiply-and-divide/": "9e5f639a87b6c2a3ffddc453c984a95292ac30ce64bdc9f5b55d5952629c15eb",
-  "ru/learn/multiply-then-add/": "e573a15bffdbbc84af7023a50f37afb0e78a6b35bdb8b7e4ce32ca87eb89c034",
+  "ru/learn/multiply-then-add/": "61ebf2be71bb9bc7b1391a9ce5f410442dd84eaeb529d588a35ca51501a9edd2",
   "ru/learn/negate-twice/": "392a6c948a48c4ab72b2cf30d4e1378a95408671c288cdfd3cc4e76569a7ad6a",
   "ru/learn/one-edge-one-world/": "2259c1b021214040d6e338b1b806a2cd6220c651a45b8d2154afa534a31f478a",
   "ru/learn/one-lut-up-close/": "3f41d0df7387a37b49b04b2f443fa6777ed1bf02a72efab59ad537a31aa215f8",
   "ru/learn/one-outlier/": "c34f1bb2662cd52c783d6eaa53842331d3c24f6e6de7a2fb3f296650d4e0c463",
   "ru/learn/one-scale-per-block/": "3996dff8150a65875d22620cba95a70a8e5e29a4c2714cfaf5f370723018bdd5",
-  "ru/learn/one-step-downhill/": "64b9dd62605f3e72f1f6c72fc703b3b36f9238e93df451648b472c0fe9903e01",
-  "ru/learn/opposite-signs-subtract/": "724e6dfd05271f44b32686b191b7d8824862c021a3a98a91e10d079b23ddcd28",
+  "ru/learn/one-step-downhill/": "c2172182b46bb262978253fb942075fb0a35ef152f88112228957720efc4de27",
+  "ru/learn/opposite-signs-subtract/": "0a9a4ec69219b10e26620e9ef4c9f91834367ab7d65ab8fda5584880bb018622",
   "ru/learn/period-and-jitter/": "c8024fbd2b6eab7df162567b25b854639105d6fd3b23e3212238d7cb19d524cd",
   "ru/learn/phase-and-lock/": "7220b431dc84c1b629636b5bc75c28741fcab3ce516061899b15133ec07cbfa7",
   "ru/learn/phi-as-a-ratio/": "b4c3927b1141b34bda4f5adc58dfdf8ab1705bca1426c266af2536bdfdcc7bb0",
   "ru/learn/phi-in-sixteen-bits/": "4bdc58cca5e2fa256bec375f95252ae1aecf49387cde4cdeb65c680f8a21d4ff",
   "ru/learn/phi-scaled-ternary/": "5e657743e5e2441b0444f0015584607e16ffcd74db3d3782c6bde6c2f041c152",
-  "ru/learn/pick-the-largest/": "a6ac78270829b8898c801a56853ee765751cf856323eb1b72fd7a992d2f24fb8",
+  "ru/learn/pick-the-largest/": "c04f38c0674bfb82f71ff50c82d2069aaeb894b9358b2ffa20953ccd23c1b653",
   "ru/learn/pins-and-package/": "8d21987accdd7b73e6191ad0fc79f1e0be360c52cbb87643bf16e7ce1fcdf0ec",
   "ru/learn/placement/": "6bab37025edd2ceefa352f6f12a9210299a524631a4ba503ff6b7d6d361fe15a",
   "ru/learn/plan-before-code/": "5471156e96fe33dceee1f58afc070d35672753cc6ef6a7c4e4da32128466d3f7",
@@ -370,7 +370,7 @@
   "ru/learn/true-false-and-text/": "1db693d3f318c2ae7568a64dc6ac764df5293b889f5122e5dd95990feb462dbf",
   "ru/learn/two-flop-sync/": "3214814cce95801bd40f6f68fe332b3a845c84380e7bcfbdf4181d406590b3db",
   "ru/learn/two-products-one-sum/": "5465012f589e86d9dfc6cb60643d9a8e3a89fc46c82115a498c4c6c8bbe712fb",
-  "ru/learn/two-to-the-x/": "feda4ba3bf8a0fc5aaa8e6f862142419dea95fa4459e0dd3cc8604823ac2eff4",
+  "ru/learn/two-to-the-x/": "6fa8f70d2d1af14819ceebee762c54bace827811abae6bbebb1a859d6e7373dc",
   "ru/learn/use-other-modules/": "7b7f6702b3597d4db99470a5eb972211a3c0bf9b83f119ba4d5353c9ae5a4e24",
   "ru/learn/vcd-format/": "3b21beb6ef6375a085efdcbbbc3213da73553b0eacd6cb681825522c23915a74",
   "ru/learn/verify-capstone/": "aecde067fcf05643180842bce0c48918db42c873012b033837bd09d3d4e3ce2f",
@@ -390,5 +390,5 @@
   "ru/learn/why-bridges/": "7f3e478b6bf74ecacaa52ffbe3c939d0ae13d9bd44e8487c730624bad5a4e220",
   "ru/learn/why-the-split-is-phi/": "4b89cf7b7bc60e2762b6868b3970e7e7f11fd3f9e920ee32a49ecbdacf8e3ed1",
   "ru/learn/wires-that-talk/": "e0a2a425148743a892f4a72c8a43bc0f0a6664c66477748665792cca177a1747",
-  "ru/learn/xor-needs-a-bend/": "017e687787c78180e91adc53c519ed96ef082eb5266df07a882bdfb2ebfbc97f"
+  "ru/learn/xor-needs-a-bend/": "bb476d0525fe3b56d81ba82df2e04f8d9d6699700b5062d1760786f2154bcfc8"
 }
diff --git a/apps/website/public/learn/multiply-then-add/card.png b/apps/website/public/learn/multiply-then-add/card.png
index 9a1aa9eaa0..b1f96a238c 100644
Binary files a/apps/website/public/learn/multiply-then-add/card.png and b/apps/website/public/learn/multiply-then-add/card.png differ
diff --git a/apps/website/public/learn/multiply-then-add/index.html b/apps/website/public/learn/multiply-then-add/index.html
index 5874d2a7fc..1abb44203e 100644
--- a/apps/website/public/learn/multiply-then-add/index.html
+++ b/apps/website/public/learn/multiply-then-add/index.html
@@ -5,7 +5,7 @@
 
 
 Multiply, then add: AI numbers course, lesson 15 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Multiply, then add

You will learn

What a multiply-accumulate does with two signed products, and how the swap from lesson 12 breaks it.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording repeats the bug of lesson 12 and swaps XOR for OR. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. Every byte in the recording was printed by the command; only the typing is staged.

+

A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording runs t27c test-report, then repeats the bug of lesson 12 with a one-line sed that swaps XOR for OR, and runs t27c test-report again. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the test that expects 512 and the test the planted bug breaks; then in the spec frame change the XOR in smul to OR yourself and see whether the browser catches it.

Open the interactive lesson →

-
gft_signed_mac.t27: recording pending
gft_signed_mac.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_signed_mac.t27 -- multiply, then add, native
t27c on gft_signed_mac.t27 -- multiply, then add, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; OR in place of the sign XOR makes exactly one test fail, pp; git restores the spec.

specs/ternary/gft_signed_mac.t27

module GftSignedMac;
 // #1764 + GF-T: a spec-first SIGNED GF-T16 MAC (y = a1*b1 + a2*b2) with
 // round-to-nearest-even, bit-exact to the ideal oracle gft16_ref.py (with sign).
diff --git a/apps/website/public/learn/one-step-downhill/card.png b/apps/website/public/learn/one-step-downhill/card.png
index 4f719c7d01..bdd9ca7933 100644
Binary files a/apps/website/public/learn/one-step-downhill/card.png and b/apps/website/public/learn/one-step-downhill/card.png differ
diff --git a/apps/website/public/learn/one-step-downhill/index.html b/apps/website/public/learn/one-step-downhill/index.html
index 010ce8f24a..e8f39b62c8 100644
--- a/apps/website/public/learn/one-step-downhill/index.html
+++ b/apps/website/public/learn/one-step-downhill/index.html
@@ -5,7 +5,7 @@
 
 
 One step downhill: AI numbers course, lesson 20 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

One step downhill

You will learn

How one SGD step moves a weight against its gradient, and why a test with two equal inputs cannot tell them apart.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording multiplies g by itself instead of by eta. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. Every byte in the recording was printed by the command; only the typing is staged.

+

gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that multiplies g by itself instead of by eta, and t27c test-report again. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the test that fails; then in the spec frame find step and decide which of its inputs must change for it to catch the bug.

Open the interactive lesson →

-
gft_sgd_step.t27: recording pending
gft_sgd_step.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_sgd_step.t27 -- one step downhill, native
t27c on gft_sgd_step.t27 -- one step downhill, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; g times g in place of eta times g makes exactly one test fail, ascend; git restores the spec.

specs/ternary/gft_sgd_step.t27

module GftSgdStep;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/learn/opposite-signs-subtract/card.png b/apps/website/public/learn/opposite-signs-subtract/card.png
index 70298efa93..af4f6af355 100644
Binary files a/apps/website/public/learn/opposite-signs-subtract/card.png and b/apps/website/public/learn/opposite-signs-subtract/card.png differ
diff --git a/apps/website/public/learn/opposite-signs-subtract/index.html b/apps/website/public/learn/opposite-signs-subtract/index.html
index d123470a19..b05f6f8e01 100644
--- a/apps/website/public/learn/opposite-signs-subtract/index.html
+++ b/apps/website/public/learn/opposite-signs-subtract/index.html
@@ -5,7 +5,7 @@
 
 
 Opposite signs subtract: AI numbers course, lesson 14 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Opposite signs subtract

You will learn

How a signed add turns into a subtraction when the signs differ, and what its 3 tests leave unchecked.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording adds the sizes in the branch a2 takes, and exactly one test fails, a2. Every byte in the recording was printed by the command; only the typing is staged.

+

sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording runs t27c test-report, then a one-line sed that adds the sizes in the branch a2 takes, and t27c test-report again: exactly one test fails, a2. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the value a2 expects; then in the spec frame find bsign and check whether a1, a2 or a3 ever depends on it.

Open the interactive lesson →

-
gft_sadd.t27: recording pending
gft_sadd.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_sadd.t27 -- opposite signs subtract, native
t27c on gft_sadd.t27 -- opposite signs subtract, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; adding the sizes where opposite signs must subtract makes exactly one test fail, a2; git restores the spec.

specs/ternary/gft_sadd.t27

module GftSadd;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/learn/pick-the-largest/card.png b/apps/website/public/learn/pick-the-largest/card.png
index 9cfe4308af..6744791f0b 100644
Binary files a/apps/website/public/learn/pick-the-largest/card.png and b/apps/website/public/learn/pick-the-largest/card.png differ
diff --git a/apps/website/public/learn/pick-the-largest/index.html b/apps/website/public/learn/pick-the-largest/index.html
index 029d27989d..baaa5505cd 100644
--- a/apps/website/public/learn/pick-the-largest/index.html
+++ b/apps/website/public/learn/pick-the-largest/index.html
@@ -5,7 +5,7 @@
 
 
 Pick the largest: AI numbers course, lesson 18 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Pick the largest

You will learn

How argmax names a class by comparing bits alone, and which rule settles a tie.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording turns > into >= for two positive scores, and exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. Every byte in the recording was printed by the command; only the typing is staged.

+

A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that turns > into >= for two positive scores, and t27c test-report again: exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the test that fails; then in the spec frame find the line that compares two negative scores and the one test that reaches it.

Open the interactive lesson →

-
gft_argmax4.t27: recording pending
gft_argmax4.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_argmax4.t27 -- pick the largest, native
t27c on gft_argmax4.t27 -- pick the largest, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; >= in place of > for two positive scores makes exactly one test fail, tie_low; git restores the spec.

specs/ternary/gft_argmax4.t27

module GftArgmax4;
 // #1764 + GF-T: a classification head over four signed GF-T16 logits. Returns the
 // INDEX {0,1,2,3} of the maximum real value (lowest index wins ties, strict >).
diff --git a/apps/website/public/learn/two-to-the-x/card.png b/apps/website/public/learn/two-to-the-x/card.png
index 6d514c2d5a..2250f92aee 100644
Binary files a/apps/website/public/learn/two-to-the-x/card.png and b/apps/website/public/learn/two-to-the-x/card.png differ
diff --git a/apps/website/public/learn/two-to-the-x/index.html b/apps/website/public/learn/two-to-the-x/index.html
index 8903a69cb9..2018d1c684 100644
--- a/apps/website/public/learn/two-to-the-x/index.html
+++ b/apps/website/public/learn/two-to-the-x/index.html
@@ -5,7 +5,7 @@
 
 
 Two to the x: AI numbers course, lesson 17 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Two to the x

You will learn

How exp2 splits x into a whole part and a fraction, and why its tests only reach the whole part.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording drops the minus sign of k for negative x, and exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. Every byte in the recording was printed by the command; only the typing is staged.

+

The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that drops the minus sign of k for negative x, and t27c test-report again: exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the value em1 expects; then in the spec frame find pow2_frac and work out what it returns when f is 0.

Open the interactive lesson →

-
gft_exp2.t27: recording pending
gft_exp2.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_exp2.t27 -- two to the x, native
t27c on gft_exp2.t27 -- two to the x, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; dropping the minus sign of k makes exactly one test fail, em1; git restores the spec.

specs/ternary/gft_exp2.t27

module GftExp2;
 // #1764 + GF-T: a GF-T exp2 primitive -- 2^x for a signed GF-T16 input x, result
 // a positive GF-T16. This is the missing building block for a GF-T softmax
diff --git a/apps/website/public/learn/xor-needs-a-bend/card.png b/apps/website/public/learn/xor-needs-a-bend/card.png
index fbb9e6131a..193f9edea7 100644
Binary files a/apps/website/public/learn/xor-needs-a-bend/card.png and b/apps/website/public/learn/xor-needs-a-bend/card.png differ
diff --git a/apps/website/public/learn/xor-needs-a-bend/index.html b/apps/website/public/learn/xor-needs-a-bend/index.html
index f0f8740b4b..6203d60b7f 100644
--- a/apps/website/public/learn/xor-needs-a-bend/index.html
+++ b/apps/website/public/learn/xor-needs-a-bend/index.html
@@ -5,7 +5,7 @@
 
 
 XOR needs a bend: AI numbers course, lesson 21 of 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

XOR needs a bend

You will learn

Why a network needs a hidden ReLU layer to compute XOR, and what one missing bend does.

-

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording removes relu from h1 alone, and exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. Every byte in the recording was printed by the command; only the typing is staged.

+

XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that removes relu from h1 alone, and t27c test-report again: exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the test that fails; then in the spec frame work out z1 for x10, x01 and x11 and see why relu changes nothing there.

Open the interactive lesson →

-
gft_xornet.t27: recording pending
gft_xornet.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_xornet.t27 -- XOR needs a bend, native
t27c on gft_xornet.t27 -- XOR needs a bend, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; removing relu from h1 makes exactly one test fail, x00; git restores the spec.

specs/ternary/gft_xornet.t27

module GftXorNet;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/ru/learn/a-bend-at-zero/card.png b/apps/website/public/ru/learn/a-bend-at-zero/card.png
index 0cc9237c1e..20af6faa55 100644
Binary files a/apps/website/public/ru/learn/a-bend-at-zero/card.png and b/apps/website/public/ru/learn/a-bend-at-zero/card.png differ
diff --git a/apps/website/public/ru/learn/a-bend-at-zero/index.html b/apps/website/public/ru/learn/a-bend-at-zero/index.html
index a1ddf05a41..b3bd55e25a 100644
--- a/apps/website/public/ru/learn/a-bend-at-zero/index.html
+++ b/apps/website/public/ru/learn/a-bend-at-zero/index.html
@@ -5,7 +5,7 @@
 
 
 Излом в нуле: курс «ИИ-числа», урок 16 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Излом в нуле

Вы узнаете

Почему ReLU нужен тест с отрицательным входом, прежде чем можно сказать, что у неё есть излом.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Нейрон пропускает свою сумму через функцию активации, и в gft_relu.t27 это relu(x) = max(0, x). on_comb возвращает 0 для нуля и для любого входа с битом знака, а иначе сам x. Заголовок говорит, что у ReLU точный градиент 0/1 и что 2-слойная сеть с ReLU решает XOR, а линейная модель GF-T этого не может. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись пропускает отрицательные входы, и relu становится тождеством, прямой линией. Падает ровно один тест, negz; больше ни у одного теста нет отрицательного входа. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

Нейрон пропускает свою сумму через функцию активации, и в gft_relu.t27 это relu(x) = max(0, x). on_comb возвращает 0 для нуля и для любого входа с битом знака, а иначе сам x. Заголовок говорит, что у ReLU точный градиент 0/1 и что 2-слойная сеть с ReLU решает XOR, а линейная модель GF-T этого не может. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который пропускает отрицательные входы, и relu становится тождеством, прямой линией, и снова t27c test-report. Падает ровно один тест, negz; больше ни у одного теста нет отрицательного входа. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и значение, которого ждёт negz; затем найдите в спеке комментарий на этой строке и проверьте, описывает ли он код после замены.

Открыть интерактивный урок →

-
gft_relu.t27: recording pending
gft_relu.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_relu.t27 -- a bend at zero, native
t27c on gft_relu.t27 -- a bend at zero, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; letting negative inputs through makes exactly one test fail, negz; git restores the spec.

specs/ternary/gft_relu.t27

module GftRelu;
 // #1764 + GF-T: a GF-T ReLU activation -- relu(x) = max(0, x). For a signed GF-T16
 // input, returns x if x > 0 else 0 (raw 0). Unlike the sign/trit quantizer, ReLU
diff --git a/apps/website/public/ru/learn/a-loss-in-bits/card.png b/apps/website/public/ru/learn/a-loss-in-bits/card.png
index 13ab6d363b..ab39783e6e 100644
Binary files a/apps/website/public/ru/learn/a-loss-in-bits/card.png and b/apps/website/public/ru/learn/a-loss-in-bits/card.png differ
diff --git a/apps/website/public/ru/learn/a-loss-in-bits/index.html b/apps/website/public/ru/learn/a-loss-in-bits/index.html
index f54adc13c1..eaef1d4bf0 100644
--- a/apps/website/public/ru/learn/a-loss-in-bits/index.html
+++ b/apps/website/public/ru/learn/a-loss-in-bits/index.html
@@ -5,7 +5,7 @@
 
 
 Потеря в битах: курс «ИИ-числа», урок 19 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Потеря в битах

Вы узнаете

Как потеря -log2(p) оценивает догадку в битах и почему безошибочная догадка должна стоить ровно 0.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Обучение начинается с числа, которое говорит, насколько неверной была догадка. gft_nll.t27 берёт p, вероятность, которую модель дала верному классу, и возвращает -log2(p): p = 1.0 стоит 0, p = 0.5 стоит 1 бит, а p = 0.25 стоит 2. on_comb — это neg(log2v(p)), и neg переворачивает бит знака, но для 0 возвращает 0. Одна строка заголовка всё ещё говорит, что на выходе log2(x); код возвращает его с обратным знаком. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись заставляет neg возвращать 65536 для 0, ноль с поставленным битом знака, и падает ровно один тест, perfect. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

Обучение начинается с числа, которое говорит, насколько неверной была догадка. gft_nll.t27 берёт p, вероятность, которую модель дала верному классу, и возвращает -log2(p): p = 1.0 стоит 0, p = 0.5 стоит 1 бит, а p = 0.25 стоит 2. on_comb — это neg(log2v(p)), и neg переворачивает бит знака, но для 0 возвращает 0. Одна строка заголовка всё ещё говорит, что на выходе log2(x); код возвращает его с обратным знаком. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который заставляет neg возвращать 65536 для 0, ноль с поставленным битом знака, и снова t27c test-report: падает ровно один тест, perfect. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и упавший тест; затем найдите в спеке строку заголовка, где сказано, что на выходе log2(x), и сравните её с on_comb.

Открыть интерактивный урок →

-
gft_nll.t27: recording pending
gft_nll.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_nll.t27 -- a loss in bits, native
t27c on gft_nll.t27 -- a loss in bits, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; a zero with the sign bit set from neg makes exactly one test fail, perfect; git restores the spec.

specs/ternary/gft_nll.t27

module GftNll;
 // #1764 + GF-T: GF-T negative-log-likelihood (cross-entropy loss for a one-hot
 // label) -- given the softmax probability p of the true class, returns the loss
diff --git a/apps/website/public/ru/learn/a-sign-is-one-bit/card.png b/apps/website/public/ru/learn/a-sign-is-one-bit/card.png
index ddd3797e0d..21bc1e9e64 100644
Binary files a/apps/website/public/ru/learn/a-sign-is-one-bit/card.png and b/apps/website/public/ru/learn/a-sign-is-one-bit/card.png differ
diff --git a/apps/website/public/ru/learn/a-sign-is-one-bit/index.html b/apps/website/public/ru/learn/a-sign-is-one-bit/index.html
index 661f58c495..5cfba88efc 100644
--- a/apps/website/public/ru/learn/a-sign-is-one-bit/index.html
+++ b/apps/website/public/ru/learn/a-sign-is-one-bit/index.html
@@ -5,7 +5,7 @@
 
 
 Знак — это один бит: курс «ИИ-числа», урок 13 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Знак — это один бит

Вы узнаете

Как умножение чисел со знаком ставит знак произведения и какой тест замечает, когда знак не ставится никогда.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Модули с 1 по 4 были о том, как хранить числа: ячейка T27, байт масштаба OCP MX, тернарные веса с масштабом фи и TNF; этот модуль заставляет их считать. gft_smul.t27 использует биты tnf17.t27: 1.0 — это 20480, а -1.0 — это 86016, те же биты плюс бит знака 65536. smul ставит знак, когда знаки входов различаются, а величину отдаёт magmul. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись делает знак всегда равным 0, и падает ровно один тест, m2, который умножает 1.0 на -1.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

Модули с 1 по 4 были о том, как хранить числа: ячейка T27, байт масштаба OCP MX, тернарные веса с масштабом фи и TNF; этот модуль заставляет их считать. gft_smul.t27 использует биты tnf17.t27: 1.0 — это 20480, а -1.0 — это 86016, те же биты плюс бит знака 65536. smul ставит знак, когда знаки входов различаются, а величину отдаёт magmul. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который делает знак всегда равным 0, и снова t27c test-report: падает ровно один тест, m2, который умножает 1.0 на -1.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и упавший тест; затем найдите в спеке m1 и m3 и поймите, почему знак, который всегда равен 0, не может их сломать.

Открыть интерактивный урок →

-
gft_smul.t27: recording pending
gft_smul.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_smul.t27 -- a sign is one bit, native
t27c on gft_smul.t27 -- a sign is one bit, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; a sign that is always 0 makes exactly one test fail, m2; git restores the spec.

specs/ternary/gft_smul.t27

module GftSmul;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/ru/learn/multiply-then-add/card.png b/apps/website/public/ru/learn/multiply-then-add/card.png
index 1983cdb669..82e2845856 100644
Binary files a/apps/website/public/ru/learn/multiply-then-add/card.png and b/apps/website/public/ru/learn/multiply-then-add/card.png differ
diff --git a/apps/website/public/ru/learn/multiply-then-add/index.html b/apps/website/public/ru/learn/multiply-then-add/index.html
index 1af5f81490..bf87e002f0 100644
--- a/apps/website/public/ru/learn/multiply-then-add/index.html
+++ b/apps/website/public/ru/learn/multiply-then-add/index.html
@@ -5,7 +5,7 @@
 
 
 Умножить, потом сложить: курс «ИИ-числа», урок 15 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Умножить, потом сложить

Вы узнаете

Что умножение с накоплением делает с двумя произведениями со знаком и как его ломает замена из урока 12.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Нейрон суммирует произведения весов на входы, и on_comb в gft_signed_mac.t27 — такая сумма из двух слагаемых: smul(a1, b1) плюс smul(a2, b2). Здесь smul ставит знак через XOR и не проверяет ноль. Нативный t27c запускает 4 теста, и все проходят; один из них, zero_clause, ждёт, что 0 x 1 + 0 x 1 даст 512, то есть 2^-39, а не 0, и комментарий называет это задуманным поведением. Браузер пропускает свои тесты, потому что его исполнитель пока не знает assert_eq. Запись повторяет ошибку урока 12 и меняет XOR на OR. Падает ровно один тест, pp: (-1.0) x (-1.0) теперь выходит отрицательным, и сумма равна 0 вместо 2.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

Нейрон суммирует произведения весов на входы, и on_comb в gft_signed_mac.t27 — такая сумма из двух слагаемых: smul(a1, b1) плюс smul(a2, b2). Здесь smul ставит знак через XOR и не проверяет ноль. Нативный t27c запускает 4 теста, и все проходят; один из них, zero_clause, ждёт, что 0 x 1 + 0 x 1 даст 512, то есть 2^-39, а не 0, и комментарий называет это задуманным поведением. Браузер пропускает свои тесты, потому что его исполнитель пока не знает assert_eq. Запись запускает t27c test-report, затем повторяет ошибку урока 12 однострочным sed, который меняет XOR на OR, и снова запускает t27c test-report. Падает ровно один тест, pp: (-1.0) x (-1.0) теперь выходит отрицательным, и сумма равна 0 вместо 2.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи тест, который ждёт 512, и тест, который ломает подложенная ошибка; затем сами замените в спеке XOR в smul на OR и посмотрите, поймает ли это браузер.

Открыть интерактивный урок →

-
gft_signed_mac.t27: recording pending
gft_signed_mac.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_signed_mac.t27 -- multiply, then add, native
t27c on gft_signed_mac.t27 -- multiply, then add, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; OR in place of the sign XOR makes exactly one test fail, pp; git restores the spec.

specs/ternary/gft_signed_mac.t27

module GftSignedMac;
 // #1764 + GF-T: a spec-first SIGNED GF-T16 MAC (y = a1*b1 + a2*b2) with
 // round-to-nearest-even, bit-exact to the ideal oracle gft16_ref.py (with sign).
diff --git a/apps/website/public/ru/learn/one-step-downhill/card.png b/apps/website/public/ru/learn/one-step-downhill/card.png
index 9390278214..bde6eaa348 100644
Binary files a/apps/website/public/ru/learn/one-step-downhill/card.png and b/apps/website/public/ru/learn/one-step-downhill/card.png differ
diff --git a/apps/website/public/ru/learn/one-step-downhill/index.html b/apps/website/public/ru/learn/one-step-downhill/index.html
index 8306c7abb9..638e85a747 100644
--- a/apps/website/public/ru/learn/one-step-downhill/index.html
+++ b/apps/website/public/ru/learn/one-step-downhill/index.html
@@ -5,7 +5,7 @@
 
 
 Один шаг вниз по склону: курс «ИИ-числа», урок 20 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Один шаг вниз по склону

Вы узнаете

Как один шаг SGD сдвигает вес против градиента и почему тест с двумя равными входами не может их различить.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. gft_sgd_step.t27 сдвигает вес против градиента: w' = w - eta*g, где eta — скорость обучения. Её 3 теста: step, где w = 1.0, g = 0.5 и eta = 0.5; nograd, где g = 0 оставляет w как есть; и ascend, где g = -1.0 и eta = 1.0 поднимают w до 2.0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись умножает g на само себя вместо eta. В step g равно eta, а в nograd оба произведения равны 0. Поэтому падает ровно один тест, ascend, где вес падает до 0, вместо того чтобы подняться до 2.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

gft_sgd_step.t27 сдвигает вес против градиента: w' = w - eta*g, где eta — скорость обучения. Её 3 теста: step, где w = 1.0, g = 0.5 и eta = 0.5; nograd, где g = 0 оставляет w как есть; и ascend, где g = -1.0 и eta = 1.0 поднимают w до 2.0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который умножает g на само себя вместо eta, и снова t27c test-report. В step g равно eta, а в nograd оба произведения равны 0. Поэтому падает ровно один тест, ascend, где вес падает до 0, вместо того чтобы подняться до 2.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и упавший тест; затем найдите в спеке step и решите, какой из его входов нужно изменить, чтобы он поймал ошибку.

Открыть интерактивный урок →

-
gft_sgd_step.t27: recording pending
gft_sgd_step.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_sgd_step.t27 -- one step downhill, native
t27c on gft_sgd_step.t27 -- one step downhill, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; g times g in place of eta times g makes exactly one test fail, ascend; git restores the spec.

specs/ternary/gft_sgd_step.t27

module GftSgdStep;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/ru/learn/opposite-signs-subtract/card.png b/apps/website/public/ru/learn/opposite-signs-subtract/card.png
index e87a51fdbc..7a1bfa0d6c 100644
Binary files a/apps/website/public/ru/learn/opposite-signs-subtract/card.png and b/apps/website/public/ru/learn/opposite-signs-subtract/card.png differ
diff --git a/apps/website/public/ru/learn/opposite-signs-subtract/index.html b/apps/website/public/ru/learn/opposite-signs-subtract/index.html
index edd2bb7977..aed422d280 100644
--- a/apps/website/public/ru/learn/opposite-signs-subtract/index.html
+++ b/apps/website/public/ru/learn/opposite-signs-subtract/index.html
@@ -5,7 +5,7 @@
 
 
 Разные знаки — вычитание: курс «ИИ-числа», урок 14 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Разные знаки — вычитание

Вы узнаете

Как сложение чисел со знаком превращается в вычитание, когда знаки разные, и что его 3 теста оставляют непроверенным.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. sadd в gft_sadd.t27 складывает два числа одного знака через magadd. Когда знаки разные, она вычитает меньшую величину из большей через magsub, берёт знак большего и при точном сокращении возвращает 0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Знаки смешивает только a2, 1.0 плюс -1.0, и они сокращаются: magsub сразу возвращает 0, потому что величины равны. Ни один тест не вычитает две разные величины, поэтому знак большего ни разу не решает результат. Запись складывает величины в той ветке, куда попадает a2, и падает ровно один тест, a2. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

sadd в gft_sadd.t27 складывает два числа одного знака через magadd. Когда знаки разные, она вычитает меньшую величину из большей через magsub, берёт знак большего и при точном сокращении возвращает 0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Знаки смешивает только a2, 1.0 плюс -1.0, и они сокращаются: magsub сразу возвращает 0, потому что величины равны. Ни один тест не вычитает две разные величины, поэтому знак большего ни разу не решает результат. Запись запускает t27c test-report, затем однострочный sed, который складывает величины в той ветке, куда попадает a2, и снова t27c test-report: падает ровно один тест, a2. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и значение, которого ждёт a2; затем найдите в спеке bsign и проверьте, зависит ли от него хоть один из a1, a2 и a3.

Открыть интерактивный урок →

-
gft_sadd.t27: recording pending
gft_sadd.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_sadd.t27 -- opposite signs subtract, native
t27c on gft_sadd.t27 -- opposite signs subtract, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; adding the sizes where opposite signs must subtract makes exactly one test fail, a2; git restores the spec.

specs/ternary/gft_sadd.t27

module GftSadd;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/ru/learn/pick-the-largest/card.png b/apps/website/public/ru/learn/pick-the-largest/card.png
index ae5bae7193..493ec7beb0 100644
Binary files a/apps/website/public/ru/learn/pick-the-largest/card.png and b/apps/website/public/ru/learn/pick-the-largest/card.png differ
diff --git a/apps/website/public/ru/learn/pick-the-largest/index.html b/apps/website/public/ru/learn/pick-the-largest/index.html
index c6d912015d..1b410dfc34 100644
--- a/apps/website/public/ru/learn/pick-the-largest/index.html
+++ b/apps/website/public/ru/learn/pick-the-largest/index.html
@@ -5,7 +5,7 @@
 
 
 Выбрать наибольшее: курс «ИИ-числа», урок 18 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Выбрать наибольшее

Вы узнаете

Как argmax называет класс, сравнивая одни только биты, и какое правило решает ничью.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Классификатор в конце называет класс с наибольшей оценкой. gft_argmax4.t27 делает это без арифметики: gt ставит отрицательные ниже нуля, ноль ниже положительных, а затем сравнивает младшие 16 бит, растущие с величиной. По заголовку ничью выигрывает меньший индекс, сравнение строгое >. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись меняет > на >= для двух положительных оценок, и падает ровно один тест, tie_low: четыре оценки 1.0 теперь выбирают индекс 3, а не 0. Ни один тест не даёт решить ответ двум отрицательным оценкам: переворот их сравнения ничего не роняет. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

Классификатор в конце называет класс с наибольшей оценкой. gft_argmax4.t27 делает это без арифметики: gt ставит отрицательные ниже нуля, ноль ниже положительных, а затем сравнивает младшие 16 бит, растущие с величиной. По заголовку ничью выигрывает меньший индекс, сравнение строгое >. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который меняет > на >= для двух положительных оценок, и снова t27c test-report: падает ровно один тест, tie_low: четыре оценки 1.0 теперь выбирают индекс 3, а не 0. Ни один тест не даёт решить ответ двум отрицательным оценкам: переворот их сравнения ничего не роняет. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и упавший тест; затем найдите в спеке строку, которая сравнивает две отрицательные оценки, и тот один тест, который до неё доходит.

Открыть интерактивный урок →

-
gft_argmax4.t27: recording pending
gft_argmax4.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_argmax4.t27 -- pick the largest, native
t27c on gft_argmax4.t27 -- pick the largest, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; >= in place of > for two positive scores makes exactly one test fail, tie_low; git restores the spec.

specs/ternary/gft_argmax4.t27

module GftArgmax4;
 // #1764 + GF-T: a classification head over four signed GF-T16 logits. Returns the
 // INDEX {0,1,2,3} of the maximum real value (lowest index wins ties, strict >).
diff --git a/apps/website/public/ru/learn/two-to-the-x/card.png b/apps/website/public/ru/learn/two-to-the-x/card.png
index c5495c669b..1da2788494 100644
Binary files a/apps/website/public/ru/learn/two-to-the-x/card.png and b/apps/website/public/ru/learn/two-to-the-x/card.png differ
diff --git a/apps/website/public/ru/learn/two-to-the-x/index.html b/apps/website/public/ru/learn/two-to-the-x/index.html
index 911e02a0be..9ee9ddf5a9 100644
--- a/apps/website/public/ru/learn/two-to-the-x/index.html
+++ b/apps/website/public/ru/learn/two-to-the-x/index.html
@@ -5,7 +5,7 @@
 
 
 Два в степени x: курс «ИИ-числа», урок 17 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

Два в степени x

Вы узнаете

Как exp2 делит x на целую часть и дробь и почему её тесты доходят только до целой части.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Заголовок gft_exp2.t27 называет exp2 недостающим кирпичиком для softmax на GF-T. on_comb делит x на целую часть k и дробь f: k задаёт смещение, k + 40, а pow2_frac превращает f в мантиссу. Заголовок обещает ошибку не больше 1 ULP, измеренную в прототипе, но все 4 теста подают целые входы, 0, 1.0, -1.0 и 2.0, поэтому f равна 0, и pow2_frac возвращает 0 при любых коэффициентах. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись теряет минус у k для отрицательного x, и падает ровно один тест, em1: 2^-1 выходит как 2.0, а не 0.5. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

Заголовок gft_exp2.t27 называет exp2 недостающим кирпичиком для softmax на GF-T. on_comb делит x на целую часть k и дробь f: k задаёт смещение, k + 40, а pow2_frac превращает f в мантиссу. Заголовок обещает ошибку не больше 1 ULP, измеренную в прототипе, но все 4 теста подают целые входы, 0, 1.0, -1.0 и 2.0, поэтому f равна 0, и pow2_frac возвращает 0 при любых коэффициентах. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который теряет минус у k для отрицательного x, и снова t27c test-report: падает ровно один тест, em1: 2^-1 выходит как 2.0, а не 0.5. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и значение, которого ждёт em1; затем найдите в спеке pow2_frac и выясните, что она возвращает, когда f равна 0.

Открыть интерактивный урок →

-
gft_exp2.t27: recording pending
gft_exp2.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_exp2.t27 -- two to the x, native
t27c on gft_exp2.t27 -- two to the x, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; dropping the minus sign of k makes exactly one test fail, em1; git restores the spec.

specs/ternary/gft_exp2.t27

module GftExp2;
 // #1764 + GF-T: a GF-T exp2 primitive -- 2^x for a signed GF-T16 input x, result
 // a positive GF-T16. This is the missing building block for a GF-T softmax
diff --git a/apps/website/public/ru/learn/xor-needs-a-bend/card.png b/apps/website/public/ru/learn/xor-needs-a-bend/card.png
index 631cc55307..3e728717dc 100644
Binary files a/apps/website/public/ru/learn/xor-needs-a-bend/card.png and b/apps/website/public/ru/learn/xor-needs-a-bend/card.png differ
diff --git a/apps/website/public/ru/learn/xor-needs-a-bend/index.html b/apps/website/public/ru/learn/xor-needs-a-bend/index.html
index dd33641e77..e137351a00 100644
--- a/apps/website/public/ru/learn/xor-needs-a-bend/index.html
+++ b/apps/website/public/ru/learn/xor-needs-a-bend/index.html
@@ -5,7 +5,7 @@
 
 
 XOR нужен излом: курс «ИИ-числа», урок 21 из 27
-
+
 
 
 
@@ -16,7 +16,7 @@
 
 
 
-
+
 
 
 
@@ -24,7 +24,7 @@
 
 
 
-
+
 
 
 
@@ -60,11 +60,11 @@
 
 

XOR нужен излом

Вы узнаете

Почему сети нужен скрытый слой с ReLU, чтобы вычислить XOR, и что делает один пропавший излом.

-

Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. XOR равен 1, когда ровно один вход равен 1, и комментарий в gft_xornet.t27 говорит, что одиночная линейная модель не может его разделить. on_comb — 2-слойная сеть: две скрытые суммы, z0 и z1, каждая через relu, затем взвешенная сумма h0 и h1. Она не обучается: тесты подают веса, заданные вручную, аналитическое решение W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись убирает relu только у h1, и падает ровно один тест, x00: в (0,0) z1 равно -1.0, и выход становится 2.0 вместо 0. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

XOR равен 1, когда ровно один вход равен 1, и комментарий в gft_xornet.t27 говорит, что одиночная линейная модель не может его разделить. on_comb — 2-слойная сеть: две скрытые суммы, z0 и z1, каждая через relu, затем взвешенная сумма h0 и h1. Она не обучается: тесты подают веса, заданные вручную, аналитическое решение W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который убирает relu только у h1, и снова t27c test-report: падает ровно один тест, x00: в (0,0) z1 равно -1.0, и выход становится 2.0 вместо 0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую строку и упавший тест; затем вычислите в спеке z1 для x10, x01 и x11 и поймите, почему relu там ничего не меняет.

Открыть интерактивный урок →

-
gft_xornet.t27: recording pending
gft_xornet.t27: recording pending ↗
-

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

+
t27c on gft_xornet.t27 -- XOR needs a bend, native
t27c on gft_xornet.t27 -- XOR needs a bend, native ↗
+

t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; removing relu from h1 makes exactly one test fail, x00; git restores the spec.

specs/ternary/gft_xornet.t27

module GftXorNet;
 // #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
 // on-device training step (forward softmax -> loss -> gradient g -> THIS update).
diff --git a/apps/website/public/term/index.html b/apps/website/public/term/index.html
index 05fdad4800..21ab587b32 100644
--- a/apps/website/public/term/index.html
+++ b/apps/website/public/term/index.html
@@ -12,9 +12,9 @@
 
 
 
-
+
 
-
+
 `
+
+export function pageHtml(meta, screen) {
+  const edited = meta.redacted.length ? `Edited: ${meta.redacted.join('; ')}.` : 'Edited: nothing.'
+  return `
+
+
+
+
+${escPre(meta.title)} · Trinity S³AI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+${STYLE}
+
+
+
+ +

${escPre(meta.title)}

+

${escPre(meta.desc)}

+
${noscriptOf(meta.title, screen)}
+
recorded ${meta.recorded}real ${meta.real_s} sshown ${meta.shown_s} sexit codes ${meta.exit_codes.join(' ')}
+
${meta.commands.map((c) => `$ ${esc(c)}`).join('\n')}
+

Staged: the prompt and the typing. Real: every byte the commands printed, at the time they printed it. Any silence longer than 2 s is shown for 2 s, and the title bar says so while it happens. ${edited} No GIF: this recording has none.

+ +
+ + + +` +} + +// ------------------------------------------------------------------ the card + +export function cardHtml(meta, screen) { + const fonts = pathToFileURL(join(PUBLIC, 'fonts')).href + const logo = pathToFileURL(join(PUBLIC, 'favicon.svg')).href + const rows = screen.runs().slice(-CARD.rows) + const body = rows.map((runs) => `
${runs.map((r, i) => `${escPre(i === runs.length - 1 ? r.text.replace(/\s+$/, '') : r.text)}`).join('')}
`).join('') + return ` +
Trinity S³AI
t27.ai/term/${meta.id}
+

${escPre(meta.title)}

+

${escPre(meta.desc)}

+
▶ recorded session · real output
${body}
+` +} + +function findChrome() { + return [process.env.CHROME, '/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome', + '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '/Applications/Chromium.app/Contents/MacOS/Chromium'].find((p) => p && existsSync(p)) +} + +// As scripts/course-pages.mjs renders its cards: headless Chromium, one screenshot, the process group killed. +async function render(chrome, html, png) { + const work = mkdtempSync(join(tmpdir(), 'term-card-')) + const src = join(work, 'card.html') + writeFileSync(src, html) + const before = existsSync(png) ? statSync(png).mtimeMs : 0 + const b = spawn(chrome, ['--headless=new', `--user-data-dir=${join(work, 'profile')}`, '--allow-file-access-from-files', '--hide-scrollbars', + '--force-device-scale-factor=1', `--window-size=${CARD.width},${CARD.height}`, '--virtual-time-budget=3000', `--screenshot=${png}`, pathToFileURL(src).href], { stdio: 'ignore', detached: true }) + const deadline = Date.now() + 60_000 + while (Date.now() < deadline) { + await new Promise((r) => setTimeout(r, 300)) + if (existsSync(png) && statSync(png).mtimeMs > before && statSync(png).size > 0) break + } + await new Promise((r) => setTimeout(r, 300)) + const exited = new Promise((r) => b.once('exit', r)) + try { process.kill(-b.pid, 'SIGKILL') } catch { b.kill('SIGKILL') } + await Promise.race([exited, new Promise((r) => setTimeout(r, 5000))]) + try { rmSync(work, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }) } catch { /* a temp dir the OS will clear */ } + if (!existsSync(png) || statSync(png).mtimeMs <= before) throw new Error(`t27c-mutation-casts: no card written to ${png}`) +} + +// ------------------------------------------------------------------ the list of recordings + +/** Put these recordings first in public/term/index.html, replacing their old rows; the count stays honest. */ +export function indexWith(html, metas) { + const row = (m) => `
${escPre(m.title)}${m.recorded} · ${m.real_s} s real
` + const ids = new Set(metas.map((m) => m.id)) + const lines = html.split('\n').filter((l) => { const m = /^/.exec(l); return !(m && ids.has(m[1])) }) + const at = lines.indexOf('
') + if (at < 0) throw new Error('public/term/index.html has no
') + // Newest first, as the list is kept; within one minute the later recording first. + const sorted = metas.map((m, i) => [m, i]).sort(([a, i], [b, j]) => (a.recorded < b.recorded ? 1 : a.recorded > b.recorded ? -1 : j - i)).map(([m]) => m) + lines.splice(at + 1, 0, ...sorted.map(row)) + const count = lines.filter((l) => /^/.exec(lines[at + 1])[1] + return lines.join('\n') + .replace(/(the output is what the commands printed\. )\d+( so far\.)/, `$1${count}$2`) + .replace(/()/g, `$1${newest}$2`) +} + +// ------------------------------------------------------------------ main + +async function main() { + const arg = (k) => { const i = process.argv.indexOf(k); return i > 0 ? process.argv[i + 1] : undefined } + const t27 = arg('--t27'), t27c = arg('--t27c'), zig = arg('--zig') + const indexOnly = process.argv.includes('--index-only') + const where = arg('--where') ?? 'a Claude Code cloud container (Linux x86_64)' + const only = (arg('--only') ?? '').split(',').filter(Boolean) + if (!indexOnly && (!t27 || !t27c || !zig)) { console.error('usage: t27c-mutation-casts.mjs --t27 DIR --t27c PATH --zig PATH [--where TEXT] [--only id,id]'); process.exit(2) } + if (process.argv.includes('--index-only')) { + // Rewrite only public/term/index.html from the meta.json the recordings already have. + const metas = RECORDINGS.map((r) => JSON.parse(readFileSync(join(TERM, r.id, 'meta.json'), 'utf8'))) + writeFileSync(join(TERM, 'index.html'), indexWith(readFileSync(join(TERM, 'index.html'), 'utf8'), metas)) + console.log(`t27c-mutation-casts: listed ${metas.length} recording(s) in public/term/index.html`) + return + } + const chrome = findChrome() + if (!chrome) throw new Error('t27c-mutation-casts: no Chromium for the cards; set CHROME') + if (!gitClean(t27)) throw new Error(`${t27} has uncommitted changes; record from a clean checkout so git checkout really restores the spec`) + const home = mkdtempSync(join(tmpdir(), 't27c-cast-home-')) + const env = { ...bareEnv(), HOME: home, COLUMNS: String(WIDTH), LINES: String(HEIGHT), PATH: `${dirname(t27c)}:${dirname(zig)}:/usr/bin:/bin` } + const redactions = [[t27, '~/t27'], [home, '~'], [process.env.HOME ?? '/root', '~']].filter(([from]) => from && from !== '/') + const todo = RECORDINGS.filter((r) => !only.length || only.includes(r.id)) + const metas = [] + const failures = [] + for (const r of todo) { + const commands = commandsOf(r) + const steps = [] + for (const c of commands) steps.push(await run(c, t27, env)) + const clean = gitClean(t27) + const problems = problemsOf(r, { steps }) + if (!clean) problems.push(`${r.id}: the checkout is not clean after the recording`) + if (problems.length) { failures.push(...problems); console.error(problems.join('\n')); continue } + let edited = false + const redact = (s) => redactions.reduce((out, [from, to]) => { if (out.includes(from)) { edited = true; return out.split(from).join(to) } return out }, s) + const events = eventsOf(commands, steps, redact) + const out = (i) => steps[i].chunks.map((c) => c.text).join('') + const [t27cVersion, zigVersion] = out(1).trim().split('\n') + const commit = out(0).trim().split(' ')[0] + const base = reportOf(out(3)) + const recordedAt = new Date() + const recorded = recordedAt.toISOString().replace('T', ' ').slice(0, 16) + ' UTC' + const real = events[events.length - 1][0] + const vacuous = base.vacuous === 0 ? 'none vacuous' : `${base.vacuous} vacuous` + const meta = { + id: r.id, + title: r.title, + desc: `${t27cVersion} and zig ${zigVersion} in ${where}, spec at t27 ${commit}: all ${base.tests} tests pass natively, ${vacuous}; ${r.what} makes exactly one test fail, ${r.fails}; git restores the spec.`, + url: `${ORIGIN}/term/${r.id}/`, + gif: null, + post: null, + recorded, + real_s: Number(real.toFixed(1)), + shown_s: Number(shownOf(events).toFixed(1)), + commands, + exit_codes: steps.map((s) => s.exit), + redacted: edited ? ['home directory shown as ~'] : [], + published: recordedAt.toISOString().slice(0, 10), + } + const header = { version: 2, width: WIDTH, height: HEIGHT, timestamp: Math.floor(recordedAt.getTime() / 1000), title: meta.title, commands, redacted: meta.redacted } + const dir = join(TERM, r.id) + mkdirSync(dir, { recursive: true }) + writeFileSync(join(dir, 'session.cast'), castText(header, events)) + writeFileSync(join(dir, 'meta.json'), JSON.stringify(meta, null, 2) + '\n') + writeFileSync(join(dir, 'index.html'), pageHtml(meta, screenAfter(events))) + // The card shows the moment the mutant's report has printed: the diff and the one failing test. + const afterMutant = events.filter((e) => e[1] === 'x')[5][0] + await render(chrome, cardHtml(meta, screenAfter(events, afterMutant)), join(dir, 'card.png')) + metas.push(meta) + console.log(`ok ${r.id}: ${base.tests} pass, mutant fails ${r.fails}, ${meta.real_s} s real`) + } + if (metas.length) writeFileSync(join(TERM, 'index.html'), indexWith(readFileSync(join(TERM, 'index.html'), 'utf8'), metas)) + rmSync(home, { recursive: true, force: true }) + if (failures.length) { console.error(`t27c-mutation-casts: ${failures.length} recording(s) refused`); process.exit(1) } + console.log(`t27c-mutation-casts: wrote ${metas.length} recording(s)`) +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) await main() diff --git a/apps/website/scripts/t27c-mutation-casts.test.mjs b/apps/website/scripts/t27c-mutation-casts.test.mjs new file mode 100644 index 0000000000..ee4e67cdaa --- /dev/null +++ b/apps/website/scripts/t27c-mutation-casts.test.mjs @@ -0,0 +1,104 @@ +// The nine recordings of "AI numbers" lessons 13 to 21 show what their lessons say: the committed +// session.cast of each runs the commands meta.json lists, its baseline report passes every test, and +// the report after the one-line sed names exactly the test the recorder was told must fail. +import assert from 'node:assert/strict' +import { existsSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { test } from 'node:test' +import { fileURLToPath } from 'node:url' +import { RECORDINGS, Screen, commandsOf, indexWith, problemsOf, reportOf } from './t27c-mutation-casts.mjs' + +const TERM = join(dirname(fileURLToPath(import.meta.url)), '..', 'public', 'term') + +const PASS = `--- test report: specs/ternary/x.t27 --- + + tests 4 + pass 4 + FAIL 0 + rate 100.0% + vacuous passes 0 of 4 (passed with 0 runtime asserts executed) +` +const ONE_FAIL = `--- test report: specs/ternary/x.t27 --- + FAIL negz + + tests 4 + pass 3 + FAIL 1 + rate 75.0% +` + +test('reportOf reads the counts and the failed tests of t27c test-report', () => { + assert.deepEqual(reportOf(PASS), { tests: 4, pass: 4, fail: 0, failed: [], vacuous: 0 }) + assert.deepEqual(reportOf(ONE_FAIL), { tests: 4, pass: 3, fail: 1, failed: ['negz'], vacuous: null }) +}) + +const H = 'a'.repeat(64) +const stepsOf = (outs) => ({ steps: outs.map((text) => ({ exit: '0', chunks: [{ at: 0, text }] })) }) +const good = ['abc x\n', 't27c 0.5.1\n0.16.0\n', `${H} s\nline\n`, PASS, '- a\n+ b\n', ONE_FAIL, `Updated 1 path from the index\n${H} s\n`] +const r = { id: 'x', fails: 'negz' } + +test('problemsOf accepts a run that shows the lesson, and refuses each way it can lie', () => { + assert.deepEqual(problemsOf(r, stepsOf(good)), []) + assert.match(problemsOf({ ...r, fails: 'pos' }, stepsOf(good)).join(), /not exactly pos/) + assert.match(problemsOf(r, stepsOf(good.map((o, i) => (i === 3 ? ONE_FAIL : o)))).join(), /baseline/) + assert.match(problemsOf(r, stepsOf(good.map((o, i) => (i === 4 ? '- a\n+ b\n- c\n' : o)))).join(), /one-line/) + assert.match(problemsOf(r, stepsOf(good.map((o, i) => (i === 6 ? `${'b'.repeat(64)} s\n` : o)))).join(), /hash/) + const failedExit = stepsOf(good) + failedExit.steps[1].exit = '1' + assert.match(problemsOf(r, failedExit).join(), /exited/) +}) + +test('the screen wraps at 104 columns and keeps the prompt colour', () => { + const s = new Screen().feed('\u001b[38;2;255;215;0mt27 $ \u001b[0m' + 'x'.repeat(110) + '\r\n') + const rows = s.runs() + assert.deepEqual(rows[0].map((run) => run.fg), ['#ffd700', '#ebebeb']) + assert.equal(rows[0].map((run) => run.text).join('').length, 104) + assert.equal(rows[1][0].text.trimEnd(), 'x'.repeat(12)) +}) + +test('indexWith lists the recordings first, newest first, and keeps the count honest', () => { + const html = ['', '

Terminal sessions recorded from real commands: the output is what the commands printed. 2 so far.

', '
', '', '', '
'].join('\n') + const out = indexWith(html, [{ id: 'x', title: 'X', recorded: '2026-10-09 08:43 UTC', real_s: 1 }, { id: 'y', title: 'Y', recorded: '2026-10-09 08:44 UTC', real_s: 2 }]) + const ids = [...out.matchAll(/^/gm)].map((m) => m[1]) + assert.deepEqual(ids, ['y', 'x', 'old']) + assert.match(out, /3 so far\./) + assert.match(out, /term\/y\/card\.png/) +}) + +for (const rec of RECORDINGS) { + test(`${rec.id}: the published recording shows exactly ${rec.fails} failing`, () => { + const dir = join(TERM, rec.id) + const meta = JSON.parse(readFileSync(join(dir, 'meta.json'), 'utf8')) + const [head, ...rows] = readFileSync(join(dir, 'session.cast'), 'utf8').trim().split('\n') + const header = JSON.parse(head) + const events = rows.map((l) => JSON.parse(l)) + assert.equal(meta.id, rec.id) + assert.equal(meta.pending, undefined, 'no recording is pending any more') + assert.deepEqual(meta.commands, commandsOf(rec)) + assert.deepEqual(header.commands, meta.commands) + assert.equal(header.version, 2) + assert.deepEqual(meta.exit_codes, meta.commands.map(() => '0')) + assert.equal(meta.gif, null) + assert.equal(existsSync(join(dir, 'session.gif')), false, 'gif is null, so there is no GIF file either') + assert.match(meta.desc, /cloud container/) + assert.match(meta.desc, /t27c \d+\.\d+\.\d+ and zig \d+\.\d+\.\d+/) + // The output of each command: the events between its exit and the next one. + const outputs = [] + let cur = '' + let typing = true + for (const [, kind, text] of events) { + if (kind === 'x') { outputs.push(cur); cur = ''; typing = true; continue } + if (typing) { if (text === '\r\n') typing = false; continue } + cur += text + } + assert.equal(outputs.length, 7) + const base = reportOf(outputs[3].replace(/\r\n/g, '\n')) + const mutant = reportOf(outputs[5].replace(/\r\n/g, '\n')) + assert.equal(base.fail, 0) + assert.equal(base.pass, base.tests) + assert.deepEqual(mutant.failed, [rec.fails]) + assert.equal(mutant.fail, 1) + const hashes = [outputs[2], outputs[6]].map((o) => /^([0-9a-f]{64})\s/m.exec(o)[1]) + assert.equal(hashes[0], hashes[1], 'git checkout restores the spec byte for byte') + }) +} diff --git a/apps/website/specs/course/ai-numbers.t27 b/apps/website/specs/course/ai-numbers.t27 index d613543b84..660c63e9f0 100644 --- a/apps/website/specs/course/ai-numbers.t27 +++ b/apps/website/specs/course/ai-numbers.t27 @@ -243,15 +243,15 @@ pub const LESSON_TEXTS : [27]str = [ "In the browser, setting MAX_TRITS to 3 made two checks fail. The native t27c is stricter: the 8 invariants of golden_sieve.t27 are proved while it compiles, so with a third trit the spec does not compile at all. The report says BLOCKED, and explains that a blocked spec never produced a binary, so it has no test results to count. A rule checked while compiling cannot ship broken. Then the recording plants a different bug: the rule against shift-register cells now always says yes, and exactly one test fails, the one that checks the formula. Every byte in the recording was printed by the command; only the typing is staged.", "The name TNF covers two different things. In the sieve, TNF(k, b) is an alphabet of weights with 3 or 9 levels. TNF17e in tnf17.t27 is a Ternary Network Float, which the spec calls the signed accumulator format: a sign bit, a 7-bit offset and a 9-bit mantissa, worth (-1)^s x (1 + m/512) x 2^(offset - 40). Its trits are in the exponent, not in the weights: the offset takes 81 values, exactly 3^4, so the exponent is four balanced trits, from -40 to +40. Four trits cost 7 bits on a binary chip, so the rung is 17 bits, not 16; the spec records that its source counted positions, and a trit is not a bit. The browser runs 20 of its 44 checks, all 10 invariants among them, and names the cast behind each skip.", "The browser skipped 24 checks of tnf17.t27; the native t27c runs all 34 tests, all pass, and none is vacuous. Negation flips the sign bit with XOR. The recording swaps XOR for OR, a one-character bug. Negating a positive number still looks right, so the test that negates one still passes. But negating a negative number now leaves it negative, so negating twice no longer gives the number back. Exactly one test fails, negate_is_an_involution, the one that states that property. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes the sign always 0, and exactly one test fails, m2, which multiplies 1.0 by -1.0. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording adds the sizes in the branch a2 takes, and exactly one test fails, a2. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording repeats the bug of lesson 12 and swaps XOR for OR. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording lets negative inputs through, which turns relu into the identity, a straight line. Exactly one test fails, negz; no other test has a negative input. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording drops the minus sign of k for negative x, and exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording turns > into >= for two positive scores, and exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes neg return 65536 for 0, a zero with the sign bit set, and exactly one test fails, perfect. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording multiplies g by itself instead of by eta. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. Every byte in the recording was printed by the command; only the typing is staged.", - "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording removes relu from h1 alone, and exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. Every byte in the recording was printed by the command; only the typing is staged.", + "Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes the sign always 0, and t27c test-report again: exactly one test fails, m2, which multiplies 1.0 by -1.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording runs t27c test-report, then a one-line sed that adds the sizes in the branch a2 takes, and t27c test-report again: exactly one test fails, a2. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording runs t27c test-report, then repeats the bug of lesson 12 with a one-line sed that swaps XOR for OR, and runs t27c test-report again. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that lets negative inputs through, which turns relu into the identity, a straight line, and t27c test-report again. Exactly one test fails, negz; no other test has a negative input. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that drops the minus sign of k for negative x, and t27c test-report again: exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that turns > into >= for two positive scores, and t27c test-report again: exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes neg return 65536 for 0, a zero with the sign bit set, and t27c test-report again: exactly one test fails, perfect. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that multiplies g by itself instead of by eta, and t27c test-report again. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", + "XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that removes relu from h1 alone, and t27c test-report again: exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "A neuron in a ternary network adds up products of trits, and the sum can be any small integer. quantize in activation_quantizer.t27 squeezes it back: above the threshold it returns 2 (P), below minus the threshold 0 (N), and in between 1 (Z). The on_comb at the end makes it combinational: no register, the output follows the inputs. The browser skips all 7 tests because its runner does not know assert_eq yet; the native t27c runs all 7, all pass, none vacuous. The recording changes > to >= on line 6, so a sum equal to the threshold already counts as P. Exactly one test fails, quantize_boundary_hi, which asks quantize(10, 10) for Z. Every byte in the recording was printed by the command; only the typing is staged.", "maj3 in bitnet_majority.t27 packs three trits with pack3, takes dot27 against w_all_p, an all-P weight chunk, and quantizes at 0: the result is the sign of a + b + c. weighted_vote is the same neuron with weights pack3(2, 2, 0) and gives the sign of a + b - c, so the weights pick the function. The browser skips all 12 tests because it does not know assert_eq yet; the native t27c runs all 12, all pass, none vacuous. The recording lowers w_all_p by 16, which turns the weight on lane 2 from P to Z, so maj3 no longer sees c. Exactly one test fails, maj_p_n_n: +1 - 1 - 1 is N, but +1 - 1 is Z. Note that wv_p_z_p passes 0, which is N, as its middle input. Every byte in the recording was printed by the command; only the typing is staged.", "One packed chunk holds 27 trits, so a wider neuron needs several. neuronN in bitnet_neuron_nchunk.t27 takes up to 8 chunks of activations and weights, adds dot27 for nchunks pairs starting at chunk 0, and quantizes the total. The browser skips all 11 tests because it does not know assert_eq yet; the native t27c runs all 11, all pass, none vacuous. The recording changes the loop bound on line 36 from nchunks to 1, so the neuron always reads exactly one chunk. The other neuron tests fill every chunk alike, so one chunk lands on the same side of the threshold. Exactly one test fails, neuron_zero_chunks, which asks for no chunks and expects Z. Every byte in the recording was printed by the command; only the typing is staged.", diff --git a/apps/website/specs/widgets/gallery.t27 b/apps/website/specs/widgets/gallery.t27 index 7e3a439531..dd24ae4d70 100644 --- a/apps/website/specs/widgets/gallery.t27 +++ b/apps/website/specs/widgets/gallery.t27 @@ -86,7 +86,7 @@ pub const WIDGET_PAGES : [194]str = ["term/x7-board/", "term/tri-x7-fasm/", "ter pub const WIDGET_TITLES : [194]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "WARS: agent configurations compared on real GitHub issues", "", "", "", "", "", "", ""]; pub const WIDGET_LINES : [194]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "One issue, one pinned commit, the same tools and gates; only the decision layer changes, and a missing run stays unknown.", "", "", "", "", "", "", ""]; ; Why a person would pass this one on: the reason the widget is in the gallery at all. -pub const WIDGET_HOOKS : [194]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "17 of 17 conformance specs compile, their test blocks pass, and no generated file has drifted.", "14 of 14 bench tools pass their own self-tests in one sweep: decode 18/18, repin 17/17, firelog 10/10, tmpcheck 9/9, stamps 9/9, keep 9/9.", "Requested 115,200 puts 115,385 on the wire (+0.16 %), 921,600 puts 923,077, and 3,000,000 is exact.", "26 checks, no board, no RTL: every receipt returns, 11 loss and fault cases recover, and a lie stops the run.", "One command reads the bench: cables, locks, and the last board runs: three x7-board runs PASS 51840/51840.", "112 files under /tmp pinned by sha256 in specs: every one has a kept copy, 0 lost, 0 unlocated.", "84 board and model logs scanned for the TRI-NET key: 0 hits, 0 logs of 1 MB or more.", "16 spec-named scripts run their own --self-test, 0 bad; 8 without the flag are listed, not skipped silently.", "The part table in far.t27 matches prjxray-db part.json, all 6 reference bitstreams present, bitwalk reads all 6 in 84.4 ms.", "One command re-runs every sweep: CRC 12/12, ECC 32520/32520 frames, 82/82 pins, 4224/4224 segbits, gold 5/5. audit: CLEAN.", "The CDC spec through the real compiler: 12 tests pass natively and the header says what the model does and does not claim.", "The tri clocks card as a spec: constants a tool page reads, and a native test-report that says so plainly -- 0 tests, because it is not a test suite.", "The board itself as a spec: 15 tests pass natively and the part line names the xc7a200tfgg676 the Wukong carries.", "Clock-tree synthesis in numbers: 15 tests pass natively, and the header pins the Artix-7 teaching delays the tree arithmetic uses.", "The MMCM model through the real compiler: VCO window, M/D/O ranges, fractional multiply in eighths, the 1/56 phase step, 16 tests natively.", "Widen the VCO window with one sed and exactly one test objects -- the rejection test that names the 500 MHz config it calls out.", "Static timing as arithmetic: 25 tests pass natively on arcs, slack and Fmax, with the Artix-7 teaching delays in the header.", "The three reset kinds through the real compiler: async assert with sync release through 2 to 4 flops, and the racy async release, 16 tests natively.", "What the compiler emits for the reset pipe: t27c gen prints the Zig, one struct copy per clock, and wc -l counts it.", "Metastability MTBF without floats: the WP323 form in Q10 log2, one flop negative slack, two flops astronomical, 15 tests natively.", "The CDC model as Zig: t27c gen prints what it compiled and wc -l says how many lines of code the model is.", "The FIFO stdlib through the real compiler: 18 tests pass natively and 6 invariants are proved comptime.", "Move a pointer by 3 instead of 1 and every test still passes: the suite asserts fill and flags, never pointer values.", "Flip one flag assignment and a single test objects: the guard that keeps the FIFO honest.", "The pins-to-XDC emitter spec parsed by the real compiler, and the create_clock lines it exists to write.", "The fpga-rxcap tool card as a spec: constants for a tool page, and a native run that says 0 tests and why.", "168 records: 14 papers entered twice, 141 cite no score, 144 state zero at pass@1.", "67 specs lose 23,578 tokens to parser recovery, each held at its pinned bound.", "Smallest hold 3445 ps on TXD1 against a 1000 ps bench; each TX pin setup and hold from the routed SDF.", "Per-bit setup and hold of an async RX nibble against its RXC edge, from the routed SDF: one nibble changes 2387 to 2486 ps apart, so a binary counter tears where a gray one cannot.", "RX_ER pulses injected at exact nibble positions of a ping -- preamble, mid-frame, IP header -- and the frame state each pulse leaves behind, RTL vs model.", "The Ethernet bring-up plan on the bench: pre-registered steps, logged exits, what ran and what did not.", "The registered IO clients on the bench, read live: names, claims and what each declares it owns.", "The bench claim in action: one client takes the hardware lock and the tool reports who holds it.", "The bench lock given back: the client gives up its claim and the bench reports itself free.", "The next queued step on the bench, read live from the run state.", "The memory map spec through the real compiler: 15 tests pass natively and 6 invariants are proved comptime.", "The SPI testbench through the real compiler: 7 tests pass natively, driving the modes and checking the captures.", "t27c check on the UART spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the SPI spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the APB bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the AXI4 spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the bus bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the memory spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "The UART spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The SPI spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The APB bridge spec lowered to synthesizable Verilog: port list, PSEL and PENABLE wiring, the address decode.", "The AXI4 spec lowered to synthesizable Verilog: the five channels' wiring and handshakes as generated wires.", "The bus bridge spec lowered to synthesizable Verilog: both sides of the crossing as generated wires.", "The whole design in one spec lowered to synthesizable Verilog: clocks, resets and bus wiring as the compiler emits them.", "The UART testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The APB bridge testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The AXI4 testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The memory testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The integration testbench spec lowered to Verilog: every bus of the design in one bench.", "The top-level testbench spec lowered to Verilog: the whole design under one bench.", "The SPI testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The UART spec lowered to hardware IR: the compiler's own view of the wires and state before Verilog exists.", "The APB bridge spec lowered to hardware IR: PSEL, PENABLE and the decode as the compiler structures them.", "The AXI4 spec lowered to hardware IR: the five channels and their handshakes as the compiler structures them.", "The Ethernet FCS spec through the real compiler: 8 tests pass natively -- the table derived from one bit step, the 0xCBF43926 check value, the 0xDEBB20E3 receiver residue.", "The RGMII spec through the real compiler: 7 tests pass natively and 1 invariant is proved comptime -- the 8 ns gigabit period, a nibble on both edges, the 78 ps tap ladder.", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "The OCP MX scale byte as a spec: 2^(e-127), code 255 is NaN, 18 tests and 9 comptime invariants, compiled on a real machine.", "The same scale byte compiled in your browser: seven backends, the spec's tests run in the page, and every skip says why.", "One outlier sets the scale for 32 weights: MXFP4 flushes 23 of them to zero, MXFP8 E4M3 none. 49 tests pass; a planted bug fails one.", "Ternary weights scaled by phi, compiled in your browser: codes 00, 01, 10, and the fourth code folded to zero.", "13 of 13 tests pass natively, and the report says 8 of them executed no assert; a planted bug fails exactly one.", "Five measured rules for a ternary weight alphabet, and the one formula that passes all five, run in your browser.", "One more trit and the spec does not compile at all: BLOCKED, not failed, because no test ever ran.", "A 17-bit float whose exponent is four balanced trits, 3^4 = 81 values, compiled in your browser.", "34 tests pass natively, none vacuous; swap one XOR for OR and only the test that negates twice fails.", "A neuron's sum squeezed back to one trit: 7 tests pass natively, and turning one > into >= fails only the boundary test.", "One ternary neuron, two functions picked by its weights: 12 tests pass natively, and one weight moved from P to Z fails exactly one.", "A neuron wider than 27 inputs, summed chunk by chunk: 11 tests pass natively, and a loop that stops after one chunk fails exactly one.", "A 27-lane ternary dot product as pure wires: 4 tests pass natively, and one wrong sign in tmul fails exactly one.", "The same dot product added into a register on every clock: 4 tests pass natively, and a lane mask short one bit fails exactly one.", "A whole small ternary network, 3 neurons then 2: 4 tests pass natively, and a trit packed one bit low fails exactly one.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "34 vector files, 512 cases: 2 files executed, 8 debt, 24 prose-only; 19% carry data.", "Four census populations recounted another way all agree; three are named as uncheckable.", "31 of 1,428 seals record no output (16 specs); 3 of those are missing from the debt ledger.", "11 specs refused at parse, 0 blocked only by prose, 23 broken on purpose as fixtures.", "12 Lean files, 15,553 lines and 4 sorry, are reached by no build root: nothing compiles them.", "tri, railway, python3 and the loop directory: each present or not, in one call.", "The terminal for seven t27 backends: every command run on hello_world. 21 pass, 0 fail.", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", "", "73 published posts with date, language and revision, newest first.", "6 of 6 self-tests hold: anomalies, blog_drift, lessons, site_gate, tick, vault."]; +pub const WIDGET_HOOKS : [194]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "17 of 17 conformance specs compile, their test blocks pass, and no generated file has drifted.", "14 of 14 bench tools pass their own self-tests in one sweep: decode 18/18, repin 17/17, firelog 10/10, tmpcheck 9/9, stamps 9/9, keep 9/9.", "Requested 115,200 puts 115,385 on the wire (+0.16 %), 921,600 puts 923,077, and 3,000,000 is exact.", "26 checks, no board, no RTL: every receipt returns, 11 loss and fault cases recover, and a lie stops the run.", "One command reads the bench: cables, locks, and the last board runs: three x7-board runs PASS 51840/51840.", "112 files under /tmp pinned by sha256 in specs: every one has a kept copy, 0 lost, 0 unlocated.", "84 board and model logs scanned for the TRI-NET key: 0 hits, 0 logs of 1 MB or more.", "16 spec-named scripts run their own --self-test, 0 bad; 8 without the flag are listed, not skipped silently.", "The part table in far.t27 matches prjxray-db part.json, all 6 reference bitstreams present, bitwalk reads all 6 in 84.4 ms.", "One command re-runs every sweep: CRC 12/12, ECC 32520/32520 frames, 82/82 pins, 4224/4224 segbits, gold 5/5. audit: CLEAN.", "The CDC spec through the real compiler: 12 tests pass natively and the header says what the model does and does not claim.", "The tri clocks card as a spec: constants a tool page reads, and a native test-report that says so plainly -- 0 tests, because it is not a test suite.", "The board itself as a spec: 15 tests pass natively and the part line names the xc7a200tfgg676 the Wukong carries.", "Clock-tree synthesis in numbers: 15 tests pass natively, and the header pins the Artix-7 teaching delays the tree arithmetic uses.", "The MMCM model through the real compiler: VCO window, M/D/O ranges, fractional multiply in eighths, the 1/56 phase step, 16 tests natively.", "Widen the VCO window with one sed and exactly one test objects -- the rejection test that names the 500 MHz config it calls out.", "Static timing as arithmetic: 25 tests pass natively on arcs, slack and Fmax, with the Artix-7 teaching delays in the header.", "The three reset kinds through the real compiler: async assert with sync release through 2 to 4 flops, and the racy async release, 16 tests natively.", "What the compiler emits for the reset pipe: t27c gen prints the Zig, one struct copy per clock, and wc -l counts it.", "Metastability MTBF without floats: the WP323 form in Q10 log2, one flop negative slack, two flops astronomical, 15 tests natively.", "The CDC model as Zig: t27c gen prints what it compiled and wc -l says how many lines of code the model is.", "The FIFO stdlib through the real compiler: 18 tests pass natively and 6 invariants are proved comptime.", "Move a pointer by 3 instead of 1 and every test still passes: the suite asserts fill and flags, never pointer values.", "Flip one flag assignment and a single test objects: the guard that keeps the FIFO honest.", "The pins-to-XDC emitter spec parsed by the real compiler, and the create_clock lines it exists to write.", "The fpga-rxcap tool card as a spec: constants for a tool page, and a native run that says 0 tests and why.", "168 records: 14 papers entered twice, 141 cite no score, 144 state zero at pass@1.", "67 specs lose 23,578 tokens to parser recovery, each held at its pinned bound.", "Smallest hold 3445 ps on TXD1 against a 1000 ps bench; each TX pin setup and hold from the routed SDF.", "Per-bit setup and hold of an async RX nibble against its RXC edge, from the routed SDF: one nibble changes 2387 to 2486 ps apart, so a binary counter tears where a gray one cannot.", "RX_ER pulses injected at exact nibble positions of a ping -- preamble, mid-frame, IP header -- and the frame state each pulse leaves behind, RTL vs model.", "The Ethernet bring-up plan on the bench: pre-registered steps, logged exits, what ran and what did not.", "The registered IO clients on the bench, read live: names, claims and what each declares it owns.", "The bench claim in action: one client takes the hardware lock and the tool reports who holds it.", "The bench lock given back: the client gives up its claim and the bench reports itself free.", "The next queued step on the bench, read live from the run state.", "The memory map spec through the real compiler: 15 tests pass natively and 6 invariants are proved comptime.", "The SPI testbench through the real compiler: 7 tests pass natively, driving the modes and checking the captures.", "t27c check on the UART spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the SPI spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the APB bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the AXI4 spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the bus bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the memory spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "The UART spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The SPI spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The APB bridge spec lowered to synthesizable Verilog: port list, PSEL and PENABLE wiring, the address decode.", "The AXI4 spec lowered to synthesizable Verilog: the five channels' wiring and handshakes as generated wires.", "The bus bridge spec lowered to synthesizable Verilog: both sides of the crossing as generated wires.", "The whole design in one spec lowered to synthesizable Verilog: clocks, resets and bus wiring as the compiler emits them.", "The UART testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The APB bridge testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The AXI4 testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The memory testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The integration testbench spec lowered to Verilog: every bus of the design in one bench.", "The top-level testbench spec lowered to Verilog: the whole design under one bench.", "The SPI testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The UART spec lowered to hardware IR: the compiler's own view of the wires and state before Verilog exists.", "The APB bridge spec lowered to hardware IR: PSEL, PENABLE and the decode as the compiler structures them.", "The AXI4 spec lowered to hardware IR: the five channels and their handshakes as the compiler structures them.", "The Ethernet FCS spec through the real compiler: 8 tests pass natively -- the table derived from one bit step, the 0xCBF43926 check value, the 0xDEBB20E3 receiver residue.", "The RGMII spec through the real compiler: 7 tests pass natively and 1 invariant is proved comptime -- the 8 ns gigabit period, a nibble on both edges, the 78 ps tap ladder.", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "The OCP MX scale byte as a spec: 2^(e-127), code 255 is NaN, 18 tests and 9 comptime invariants, compiled on a real machine.", "The same scale byte compiled in your browser: seven backends, the spec's tests run in the page, and every skip says why.", "One outlier sets the scale for 32 weights: MXFP4 flushes 23 of them to zero, MXFP8 E4M3 none. 49 tests pass; a planted bug fails one.", "Ternary weights scaled by phi, compiled in your browser: codes 00, 01, 10, and the fourth code folded to zero.", "13 of 13 tests pass natively, and the report says 8 of them executed no assert; a planted bug fails exactly one.", "Five measured rules for a ternary weight alphabet, and the one formula that passes all five, run in your browser.", "One more trit and the spec does not compile at all: BLOCKED, not failed, because no test ever ran.", "A 17-bit float whose exponent is four balanced trits, 3^4 = 81 values, compiled in your browser.", "34 tests pass natively, none vacuous; swap one XOR for OR and only the test that negates twice fails.", "A neuron's sum squeezed back to one trit: 7 tests pass natively, and turning one > into >= fails only the boundary test.", "One ternary neuron, two functions picked by its weights: 12 tests pass natively, and one weight moved from P to Z fails exactly one.", "A neuron wider than 27 inputs, summed chunk by chunk: 11 tests pass natively, and a loop that stops after one chunk fails exactly one.", "A 27-lane ternary dot product as pure wires: 4 tests pass natively, and one wrong sign in tmul fails exactly one.", "The same dot product added into a register on every clock: 4 tests pass natively, and a lane mask short one bit fails exactly one.", "A whole small ternary network, 3 neurons then 2: 4 tests pass natively, and a trit packed one bit low fails exactly one.", "A signed multiply: 3 tests pass natively, none vacuous, and a sign stuck at 0 fails exactly one, m2.", "A signed add: 3 tests pass natively, and adding where opposite signs must subtract fails exactly one, a2.", "Two signed products summed: 4 tests pass natively, and OR in place of the sign XOR fails exactly one, pp.", "A ReLU: 4 tests pass natively, and letting negative inputs through fails only negz, the one test with a negative input.", "2^x for a softmax: 4 tests pass natively, and dropping the minus sign of k fails exactly one, em1.", "argmax by comparing bits alone: 4 tests pass natively, and >= in place of > fails exactly one, tie_low.", "A loss in bits: 3 tests pass natively, and a zero with the sign bit set fails exactly one, perfect.", "One SGD step: 3 tests pass natively, and g times g in place of eta times g fails exactly one, ascend.", "A 2-layer ReLU net for XOR: 4 tests pass natively, and one relu removed fails exactly one, x00.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "34 vector files, 512 cases: 2 files executed, 8 debt, 24 prose-only; 19% carry data.", "Four census populations recounted another way all agree; three are named as uncheckable.", "31 of 1,428 seals record no output (16 specs); 3 of those are missing from the debt ledger.", "11 specs refused at parse, 0 blocked only by prose, 23 broken on purpose as fixtures.", "12 Lean files, 15,553 lines and 4 sorry, are reached by no build root: nothing compiles them.", "tri, railway, python3 and the loop directory: each present or not, in one call.", "The terminal for seven t27 backends: every command run on hello_world. 21 pass, 0 fail.", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", "", "73 published posts with date, language and revision, newest first.", "6 of 6 self-tests hold: anomalies, blog_drift, lessons, site_gate, tick, vault."]; ; --- Ideas: asked for, not built --------------------------------------------------------------- ; From what engineers share today (gh-skyline, Tiny Tapeout renders, Wrapped cards, Gatle-style diff --git a/apps/website/src/data/blog/bodies/two-courses-twenty-seven-lessons-each.ts b/apps/website/src/data/blog/bodies/two-courses-twenty-seven-lessons-each.ts index d2e14a36ce..5dd8d29425 100644 --- a/apps/website/src/data/blog/bodies/two-courses-twenty-seven-lessons-each.ts +++ b/apps/website/src/data/blog/bodies/two-courses-twenty-seven-lessons-each.ts @@ -48,6 +48,10 @@ export const body: Block[] = [ kind: 'p', text: 'Recordings pending for lessons 13 to 21. Their nine specs (gft_smul, gft_sadd, gft_signed_mac, gft_relu, gft_exp2, gft_argmax4, gft_nll, gft_sgd_step and gft_xornet) are recorded with tri test and tri mutate plant from t27#7400, which is not merged yet. Until it is, each of those lessons opens a placeholder page that says the recording is pending and shows no run; the lesson text says so too. Lessons 22 to 27 open their recordings now.', }, + { + kind: 'p', + text: 'Update, 9 October 2026: the nine recordings exist now, without #7400, which is still not merged. Each one runs t27c test-report on its spec, where every test passes and none is vacuous, then one sed line that plants the bug in the table below, then t27c test-report again, where exactly the named test fails, and finally git checkout, after which sha256sum prints the hash the spec had before the edit. They were recorded with t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container, on t27 at 72f793357, and every one of the nine failed the test this table names. t27c test-report still exits 0 when a test fails (t27#7370), so the verdict in each recording is read from the report text.', + }, { kind: 'table', head: ['Lesson', 'Spec', 'Tests', 'The planted bug', 'The one test that fails'], @@ -197,6 +201,10 @@ export const ruBody: Block[] = [ kind: 'p', text: 'Записи уроков с 13 по 21 ещё не готовы. Их девяти спекам (gft_smul, gft_sadd, gft_signed_mac, gft_relu, gft_exp2, gft_argmax4, gft_nll, gft_sgd_step и gft_xornet) нужны команды tri test и tri mutate plant из t27#7400, который ещё не смержен. До тех пор каждый из этих уроков открывает страницу-заглушку, где сказано, что запись ещё не готова, и нет никакого запуска; текст урока говорит то же. Уроки с 22 по 27 открывают свои записи уже сейчас.', }, + { + kind: 'p', + text: 'Обновление от 9 октября 2026: девять записей теперь есть, без #7400, который так и не смержен. Каждая запускает t27c test-report на своей спеке, где все тесты проходят и ни один не пустой, затем одну строку sed, которая сажает ошибку из таблицы ниже, затем снова t27c test-report, где падает ровно названный тест, и в конце git checkout, после которого sha256sum печатает тот же хеш, что был у спеки до правки. Записи сделаны с t27c 0.5.1 и zig 0.16.0 в облачном контейнере Claude Code, на t27 в 72f793357, и в каждой из девяти упал именно тот тест, который называет таблица. t27c test-report по-прежнему выходит с кодом 0, когда тест падает (t27#7370), поэтому вердикт в каждой записи читается из текста отчёта.', + }, { kind: 'table', head: ['Урок', 'Спека', 'Тесты', 'Подложенная ошибка', 'Единственный упавший тест'], diff --git a/apps/website/src/lib/course.generated.ts b/apps/website/src/lib/course.generated.ts index 9d2129add9..ad9561e7bd 100644 --- a/apps/website/src/lib/course.generated.ts +++ b/apps/website/src/lib/course.generated.ts @@ -1,5 +1,5 @@ // GENERATED by scripts/course-from-spec.mjs from specs/course/courses.t27, specs/course/t27-basics.t27, specs/course/course.t27, specs/course/ai-numbers.t27, specs/course/goldenfloat.t27, specs/course/verify-hardware.t27, specs/course/clocks-and-cdc.t27, specs/course/buses-and-peripherals.t27 and their Russian bundles -// spec sha256 t27-basics 4827f6b9f5d95ae98e1d49f037966d2cc826e07c691044f2f5fa1ee9c98ae474, course 1425d0e5b804fbc09a6576d9433c875594a90f8c97c4760087e5a660dee5adbe, ai-numbers 60624d355254e75d7a8c210aa5653b3e0debe2eb4df9883c74867454c4a3c9e5, goldenfloat d6b6019b8e6bf77a41991b32a3a3d1b5eb78ca4b7078e2bf5f5436df3dd7c59c, verify-hardware 7ca437575eaa8f8646f42c82b75862f091fb509708de152c4e18ed76ee39d4cf, clocks-and-cdc 7d491967f9326fcde194ab19a9bfa7f0ed960ba94d7ca3d82df0747e269c5acc, buses-and-peripherals 6f2ae91d6a86132ad4ec9562158899bc0f6f1e1a3a77a742af3e30f663077640 +// spec sha256 t27-basics 4827f6b9f5d95ae98e1d49f037966d2cc826e07c691044f2f5fa1ee9c98ae474, course 1425d0e5b804fbc09a6576d9433c875594a90f8c97c4760087e5a660dee5adbe, ai-numbers 7f736e357ef13446dfa8f978482cc28c7710caac3f7396769a8fc01b91e20802, goldenfloat d6b6019b8e6bf77a41991b32a3a3d1b5eb78ca4b7078e2bf5f5436df3dd7c59c, verify-hardware 7ca437575eaa8f8646f42c82b75862f091fb509708de152c4e18ed76ee39d4cf, clocks-and-cdc 7d491967f9326fcde194ab19a9bfa7f0ed960ba94d7ca3d82df0747e269c5acc, buses-and-peripherals 6f2ae91d6a86132ad4ec9562158899bc0f6f1e1a3a77a742af3e30f663077640 // Do not edit: change the .t27 source (or a Russian bundle) and regenerate. export const COURSES = [ @@ -2519,7 +2519,7 @@ export const COURSES = [ "publicSpec": "public/learn/ai-numbers.t27", "ruContract": "specs/course/ai-numbers-ru.t27", "ruBundle": "apps/website/i18n/ai-numbers.ru.json", - "sha256": "60624d355254e75d7a8c210aa5653b3e0debe2eb4df9883c74867454c4a3c9e5", + "sha256": "7f736e357ef13446dfa8f978482cc28c7710caac3f7396769a8fc01b91e20802", "schemaVersion": 1, "gallery": "specs/widgets/gallery.t27" }, @@ -3232,7 +3232,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-smul", "kind": "cast", - "title": "gft_smul.t27: recording pending", + "title": "t27c on gft_smul.t27 -- a sign is one bit, native", "url": "https://t27.ai/term/t27c-gft-smul/", "preview": "term/t27c-gft-smul/index.html", "height": 640 @@ -3247,13 +3247,13 @@ export const COURSES = [ "en": { "title": "A sign is one bit", "goal": "How a signed multiply sets the sign of its product, and which test notices when it never does.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes the sign always 0, and exactly one test fails, m2, which multiplies 1.0 by -1.0. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes the sign always 0, and t27c test-report again: exactly one test fails, m2, which multiplies 1.0 by -1.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the test that fails; then in the spec frame find m1 and m3 and work out why a sign that is always 0 cannot break them." }, "ru": { "title": "Знак — это один бит", "goal": "Как умножение чисел со знаком ставит знак произведения и какой тест замечает, когда знак не ставится никогда.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Модули с 1 по 4 были о том, как хранить числа: ячейка T27, байт масштаба OCP MX, тернарные веса с масштабом фи и TNF; этот модуль заставляет их считать. gft_smul.t27 использует биты tnf17.t27: 1.0 — это 20480, а -1.0 — это 86016, те же биты плюс бит знака 65536. smul ставит знак, когда знаки входов различаются, а величину отдаёт magmul. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись делает знак всегда равным 0, и падает ровно один тест, m2, который умножает 1.0 на -1.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "Модули с 1 по 4 были о том, как хранить числа: ячейка T27, байт масштаба OCP MX, тернарные веса с масштабом фи и TNF; этот модуль заставляет их считать. gft_smul.t27 использует биты tnf17.t27: 1.0 — это 20480, а -1.0 — это 86016, те же биты плюс бит знака 65536. smul ставит знак, когда знаки входов различаются, а величину отдаёт magmul. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который делает знак всегда равным 0, и снова t27c test-report: падает ровно один тест, m2, который умножает 1.0 на -1.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке m1 и m3 и поймите, почему знак, который всегда равен 0, не может их сломать." } }, @@ -3264,7 +3264,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-sadd", "kind": "cast", - "title": "gft_sadd.t27: recording pending", + "title": "t27c on gft_sadd.t27 -- opposite signs subtract, native", "url": "https://t27.ai/term/t27c-gft-sadd/", "preview": "term/t27c-gft-sadd/index.html", "height": 640 @@ -3279,13 +3279,13 @@ export const COURSES = [ "en": { "title": "Opposite signs subtract", "goal": "How a signed add turns into a subtraction when the signs differ, and what its 3 tests leave unchecked.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording adds the sizes in the branch a2 takes, and exactly one test fails, a2. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording runs t27c test-report, then a one-line sed that adds the sizes in the branch a2 takes, and t27c test-report again: exactly one test fails, a2. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the value a2 expects; then in the spec frame find bsign and check whether a1, a2 or a3 ever depends on it." }, "ru": { "title": "Разные знаки — вычитание", "goal": "Как сложение чисел со знаком превращается в вычитание, когда знаки разные, и что его 3 теста оставляют непроверенным.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. sadd в gft_sadd.t27 складывает два числа одного знака через magadd. Когда знаки разные, она вычитает меньшую величину из большей через magsub, берёт знак большего и при точном сокращении возвращает 0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Знаки смешивает только a2, 1.0 плюс -1.0, и они сокращаются: magsub сразу возвращает 0, потому что величины равны. Ни один тест не вычитает две разные величины, поэтому знак большего ни разу не решает результат. Запись складывает величины в той ветке, куда попадает a2, и падает ровно один тест, a2. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "sadd в gft_sadd.t27 складывает два числа одного знака через magadd. Когда знаки разные, она вычитает меньшую величину из большей через magsub, берёт знак большего и при точном сокращении возвращает 0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Знаки смешивает только a2, 1.0 плюс -1.0, и они сокращаются: magsub сразу возвращает 0, потому что величины равны. Ни один тест не вычитает две разные величины, поэтому знак большего ни разу не решает результат. Запись запускает t27c test-report, затем однострочный sed, который складывает величины в той ветке, куда попадает a2, и снова t27c test-report: падает ровно один тест, a2. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и значение, которого ждёт a2; затем найдите в спеке bsign и проверьте, зависит ли от него хоть один из a1, a2 и a3." } }, @@ -3296,7 +3296,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-signed-mac", "kind": "cast", - "title": "gft_signed_mac.t27: recording pending", + "title": "t27c on gft_signed_mac.t27 -- multiply, then add, native", "url": "https://t27.ai/term/t27c-gft-signed-mac/", "preview": "term/t27c-gft-signed-mac/index.html", "height": 640 @@ -3311,13 +3311,13 @@ export const COURSES = [ "en": { "title": "Multiply, then add", "goal": "What a multiply-accumulate does with two signed products, and how the swap from lesson 12 breaks it.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording repeats the bug of lesson 12 and swaps XOR for OR. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording runs t27c test-report, then repeats the bug of lesson 12 with a one-line sed that swaps XOR for OR, and runs t27c test-report again. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the test that expects 512 and the test the planted bug breaks; then in the spec frame change the XOR in smul to OR yourself and see whether the browser catches it." }, "ru": { "title": "Умножить, потом сложить", "goal": "Что умножение с накоплением делает с двумя произведениями со знаком и как его ломает замена из урока 12.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Нейрон суммирует произведения весов на входы, и on_comb в gft_signed_mac.t27 — такая сумма из двух слагаемых: smul(a1, b1) плюс smul(a2, b2). Здесь smul ставит знак через XOR и не проверяет ноль. Нативный t27c запускает 4 теста, и все проходят; один из них, zero_clause, ждёт, что 0 x 1 + 0 x 1 даст 512, то есть 2^-39, а не 0, и комментарий называет это задуманным поведением. Браузер пропускает свои тесты, потому что его исполнитель пока не знает assert_eq. Запись повторяет ошибку урока 12 и меняет XOR на OR. Падает ровно один тест, pp: (-1.0) x (-1.0) теперь выходит отрицательным, и сумма равна 0 вместо 2.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "Нейрон суммирует произведения весов на входы, и on_comb в gft_signed_mac.t27 — такая сумма из двух слагаемых: smul(a1, b1) плюс smul(a2, b2). Здесь smul ставит знак через XOR и не проверяет ноль. Нативный t27c запускает 4 теста, и все проходят; один из них, zero_clause, ждёт, что 0 x 1 + 0 x 1 даст 512, то есть 2^-39, а не 0, и комментарий называет это задуманным поведением. Браузер пропускает свои тесты, потому что его исполнитель пока не знает assert_eq. Запись запускает t27c test-report, затем повторяет ошибку урока 12 однострочным sed, который меняет XOR на OR, и снова запускает t27c test-report. Падает ровно один тест, pp: (-1.0) x (-1.0) теперь выходит отрицательным, и сумма равна 0 вместо 2.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи тест, который ждёт 512, и тест, который ломает подложенная ошибка; затем сами замените в спеке XOR в smul на OR и посмотрите, поймает ли это браузер." } }, @@ -3328,7 +3328,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-relu", "kind": "cast", - "title": "gft_relu.t27: recording pending", + "title": "t27c on gft_relu.t27 -- a bend at zero, native", "url": "https://t27.ai/term/t27c-gft-relu/", "preview": "term/t27c-gft-relu/index.html", "height": 640 @@ -3343,13 +3343,13 @@ export const COURSES = [ "en": { "title": "A bend at zero", "goal": "Why a ReLU needs a test with a negative input before anyone can say it bends.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording lets negative inputs through, which turns relu into the identity, a straight line. Exactly one test fails, negz; no other test has a negative input. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that lets negative inputs through, which turns relu into the identity, a straight line, and t27c test-report again. Exactly one test fails, negz; no other test has a negative input. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the value negz expects; then in the spec frame find the comment on that line and check whether it still describes the code after the change." }, "ru": { "title": "Излом в нуле", "goal": "Почему ReLU нужен тест с отрицательным входом, прежде чем можно сказать, что у неё есть излом.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Нейрон пропускает свою сумму через функцию активации, и в gft_relu.t27 это relu(x) = max(0, x). on_comb возвращает 0 для нуля и для любого входа с битом знака, а иначе сам x. Заголовок говорит, что у ReLU точный градиент 0/1 и что 2-слойная сеть с ReLU решает XOR, а линейная модель GF-T этого не может. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись пропускает отрицательные входы, и relu становится тождеством, прямой линией. Падает ровно один тест, negz; больше ни у одного теста нет отрицательного входа. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "Нейрон пропускает свою сумму через функцию активации, и в gft_relu.t27 это relu(x) = max(0, x). on_comb возвращает 0 для нуля и для любого входа с битом знака, а иначе сам x. Заголовок говорит, что у ReLU точный градиент 0/1 и что 2-слойная сеть с ReLU решает XOR, а линейная модель GF-T этого не может. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который пропускает отрицательные входы, и relu становится тождеством, прямой линией, и снова t27c test-report. Падает ровно один тест, negz; больше ни у одного теста нет отрицательного входа. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и значение, которого ждёт negz; затем найдите в спеке комментарий на этой строке и проверьте, описывает ли он код после замены." } }, @@ -3360,7 +3360,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-exp2", "kind": "cast", - "title": "gft_exp2.t27: recording pending", + "title": "t27c on gft_exp2.t27 -- two to the x, native", "url": "https://t27.ai/term/t27c-gft-exp2/", "preview": "term/t27c-gft-exp2/index.html", "height": 640 @@ -3375,13 +3375,13 @@ export const COURSES = [ "en": { "title": "Two to the x", "goal": "How exp2 splits x into a whole part and a fraction, and why its tests only reach the whole part.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording drops the minus sign of k for negative x, and exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that drops the minus sign of k for negative x, and t27c test-report again: exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the value em1 expects; then in the spec frame find pow2_frac and work out what it returns when f is 0." }, "ru": { "title": "Два в степени x", "goal": "Как exp2 делит x на целую часть и дробь и почему её тесты доходят только до целой части.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Заголовок gft_exp2.t27 называет exp2 недостающим кирпичиком для softmax на GF-T. on_comb делит x на целую часть k и дробь f: k задаёт смещение, k + 40, а pow2_frac превращает f в мантиссу. Заголовок обещает ошибку не больше 1 ULP, измеренную в прототипе, но все 4 теста подают целые входы, 0, 1.0, -1.0 и 2.0, поэтому f равна 0, и pow2_frac возвращает 0 при любых коэффициентах. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись теряет минус у k для отрицательного x, и падает ровно один тест, em1: 2^-1 выходит как 2.0, а не 0.5. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "Заголовок gft_exp2.t27 называет exp2 недостающим кирпичиком для softmax на GF-T. on_comb делит x на целую часть k и дробь f: k задаёт смещение, k + 40, а pow2_frac превращает f в мантиссу. Заголовок обещает ошибку не больше 1 ULP, измеренную в прототипе, но все 4 теста подают целые входы, 0, 1.0, -1.0 и 2.0, поэтому f равна 0, и pow2_frac возвращает 0 при любых коэффициентах. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который теряет минус у k для отрицательного x, и снова t27c test-report: падает ровно один тест, em1: 2^-1 выходит как 2.0, а не 0.5. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и значение, которого ждёт em1; затем найдите в спеке pow2_frac и выясните, что она возвращает, когда f равна 0." } }, @@ -3392,7 +3392,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-argmax4", "kind": "cast", - "title": "gft_argmax4.t27: recording pending", + "title": "t27c on gft_argmax4.t27 -- pick the largest, native", "url": "https://t27.ai/term/t27c-gft-argmax4/", "preview": "term/t27c-gft-argmax4/index.html", "height": 640 @@ -3407,13 +3407,13 @@ export const COURSES = [ "en": { "title": "Pick the largest", "goal": "How argmax names a class by comparing bits alone, and which rule settles a tie.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording turns > into >= for two positive scores, and exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that turns > into >= for two positive scores, and t27c test-report again: exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the test that fails; then in the spec frame find the line that compares two negative scores and the one test that reaches it." }, "ru": { "title": "Выбрать наибольшее", "goal": "Как argmax называет класс, сравнивая одни только биты, и какое правило решает ничью.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Классификатор в конце называет класс с наибольшей оценкой. gft_argmax4.t27 делает это без арифметики: gt ставит отрицательные ниже нуля, ноль ниже положительных, а затем сравнивает младшие 16 бит, растущие с величиной. По заголовку ничью выигрывает меньший индекс, сравнение строгое >. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись меняет > на >= для двух положительных оценок, и падает ровно один тест, tie_low: четыре оценки 1.0 теперь выбирают индекс 3, а не 0. Ни один тест не даёт решить ответ двум отрицательным оценкам: переворот их сравнения ничего не роняет. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "Классификатор в конце называет класс с наибольшей оценкой. gft_argmax4.t27 делает это без арифметики: gt ставит отрицательные ниже нуля, ноль ниже положительных, а затем сравнивает младшие 16 бит, растущие с величиной. По заголовку ничью выигрывает меньший индекс, сравнение строгое >. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который меняет > на >= для двух положительных оценок, и снова t27c test-report: падает ровно один тест, tie_low: четыре оценки 1.0 теперь выбирают индекс 3, а не 0. Ни один тест не даёт решить ответ двум отрицательным оценкам: переворот их сравнения ничего не роняет. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке строку, которая сравнивает две отрицательные оценки, и тот один тест, который до неё доходит." } }, @@ -3424,7 +3424,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-nll", "kind": "cast", - "title": "gft_nll.t27: recording pending", + "title": "t27c on gft_nll.t27 -- a loss in bits, native", "url": "https://t27.ai/term/t27c-gft-nll/", "preview": "term/t27c-gft-nll/index.html", "height": 640 @@ -3439,13 +3439,13 @@ export const COURSES = [ "en": { "title": "A loss in bits", "goal": "How the loss -log2(p) prices a guess in bits, and why a perfect guess must cost exactly 0.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes neg return 65536 for 0, a zero with the sign bit set, and exactly one test fails, perfect. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that makes neg return 65536 for 0, a zero with the sign bit set, and t27c test-report again: exactly one test fails, perfect. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the test that fails; then in the spec frame find the header line that says the output is log2(x) and compare it with on_comb." }, "ru": { "title": "Потеря в битах", "goal": "Как потеря -log2(p) оценивает догадку в битах и почему безошибочная догадка должна стоить ровно 0.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Обучение начинается с числа, которое говорит, насколько неверной была догадка. gft_nll.t27 берёт p, вероятность, которую модель дала верному классу, и возвращает -log2(p): p = 1.0 стоит 0, p = 0.5 стоит 1 бит, а p = 0.25 стоит 2. on_comb — это neg(log2v(p)), и neg переворачивает бит знака, но для 0 возвращает 0. Одна строка заголовка всё ещё говорит, что на выходе log2(x); код возвращает его с обратным знаком. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись заставляет neg возвращать 65536 для 0, ноль с поставленным битом знака, и падает ровно один тест, perfect. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "Обучение начинается с числа, которое говорит, насколько неверной была догадка. gft_nll.t27 берёт p, вероятность, которую модель дала верному классу, и возвращает -log2(p): p = 1.0 стоит 0, p = 0.5 стоит 1 бит, а p = 0.25 стоит 2. on_comb — это neg(log2v(p)), и neg переворачивает бит знака, но для 0 возвращает 0. Одна строка заголовка всё ещё говорит, что на выходе log2(x); код возвращает его с обратным знаком. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который заставляет neg возвращать 65536 для 0, ноль с поставленным битом знака, и снова t27c test-report: падает ровно один тест, perfect. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке строку заголовка, где сказано, что на выходе log2(x), и сравните её с on_comb." } }, @@ -3456,7 +3456,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-sgd-step", "kind": "cast", - "title": "gft_sgd_step.t27: recording pending", + "title": "t27c on gft_sgd_step.t27 -- one step downhill, native", "url": "https://t27.ai/term/t27c-gft-sgd-step/", "preview": "term/t27c-gft-sgd-step/index.html", "height": 640 @@ -3471,13 +3471,13 @@ export const COURSES = [ "en": { "title": "One step downhill", "goal": "How one SGD step moves a weight against its gradient, and why a test with two equal inputs cannot tell them apart.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording multiplies g by itself instead of by eta. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that multiplies g by itself instead of by eta, and t27c test-report again. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the test that fails; then in the spec frame find step and decide which of its inputs must change for it to catch the bug." }, "ru": { "title": "Один шаг вниз по склону", "goal": "Как один шаг SGD сдвигает вес против градиента и почему тест с двумя равными входами не может их различить.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. gft_sgd_step.t27 сдвигает вес против градиента: w' = w - eta*g, где eta — скорость обучения. Её 3 теста: step, где w = 1.0, g = 0.5 и eta = 0.5; nograd, где g = 0 оставляет w как есть; и ascend, где g = -1.0 и eta = 1.0 поднимают w до 2.0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись умножает g на само себя вместо eta. В step g равно eta, а в nograd оба произведения равны 0. Поэтому падает ровно один тест, ascend, где вес падает до 0, вместо того чтобы подняться до 2.0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "gft_sgd_step.t27 сдвигает вес против градиента: w' = w - eta*g, где eta — скорость обучения. Её 3 теста: step, где w = 1.0, g = 0.5 и eta = 0.5; nograd, где g = 0 оставляет w как есть; и ascend, где g = -1.0 и eta = 1.0 поднимают w до 2.0. Браузер пропускает все 3 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 3, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который умножает g на само себя вместо eta, и снова t27c test-report. В step g равно eta, а в nograd оба произведения равны 0. Поэтому падает ровно один тест, ascend, где вес падает до 0, вместо того чтобы подняться до 2.0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и упавший тест; затем найдите в спеке step и решите, какой из его входов нужно изменить, чтобы он поймал ошибку." } }, @@ -3488,7 +3488,7 @@ export const COURSES = [ "widget": { "id": "t27c-gft-xornet", "kind": "cast", - "title": "gft_xornet.t27: recording pending", + "title": "t27c on gft_xornet.t27 -- XOR needs a bend, native", "url": "https://t27.ai/term/t27c-gft-xornet/", "preview": "term/t27c-gft-xornet/index.html", "height": 640 @@ -3503,13 +3503,13 @@ export const COURSES = [ "en": { "title": "XOR needs a bend", "goal": "Why a network needs a hidden ReLU layer to compute XOR, and what one missing bend does.", - "text": "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording removes relu from h1 alone, and exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. Every byte in the recording was printed by the command; only the typing is staged.", + "text": "XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording runs t27c test-report, then a one-line sed that removes relu from h1 alone, and t27c test-report again: exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. git checkout puts the line back, and sha256sum prints the same hash as before the edit. Every byte in the recording was printed by the command; only the typing is staged.", "task": "In the recording, find the changed line and the test that fails; then in the spec frame work out z1 for x10, x01 and x11 and see why relu changes nothing there." }, "ru": { "title": "XOR нужен излом", "goal": "Почему сети нужен скрытый слой с ReLU, чтобы вычислить XOR, и что делает один пропавший излом.", - "text": "Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. XOR равен 1, когда ровно один вход равен 1, и комментарий в gft_xornet.t27 говорит, что одиночная линейная модель не может его разделить. on_comb — 2-слойная сеть: две скрытые суммы, z0 и z1, каждая через relu, затем взвешенная сумма h0 и h1. Она не обучается: тесты подают веса, заданные вручную, аналитическое решение W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись убирает relu только у h1, и падает ровно один тест, x00: в (0,0) z1 равно -1.0, и выход становится 2.0 вместо 0. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "text": "XOR равен 1, когда ровно один вход равен 1, и комментарий в gft_xornet.t27 говорит, что одиночная линейная модель не может его разделить. on_comb — 2-слойная сеть: две скрытые суммы, z0 и z1, каждая через relu, затем взвешенная сумма h0 и h1. Она не обучается: тесты подают веса, заданные вручную, аналитическое решение W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись запускает t27c test-report, затем однострочный sed, который убирает relu только у h1, и снова t27c test-report: падает ровно один тест, x00: в (0,0) z1 равно -1.0, и выход становится 2.0 вместо 0. git checkout возвращает строку на место, и sha256sum печатает тот же хеш, что и до правки. Каждый байт в записи напечатала команда; инсценирован только набор текста.", "task": "Найдите в записи изменённую строку и упавший тест; затем вычислите в спеке z1 для x10, x01 и x11 и поймите, почему relu там ничего не меняет." } }, diff --git a/apps/website/src/lib/queenWidgets.generated.ts b/apps/website/src/lib/queenWidgets.generated.ts index d61184535c..4078bc8b8f 100644 --- a/apps/website/src/lib/queenWidgets.generated.ts +++ b/apps/website/src/lib/queenWidgets.generated.ts @@ -1,12 +1,12 @@ // GENERATED by scripts/widgets-from-spec.mjs from specs/widgets/gallery.t27 -// spec sha256 259d159c4d86a5e01c72681db4266cd7a368308f5ef3727119475d9d30ddd413 +// spec sha256 3ea7df0d182a10a122d0548c54466e14570dbdd89757f6ad13dc5fa947d65e7c // Do not edit: change the .t27 source and regenerate. export const QUEEN_WIDGETS = { "source": { "spec": "specs/widgets/gallery.t27", "publicSpec": "public/widgets/gallery.t27", - "sha256": "259d159c4d86a5e01c72681db4266cd7a368308f5ef3727119475d9d30ddd413", + "sha256": "3ea7df0d182a10a122d0548c54466e14570dbdd89757f6ad13dc5fa947d65e7c", "schemaVersion": 1 }, "name": "t27 widgets", @@ -2294,154 +2294,226 @@ export const QUEEN_WIDGETS = { "id": "t27c-gft-smul", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_smul.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "A signed multiply: 3 tests pass natively, none vacuous, and a sign stuck at 0 fails exactly one, m2.", + "title": "t27c on gft_smul.t27 -- a sign is one bit, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; a sign that is always 0 makes exactly one test fail, m2; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-smul/", "view": null, "image": "https://t27.ai/term/t27c-gft-smul/card.png", "gif": null, "preview": "term/t27c-gft-smul/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_smul.t27: recording pending](https://t27.ai/term/t27c-gft-smul/card.png)](https://t27.ai/term/t27c-gft-smul/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_smul.t27 && sed -n '110p;121p' specs/ternary/gft_smul.t27", + "t27c test-report specs/ternary/gft_smul.t27", + "sed -i 's/if (sgn != sb) { sgn = 1; } else { sgn = 0; }/sgn = 0;/' specs/ternary/gft_smul.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_smul.t27", + "git checkout specs/ternary/gft_smul.t27 && sha256sum specs/ternary/gft_smul.t27" + ], + "recorded": "2026-10-09 08:43 UTC", + "iframe": "", + "markdown": "[![t27c on gft_smul.t27 -- a sign is one bit, native](https://t27.ai/term/t27c-gft-smul/card.png)](https://t27.ai/term/t27c-gft-smul/)" }, { "id": "t27c-gft-sadd", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_sadd.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "A signed add: 3 tests pass natively, and adding where opposite signs must subtract fails exactly one, a2.", + "title": "t27c on gft_sadd.t27 -- opposite signs subtract, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; adding the sizes where opposite signs must subtract makes exactly one test fail, a2; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-sadd/", "view": null, "image": "https://t27.ai/term/t27c-gft-sadd/card.png", "gif": null, "preview": "term/t27c-gft-sadd/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_sadd.t27: recording pending](https://t27.ai/term/t27c-gft-sadd/card.png)](https://t27.ai/term/t27c-gft-sadd/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_sadd.t27 && sed -n '75p;120p' specs/ternary/gft_sadd.t27", + "t27c test-report specs/ternary/gft_sadd.t27", + "sed -i 's/} else { r = magsub(ma, mb); }/} else { r = magadd(ma, mb); }/' specs/ternary/gft_sadd.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_sadd.t27", + "git checkout specs/ternary/gft_sadd.t27 && sha256sum specs/ternary/gft_sadd.t27" + ], + "recorded": "2026-10-09 08:43 UTC", + "iframe": "", + "markdown": "[![t27c on gft_sadd.t27 -- opposite signs subtract, native](https://t27.ai/term/t27c-gft-sadd/card.png)](https://t27.ai/term/t27c-gft-sadd/)" }, { "id": "t27c-gft-signed-mac", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_signed_mac.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "Two signed products summed: 4 tests pass natively, and OR in place of the sign XOR fails exactly one, pp.", + "title": "t27c on gft_signed_mac.t27 -- multiply, then add, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; OR in place of the sign XOR makes exactly one test fail, pp; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-signed-mac/", "view": null, "image": "https://t27.ai/term/t27c-gft-signed-mac/card.png", "gif": null, "preview": "term/t27c-gft-signed-mac/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_signed_mac.t27: recording pending](https://t27.ai/term/t27c-gft-signed-mac/card.png)](https://t27.ai/term/t27c-gft-signed-mac/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_signed_mac.t27 && sed -n '109p;138p;155p' specs/ternary/gft_signed_mac.t27", + "t27c test-report specs/ternary/gft_signed_mac.t27", + "sed -i 's/(sa ^ sb)/(sa | sb)/' specs/ternary/gft_signed_mac.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_signed_mac.t27", + "git checkout specs/ternary/gft_signed_mac.t27 && sha256sum specs/ternary/gft_signed_mac.t27" + ], + "recorded": "2026-10-09 08:43 UTC", + "iframe": "", + "markdown": "[![t27c on gft_signed_mac.t27 -- multiply, then add, native](https://t27.ai/term/t27c-gft-signed-mac/card.png)](https://t27.ai/term/t27c-gft-signed-mac/)" }, { "id": "t27c-gft-relu", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_relu.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "A ReLU: 4 tests pass natively, and letting negative inputs through fails only negz, the one test with a negative input.", + "title": "t27c on gft_relu.t27 -- a bend at zero, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; letting negative inputs through makes exactly one test fail, negz; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-relu/", "view": null, "image": "https://t27.ai/term/t27c-gft-relu/card.png", "gif": null, "preview": "term/t27c-gft-relu/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_relu.t27: recording pending](https://t27.ai/term/t27c-gft-relu/card.png)](https://t27.ai/term/t27c-gft-relu/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_relu.t27 && sed -n '12p;19p' specs/ternary/gft_relu.t27", + "t27c test-report specs/ternary/gft_relu.t27", + "sed -i 's/== 1) { return 0; }/== 1) { return x; }/' specs/ternary/gft_relu.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_relu.t27", + "git checkout specs/ternary/gft_relu.t27 && sha256sum specs/ternary/gft_relu.t27" + ], + "recorded": "2026-10-09 08:43 UTC", + "iframe": "", + "markdown": "[![t27c on gft_relu.t27 -- a bend at zero, native](https://t27.ai/term/t27c-gft-relu/card.png)](https://t27.ai/term/t27c-gft-relu/)" }, { "id": "t27c-gft-exp2", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_exp2.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "2^x for a softmax: 4 tests pass natively, and dropping the minus sign of k fails exactly one, em1.", + "title": "t27c on gft_exp2.t27 -- two to the x, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; dropping the minus sign of k makes exactly one test fail, em1; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-exp2/", "view": null, "image": "https://t27.ai/term/t27c-gft-exp2/card.png", "gif": null, "preview": "term/t27c-gft-exp2/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_exp2.t27: recording pending](https://t27.ai/term/t27c-gft-exp2/card.png)](https://t27.ai/term/t27c-gft-exp2/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_exp2.t27 && sed -n '49p;64p' specs/ternary/gft_exp2.t27", + "t27c test-report specs/ternary/gft_exp2.t27", + "sed -i 's/k = 0 - ki; f = 0;/k = ki; f = 0;/' specs/ternary/gft_exp2.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_exp2.t27", + "git checkout specs/ternary/gft_exp2.t27 && sha256sum specs/ternary/gft_exp2.t27" + ], + "recorded": "2026-10-09 08:44 UTC", + "iframe": "", + "markdown": "[![t27c on gft_exp2.t27 -- two to the x, native](https://t27.ai/term/t27c-gft-exp2/card.png)](https://t27.ai/term/t27c-gft-exp2/)" }, { "id": "t27c-gft-argmax4", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_argmax4.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "argmax by comparing bits alone: 4 tests pass natively, and >= in place of > fails exactly one, tie_low.", + "title": "t27c on gft_argmax4.t27 -- pick the largest, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; >= in place of > for two positive scores makes exactly one test fail, tie_low; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-argmax4/", "view": null, "image": "https://t27.ai/term/t27c-gft-argmax4/card.png", "gif": null, "preview": "term/t27c-gft-argmax4/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_argmax4.t27: recording pending](https://t27.ai/term/t27c-gft-argmax4/card.png)](https://t27.ai/term/t27c-gft-argmax4/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_argmax4.t27 && sed -n '28p;50p' specs/ternary/gft_argmax4.t27", + "t27c test-report specs/ternary/gft_argmax4.t27", + "sed -i 's/ma > mb/ma >= mb/' specs/ternary/gft_argmax4.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_argmax4.t27", + "git checkout specs/ternary/gft_argmax4.t27 && sha256sum specs/ternary/gft_argmax4.t27" + ], + "recorded": "2026-10-09 08:44 UTC", + "iframe": "", + "markdown": "[![t27c on gft_argmax4.t27 -- pick the largest, native](https://t27.ai/term/t27c-gft-argmax4/card.png)](https://t27.ai/term/t27c-gft-argmax4/)" }, { "id": "t27c-gft-nll", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_nll.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "A loss in bits: 3 tests pass natively, and a zero with the sign bit set fails exactly one, perfect.", + "title": "t27c on gft_nll.t27 -- a loss in bits, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; a zero with the sign bit set from neg makes exactly one test fail, perfect; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-nll/", "view": null, "image": "https://t27.ai/term/t27c-gft-nll/card.png", "gif": null, "preview": "term/t27c-gft-nll/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_nll.t27: recording pending](https://t27.ai/term/t27c-gft-nll/card.png)](https://t27.ai/term/t27c-gft-nll/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_nll.t27 && sed -n '88p;97p' specs/ternary/gft_nll.t27", + "t27c test-report specs/ternary/gft_nll.t27", + "sed -i 's/if (v == 0) { return 0; }/if (v == 0) { return 65536; }/' specs/ternary/gft_nll.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_nll.t27", + "git checkout specs/ternary/gft_nll.t27 && sha256sum specs/ternary/gft_nll.t27" + ], + "recorded": "2026-10-09 08:44 UTC", + "iframe": "", + "markdown": "[![t27c on gft_nll.t27 -- a loss in bits, native](https://t27.ai/term/t27c-gft-nll/card.png)](https://t27.ai/term/t27c-gft-nll/)" }, { "id": "t27c-gft-sgd-step", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_sgd_step.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "One SGD step: 3 tests pass natively, and g times g in place of eta times g fails exactly one, ascend.", + "title": "t27c on gft_sgd_step.t27 -- one step downhill, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 3 tests pass natively, none vacuous; g times g in place of eta times g makes exactly one test fail, ascend; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-sgd-step/", "view": null, "image": "https://t27.ai/term/t27c-gft-sgd-step/card.png", "gif": null, "preview": "term/t27c-gft-sgd-step/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_sgd_step.t27: recording pending](https://t27.ai/term/t27c-gft-sgd-step/card.png)](https://t27.ai/term/t27c-gft-sgd-step/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_sgd_step.t27 && sed -n '118p;127p' specs/ternary/gft_sgd_step.t27", + "t27c test-report specs/ternary/gft_sgd_step.t27", + "sed -i 's/smul(eta, g)/smul(g, g)/' specs/ternary/gft_sgd_step.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_sgd_step.t27", + "git checkout specs/ternary/gft_sgd_step.t27 && sha256sum specs/ternary/gft_sgd_step.t27" + ], + "recorded": "2026-10-09 08:44 UTC", + "iframe": "", + "markdown": "[![t27c on gft_sgd_step.t27 -- one step downhill, native](https://t27.ai/term/t27c-gft-sgd-step/card.png)](https://t27.ai/term/t27c-gft-sgd-step/)" }, { "id": "t27c-gft-xornet", "kind": "cast", "category": "compiler", - "hook": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", - "title": "gft_xornet.t27: recording pending", - "line": "Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.", + "hook": "A 2-layer ReLU net for XOR: 4 tests pass natively, and one relu removed fails exactly one, x00.", + "title": "t27c on gft_xornet.t27 -- XOR needs a bend, native", + "line": "t27c 0.5.1 and zig 0.16.0 in a Claude Code cloud container (Linux x86_64), spec at t27 72f793357: all 4 tests pass natively, none vacuous; removing relu from h1 makes exactly one test fail, x00; git restores the spec.", "url": "https://t27.ai/term/t27c-gft-xornet/", "view": null, "image": "https://t27.ai/term/t27c-gft-xornet/card.png", "gif": null, "preview": "term/t27c-gft-xornet/index.html", - "commands": [], - "recorded": null, - "iframe": "", - "markdown": "[![gft_xornet.t27: recording pending](https://t27.ai/term/t27c-gft-xornet/card.png)](https://t27.ai/term/t27c-gft-xornet/)" + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sha256sum specs/ternary/gft_xornet.t27 && sed -n '137p;142p' specs/ternary/gft_xornet.t27", + "t27c test-report specs/ternary/gft_xornet.t27", + "sed -i 's/relu(z1)/z1/' specs/ternary/gft_xornet.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/ternary/gft_xornet.t27", + "git checkout specs/ternary/gft_xornet.t27 && sha256sum specs/ternary/gft_xornet.t27" + ], + "recorded": "2026-10-09 08:44 UTC", + "iframe": "", + "markdown": "[![t27c on gft_xornet.t27 -- XOR needs a bend, native](https://t27.ai/term/t27c-gft-xornet/card.png)](https://t27.ai/term/t27c-gft-xornet/)" }, { "id": "basics-what-a-spec-is",