Skip to content

fix(ci): judge the headless test graph instead of piping it through tee (Refs #989) - #1207

Merged
dmitrii-f-t27 merged 1 commit into
mainfrom
spec/trinity-s12-headless
Oct 1, 2026
Merged

dmitrii-f-t27 merged 1 commit into
mainfrom
spec/trinity-s12-headless

Conversation

@dmitrii-f-t27

Copy link
Copy Markdown
Collaborator

S12 of #988, first part (Refs #989; Refs #616 -- this supersedes the tee fix of #987 by judging the result instead of only propagating it).

What was wrong

ci.yml ran the tests as zig build test -Dci=true 2>&1 | tee test-output.txt under bash without pipefail, so the step's exit code was tee's. On main (afc9d38) the test graph does not compile five of its roots, and every CI run reported that as a success. The summary line says it plainly and nothing read it:

Build Summary: 124/135 steps succeeded; 5 failed; 2510/2510 tests passed

Measured twice with Zig 0.15.2: the Build & Test job of CI run 36875247882 (Linux x86_64) and a Linux aarch64 container with empty caches. The five roots, and what the compiler says:

test root error capability card (S01 manifest)
src/trinity.zig -- the library's own test root src/hybrid.zig:4, src/vsa.zig:7: no module named zig-hdc-vsa (the test module provides it as hdc_vsa); src/science.zig:53,143,161,179,204: expected optional type, found [59049]i8 trinity/lib.trinity
src/vm.zig src/vm.zig:1150: expected optional type, found [59049]i8; src/vsa_jit.zig:117: member function expected 1 argument, found 2 trinity/lib.trinity
src/queen_api.zig the C allocator without libc (4 errors): its test module does not set link_libc, as the main test module does trinity/queen.lib
src/c_api.zig src/c_api.zig:270: expected statement, found a document comment trinity/abi.c-api
src/e2e_test.zig imports packed_trit.zig, removed in 42490a2 (#517) trinity/examples.zig

One more run step compiles and runs no test at all (external/zig-hslm/src/root.zig has no test declaration).

What this adds

  • specs/reproduce/headless.t27 -- the headless profile as measured: Zig 0.15.2, the two commands, the four dependency pins of build.zig.zon, the 46 files the build installs (45 executables and libtrinity.a), the 67 test roots the test step compiles, the floors the graph may not drop below (135 steps, 61 run steps with tests, 2510 tests passed, at most 1 empty run step), and the five blocked roots, each with its error and the card it holds back.
  • tools/reproduce.py -- the documented clean-clone command, python3 tools/reproduce.py headless. It points ZIG_GLOBAL_CACHE_DIR and ZIG_LOCAL_CACHE_DIR at a new empty directory, runs the build and the test step, reads each exit code from the process, and judges: exactly the blocked roots may fail (a sixth fails the gate, and so does a blocked root that compiles again -- take it off the list); a failed test, a leak, a failure the output does not name, a missing install, a changed dependency pin, fewer tests, fewer run steps or a declared root that left the graph fail it. Every install is hashed into the record it writes. --self-check plants fifteen defects (the swallowed exit code of CI: main has no build.zig matching its own source tree (2217 files vs a build script from March) #616 among them) and shows each caught. Standard library only.
  • ci.yml: the Test step is now python3 tools/reproduce.py test --reuse-cache -- the same judge over the same test graph, the leak check kept. CI stays green on main only because the five failures are named, with reasons, in a file a reviewer can read; a new one turns it red.
  • .github/workflows/reproduce.yml: the headless profile from a clean clone on a runner with no cache (setup-zig use-cache: false, empty zig caches), the negative control first, the record uploaded as reproduce-headless. On pushes to main, on pull requests that touch the build, weekly, and by hand.

Python follows the existing CI scripts (#997); .claude/rules/no-shell-scripts.md still says "zero Python", which the repository no longer follows.

What it does not do yet (rest of #989)

  • Fix the five roots. Each is a small, separate change (a link_libc, a comment, a removed import, a module name, an optional that became an array); each makes one capability's tests run for the first time in months, and the gate will ask for its line to be removed.
  • Consume the canonical t27 manifest by a pinned, byte-checked lock, and publish the capability -> spec -> artifact -> evidence index; the end-to-end task over CLI/MCP, tri-api policy, persistence and the Queen lifecycle; the catalog refresh and publication.
  • Any profile but headless.
{
  "version": 1,
  "head_sha": "14c595aae4b13fb5f60d729578d46046c2fce8d3",
  "summary": "CI judges the headless test graph against a declared profile instead of piping it through tee, so the five test roots that do not compile on main are named, with reasons, and a sixth fails the job.",
  "changes": [
    "Added specs/reproduce/headless.t27: the headless profile measured at afc9d384 with Zig 0.15.2, its commands, dependency pins, 46 installs, 67 compiled test roots, graph floors and five blocked roots with errors and capability cards.",
    "Added tools/reproduce.py: the clean-clone command that builds and tests with fresh zig caches, reads every exit code from the process and judges the result against the spec, with a self-check of fifteen planted defects.",
    "Replaced the tee-piped CI test step with the same judge, keeping the leak check, and added a reproduce workflow that runs the whole profile on a runner with no cache and uploads the record."
  ],
  "tests": [
    {
      "command": "python3 tools/reproduce.py --self-check",
      "status": "passed",
      "result": "All fifteen planted defects are reported under their own codes, including the swallowed exit code of #616, and a conforming record stays silent.",
      "evidence": "tools/reproduce.py self_check; run locally on macOS Python 3 before the commit"
    },
    {
      "command": "python3 tools/reproduce.py headless --zig zig-0.15.2 in ubuntu:24.04 aarch64 with empty caches",
      "status": "passed",
      "result": "Build 94/94 steps with 46/46 installs; test 124/135 steps with exactly the five blocked roots failing, 61 run steps with tests, one empty, 2510 tests passed; no violation.",
      "evidence": "Run on tree e5bb811d4eaf (this commit) in ubuntu:24.04 aarch64 with empty caches, 2 min 43 s; record zig-out/reproduce/headless.json lists no violation"
    },
    {
      "command": "Build and Test job of CI run 36875247882 on main afc9d384, read from its log",
      "status": "passed",
      "result": "The log prints the same summary, 124/135 steps succeeded with five failed compiles, while the job reported success through tee.",
      "evidence": "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/gHashTag/trinity/actions/runs/36875247882"
    }
  ],
  "limitations": [
    "The five blocked roots are named, not fixed; each fix is a separate small change that makes one capability's tests run again and removes its line from the spec.",
    "Only the headless profile is reproduced; web, native, FPGA, training and network profiles keep their present status.",
    "The pinned t27 lock, the capability evidence index, the end-to-end task and the catalog publication of #989 come in later parts."
  ],
  "tags": [
    "Engineering",
    "Verification",
    "CI"
  ],
  "blog": {
    "title": "A green build that hid five test roots that never compiled",
    "summary": "Trinity's test step piped its output through tee without pipefail, so failed compiles of five test roots, the core library among them, reported success on every push until a judge read the exit code.",
    "outline": [
      "How a pipe without pipefail turns a failed test step green, and what the build summary said all along.",
      "Declaring the headless profile as data: commands, pins, installs, test roots, floors and the blocked roots with their reasons.",
      "What is still open: fixing the five roots one by one, the pinned contract lock, the evidence index and the end-to-end task."
    ]
  }
}

🤖 Generated with Claude Code

…ee (Refs #989, #616)

S12 of #988, first part. ci.yml ran `zig build test -Dci=true 2>&1 | tee` under
bash without pipefail, so the step's exit code was tee's: on main five test roots
do not compile -- src/trinity.zig, the library's own test root, among them -- and
every run was reported green while its summary said "124/135 steps succeeded;
5 failed".

specs/reproduce/headless.t27 declares the headless profile as measured at afc9d38
(Zig 0.15.2; CI run 36875247882 and a Linux aarch64 container with empty caches):
the commands, the four dependency pins, the 46 installs, the 67 compiled test roots,
the floors of the test graph, and the five blocked roots with their errors and the
capability cards they hold back. tools/reproduce.py runs the build and the test step
from a clean clone with fresh zig caches, reads each exit code from the process, and
judges the result against the spec; --self-check plants fifteen defects. The CI test
step now runs the same judge, and .github/workflows/reproduce.yml runs the whole
profile on a runner with no cache.

Судим граф тестов headless-профиля вместо того, чтобы прогонять его через tee.

S12 эпика #988, первая часть. ci.yml запускал `zig build test -Dci=true 2>&1 | tee`
в bash без pipefail, и код шага был кодом tee: на main пять корней тестов не
компилируются -- среди них src/trinity.zig, собственный тест библиотеки, -- а каждый
прогон считался зелёным, хотя сводка говорила «124/135 steps succeeded; 5 failed».

specs/reproduce/headless.t27 описывает headless-профиль, измеренный на afc9d38
(Zig 0.15.2; CI run 36875247882 и контейнер Linux aarch64 с пустыми кэшами): команды,
четыре пина зависимостей, 46 установок, 67 корней тестов, нижние границы графа и пять
заблокированных корней с ошибками и карточками возможностей. tools/reproduce.py
собирает и тестирует из чистого клона со свежими кэшами zig, читает код выхода
каждой команды из процесса и сверяет результат со спекой; --self-check подкладывает
пятнадцать дефектов. Шаг тестов в CI теперь идёт через тот же судья, а
.github/workflows/reproduce.yml прогоняет весь профиль на раннере без кэша.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the status:in-progress 🔵 Agent working label Oct 1, 2026
@dmitrii-f-t27
dmitrii-f-t27 merged commit 3c33adb into main Oct 1, 2026
24 of 27 checks passed
@github-actions github-actions Bot added status:completed Done and removed status:in-progress 🔵 Agent working labels Oct 1, 2026
dmitrii-f-t27 added a commit that referenced this pull request Oct 1, 2026
S12 of #988, second part. #1207 named five test roots that did not compile and
whose tests had not run for months; this change fixes all five and the test graph
runs whole. A clean clone with empty caches on CI's x86_64 runner: build 94/94
with 46/46 installs, test 135/135 steps, 2768/2768 tests passed. On Linux
aarch64: 2760 passed and 8 skipped (the x86-64 JIT tests of src/jit.zig).

- build.zig: both dependency module names (zig-hdc-vsa / hdc_vsa) and
  golden_float on the test modules of src/trinity.zig and src/e2e_test.zig; libc
  on the queen_api and c_api test modules; the C headers at src/libs/c, where
  they moved on 2026-03-19, so zig build libvsa and libqueen build again.
- src/c_api.zig, src/vm.zig, src/science.zig, src/vsa_jit.zig: ported to the
  HybridBigInt of the pinned zig-golden-float (the unpacked cache is an array;
  dotProduct, bundle2 and bundle3 take no allocator); c_api's duplicated `if`
  removed and gen_encoding reached through the vsa.zig facade; e2e_test.zig no
  longer imports the deleted packed_trit.zig.
- The first x86_64 run of these roots found a real bug: src/jit.zig returned the
  generated code as a pointer with Zig's own calling convention, while the code
  reads rdi and rsi; the x86-64 backend that builds Debug does not pass them
  there, so bind and bundle read the wrong memory. JitBinaryFn now has
  callconv(.c), as src/jit_arm64.zig already declares.
- The second run failed the wall-clock thresholds of the e2e benchmarks: Zig
  0.15's own x86_64 Debug backend compiles zig-golden-float's by-value 71 KB
  HybridBigInt code 185 to 1680 times slower than LLVM on aarch64 (cosine 19.4 ms
  against 11.6 us). The e2e test compile now uses LLVM; no threshold raised, no
  test skipped.
- specs/reproduce/headless.t27 names no blocked root, raises the floors (66 run
  steps with tests, 2760 tests passed) and caps skipped tests at 8.
  tools/reproduce.py reads run steps once from the tree after the summary (zig
  prints a failed step twice), names failed tests, prints and uploads the whole
  output of a failed command, judges skipped tests, and its self-check plants a
  defect for every rule and parses verbatim zig 0.15 output.

Пять заблокированных корней тестов снова компилируются.

S12 эпика #988, вторая часть. #1207 назвал пять корней тестов, которые не
компилировались и чьи тесты месяцами не запускались; это изменение чинит все
пять, и граф тестов идёт целиком. Чистый клон с пустыми кэшами на x86_64-раннере
CI: сборка 94/94 и 46/46 установок, тесты 135/135 шагов, 2768/2768 прошли. На
Linux aarch64: 2760 прошли и 8 пропущены (x86-64 JIT-тесты src/jit.zig).

- build.zig: оба имени модуля зависимости (zig-hdc-vsa / hdc_vsa) и golden_float
  у тестовых модулей src/trinity.zig и src/e2e_test.zig; libc у тестовых модулей
  queen_api и c_api; C-заголовки в src/libs/c, куда они переехали 2026-03-19,
  поэтому zig build libvsa и libqueen снова собираются.
- src/c_api.zig, src/vm.zig, src/science.zig, src/vsa_jit.zig: переведены на
  HybridBigInt закреплённого zig-golden-float (распакованный кэш -- массив;
  dotProduct, bundle2 и bundle3 без аллокатора); в c_api убран дубль `if`, а
  gen_encoding берётся через фасад vsa.zig; e2e_test.zig больше не импортирует
  удалённый packed_trit.zig.
- Первый прогон этих корней на x86_64 нашёл настоящий баг: src/jit.zig отдавал
  сгенерированный код указателем с собственным соглашением вызова Zig, а код
  читает rdi и rsi; x86-64 бэкенд Debug передаёт их не туда, и bind с bundle
  читали чужую память. Теперь у JitBinaryFn callconv(.c), как в src/jit_arm64.zig.
- Второй прогон уронил пороги времени e2e-бенчмарков: собственный x86_64
  Debug-бэкенд Zig 0.15 компилирует код zig-golden-float с HybridBigInt по 71 КБ
  по значению в 185-1680 раз медленнее, чем LLVM на aarch64 (cosine 19,4 мс
  против 11,6 мкс). Компиляция e2e-тестов теперь через LLVM; пороги не подняты,
  тесты не пропущены.
- specs/reproduce/headless.t27 больше не называет заблокированных корней,
  поднимает нижние границы (66 шагов с тестами, 2760 пройденных) и ограничивает
  пропуски восемью. tools/reproduce.py читает шаги один раз из дерева после
  сводки (zig печатает упавший шаг дважды), называет упавшие тесты, печатает и
  загружает весь вывод упавшей команды, судит пропуски, а его self-check
  подкладывает дефект под каждое правило и разбирает дословный вывод zig 0.15.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant