Skip to content

feat(zig): Zig 言語の Graph 対応 - #95

Merged
magicdrive merged 15 commits into
mainfrom
feat/zig-graph
Oct 28, 2026
Merged

magicdrive merged 15 commits into
mainfrom
feat/zig-graph

Conversation

@magicdrive

@magicdrive magicdrive commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

概要

Zig(.zig)を Graph レベルで追加します。対象は Zig 0.15〜0.17(現行の安定版は 0.17.0)、parser は gotreesitter v0.56.0 に同梱の tree-sitter-grammars/tree-sitter-zig(6479aa13、MIT、純 Go。新しい依存の追加なし)です。

実際の Zig プロジェクトで「定義はどこか」「誰が呼んでいるか」「どのファイルを import しているか」「変更の影響範囲」を、誤った Strong/Exact を出さずに答えられることを目標にしています。

設計(要点)

  • Identity はファイルのパスから(Rust と同じ方式)。src/engine.zig::Engine::run。ファイル自身も宣言(src/engine.zig、@import の解決先)です。全ファイルが IdentityOnly なので、名前の一致で解決されることはありません。
  • 解決するもの
    • 字句スコープ(ローカル・引数・capture は観測しない。usingnamespace を持つ container ではその名前を未解決のままにする)
    • 相対 @import、container のメンバー、@This()、別ファイルをまたぐ再 export の連鎖
    • generic な型ファクトリ(return struct {...} が 1 つだけの関数 → F::())
    • 宣言で証明できる受信者:宣言された型、T{...}、書かれた戻り値型(別ファイルも可)、struct のフィールド型
    • 宣言された型から結果型が決まる decl literal / enum literal
  • comptime は評価しない。if / switch で名前空間を選ぶ場合は各分岐を候補とし、メンバーは Candidate になります。
  • build.zig は読むだけで実行しない。module の import 表(addImport / .imports など)を静的に読み、名前付き module の上限は Strong です。ただし、その事実を Strong や相対 import の Exact の根拠にするのは、build が Closed のときだけです(下記)。

共有コードへの変更(言語非依存)

  • AliasDraft.Members:値が名前空間である定数を「それ自身の宣言」として扱い、その下のメンバーだけを alias の先で読む。
  • AliasDraft.ConfidenceCap:その alias を経由した解決に上限を掛ける(下げる方向だけ)。
  • ReferenceDraft.TentativeIdentity:宣言があればそれが解決先、なければ Unresolved。リポジトリ外とも主張せず、同名の宣言への completeness の帰属からも除外しない(関数ポインタのフィールド呼び出しかもしれない呼び出しで、get_callers が誤って「完全」と答えないため)。
  • AliasDraft.Open / AliasDraft.Fallback / Resolution.Open、ReferenceDraft.PathCaseFold / MayBeAnyFile、source.FoldPath:不明な束縛を Candidate かつ「候補で閉じない」解決として表し、completeness で未解決と同じく帰属させる。大文字小文字・Unicode 正規化だけが違うパスは Candidate。
  • MCP の target lookup が Engine.run を ...::Engine::run に区切り位置で照合する別表記を追加。
    • 既存言語への影響(監査で確認): これらの draft フィールドは既存の provider が設定しないため、Go・Rust・Python・Java・PHP・TypeScript・JavaScript・Terraform・HTML・CSS の 23 の root で、シンボル・edge・confidence・completeness・解決済み参照は base と完全に一致します。変わるのは target lookup だけです。Rust の A::b が A.b という入力でも引けるようになり、602 件の入力で候補が増えました(減った候補は 0 件)。その結果、以前は一意だった入力が、同名の Rust のシンボルと合わせて曖昧と報告されることがあります(TigerBeetle の Java Client.close など 96 件)。一つを勝手に選ばない、という契約の範囲内の変化です。

詳細は ARCHITECTURE.md §1(Zig)、docs/language-support.md#zig、docs/development/zig.md にあります。

Soundness-first:Closed / Open の build(オーナー判断 B を採用)

原則:解決結果を Exact / Strong にするのは、その結果を変えうるすべての経路が静的解析で否定できるときだけです。否定できない経路が残れば Candidate か Unresolved にします。

  • Closed:build.zig のコードが build の graph に届くすべての経路を Ark が追えるとき。Zig 0.17.0 の std と build runner(configurer)には graph を持つグローバルがないので、graph には b とそこから得た値を経由してしか届きません。
  • Closed の条件(どれも値の追跡に依存しない構造的な規則を含む)
    • 別ファイルのコードの @import がない(std・builtin 以外。.zon も依存の名前になりうるので不可)。
    • 依存の build を実行しない(b.dependency は親と graph を共有する)。
    • std の名前空間の関数は許可表のものだけ(std.mem.eql など読み取り専用、コンテナの型、パスとファイルの操作)。
    • ファイル内の関数名が、評価器が名前で読む std.Build の API(createModule など)や、std が慣習で呼ぶ名前(format・hash など)と同じでない。
    • ポインタを作る・型を変える builtin がなく、ポインタ経由・添字経由の書き込みがない(getOrPut の slot を除く)。
    • コンパイラが Module API の外で作る module がない(test_runner、zig_lib_dir、std の束縛、CLI で分割される名前)。
    • graph の値(taint)の使い道は、ファイル内の関数、std 0.17.0 の source で表・root・登録を変えないと確かめたメソッドとフィールド(TestClosedTablesAgainstStd が照合)、コンテナ、評価器が読む形だけ。
  • Open:それ以外。Open な build はどのファイルにも module を作れるので、リポジトリのすべての import が Candidate(completeness では未解決として帰属)になります。ファイル内の参照(自分の宣言への参照)は影響を受けません。
  • 相対 import の identity:@import("x.zig") は import 側の identity {zigimp}<path> を持ち、ファイル(とその spec で表が束縛しうる module)へ alias します。以前はファイル自身の identity に alias を付けていたため、build が不明になるとファイル内の参照まで Candidate に落ちていました。
  • 静的に検出して Candidate に下げるもの(オーナーの方針による)
    • 索引にない module root(symlink を経由するパス、索引が読まないディレクトリの下、生成されたファイル):AliasDraft.Absent で、その module の表をリポジトリのすべてのファイルに Candidate として効かせます。root が索引にあるときは何も変わりません。
    • リポジトリの Zig ソースやディレクトリの複製(addCopyFile / addCopyDirectory):build を Open にします。データの複製では Open にしません。
  • 信頼境界の外(docs/development/zig.md decision 8 の表)
    • オーナーが承認したもの:build が書き換えるソースファイル、build が実行するコマンドによるコンパイル
    • 従来どおりのもの:リポジトリの外の利用者、コマンドライン、illegal behavior
    • 残るもの:どの build からも参照されない、索引が読まない build.zig。walk が除外したものを報告しないためで、対応には walk の変更が必要です(後続)。

検証

  • RED → GREEN:

    • 設計の各条件のテスト(world_test.go)
    • 敵対的レビュー 4 回の反例(review_r1_test.go 〜 review_r4b_test.go)
    • 規則ごとの単独のテスト(world_rules_test.go)
    • MCP の結合テスト TestMCPZig_OpenBuildNeverReadsComplete

    修正前のコードで RED、修正後に GREEN であることを確認しました。

  • Zig 0.17.0 によるオラクル:公式配布物を使いました(SHA-256 4f9a1c52…29c6、index.json と一致)。

    • import 表の優先、module root の境界、module ごとの表、分岐による束縛、複数の依存、build option、大文字小文字をコンパイラで確認しました。
    • 正例の build.zig は、すべて型検査を通ります(実行はしていません)。
  • 敵対的レビュー:4 回で計 9 人のレビュアーに依頼しました。

    • 1〜3 回目は反例が出ました(計 41 形)。そのたびに、前の規則に依存しない構造的な規則に置き換えました。
    • 4 回目(2 人。うち 1 人は Maker の CLI への変換を精読)では、graph の反例は 0 件でした。見つかった 1 形(build がソースを書き換える)は境界の外です。
  • 変異テスト:22 種のうち 21 種を検出しました。生存した 1 種(コンパイラが作る module の規則)は、先に働く「知らない options のフィールド」の規則が捕まえる重なりです。

  • 既存言語:23 の root で base(3284131)と比べ、シンボル・edge・evidence・confidence・completeness は一致しました。差分は MCP lookup で Rust の候補が増える 602 件だけで、減った候補は 0 件です。

  • ローカル:

    • gofmt・vet・make staticcheck・go test ./...(ARK_ZIG_STD で std との照合を含む)
    • -race(7 パッケージ)
    • FuzzExtractZig 120 秒
    • govulncheck(呼ばれる脆弱性 0 件)

    すべて GREEN です。

実 OSS 評価(commit を固定、最終 head)

TigerBeetle 0450c32 Ghostty 83fb956
Zig ファイル / parse error のあるファイル 248 / 0 814 / 6
シンボル 18,522 35,408
edge(すべて Exact、ファイル内) 29,630 63,836
参照(exact / candidate / unresolved) 50,579 / 15,591 / 17,867 105,870 / 32,756 / 83,532
build.zig(Closed / Open) 0 / 2 2 / 54
MCP Cold / Warm / Restore / Peak RSS 2,692ms / 52ms / 1,488ms / 734MB 7,658ms / 142ms / 3,979ms / 1,366MB
  • 両リポジトリとも、root の build.zig は Open が正しい判定です。
    • TigerBeetle:custom step の make を関数ポインタで呼び、@fieldParentPtr を使っています。
    • Ghostty:build の helper(src/build/)を import しています。
  • そのため、import とファイルをまたぐ参照はすべて Candidate になりました(前回は edge 41,670 / 88,400、Strong 4,341 / 25)。
  • 性能は前回の表と比べて、Cold が +18〜20%、Restore が +34〜51% です。
  • Precision:新しい層別標本 100 件(seed 20261030)を 3 人が独立に確認し、CORRECT 100 / WRONG 0 / UNSURE 0 でした。
    • 層は、メンバー呼び出し、通常の呼び出し、references、uses_type です。
    • 標本は最後の規則を足す前の graph から取りました。最終の graph はその部分集合(8 件少ない)で、100 件はすべて残っています。
    • 母集団はファイル内の Exact だけで、Strong・ファイルをまたぐもの・import の層は 0 件でした。

既知の制限

  • 推論が必要な受信者、別ファイルの struct のフィールドを経由するメンバー、std 経由のすべては Unresolved のままです。
  • re-export の連鎖の途中にある、大文字小文字だけが違うパスは Unresolved です(帰属は保たれます)。
  • 後続の課題(docs/development/zig.md の Remaining work)
    • edge の種類の不一致 2 件(struct literal と型の参照が calls)
    • 性能(Restore +34〜51%)
    • 再現率
    • 索引が読まない build.zig
  • Closed の条件が厳しいので、依存・helper・.zon を使う build は Open です。今回の 2 リポジトリでは、名前付き module の Strong も、相対 import の Exact も出ません。

判定:オーナーの判断に委ねます

MERGE READY の条件は、すべて満たしています。

  • Closed / Open の設計が成立しています。4 回目のレビューで、graph の反例は 0 件でした。
  • オーナーの方針で残した 3 項目のうち、symlink を経由する root、索引にない root、複製は、静的に検出して Candidate に下げます。修正前に RED、修正後に GREEN であることを確認しました。
  • 既存言語に意図しない回帰はなく、Precision の標本で WRONG は 0 件です。
  • 最終 head の CI は GREEN です(下記)。

残存リスクとして報告します。

  • どの build からも参照されない、索引が読まない build.zig:Ark には見えません。検出には、walk・索引が除外した build.zig を伝える仕組みが要ります。
  • 精度の低下:依存・helper・.zon を使う build は Open で、今回の 2 リポジトリでは Strong と相対 import の Exact が出ません。

マージ、tag、release は行っていません。

CI:最終 head dcba961、run 40662708022 で 28/28 が GREEN です(CLEAN・MERGEABLE)。

初回は filesystem-security (macos-latest) の internal/fsroot TestReadStable_ConcurrentWriters(並行した書き込みの時間に依存する判定)が失敗しました。原因が PR にないことを、次の 3 点で確かめてから、失敗したジョブだけを再実行し、success になりました。

  • PR は fsroot/accesspolicy/safewalk に差分を持たない。
  • ローカルで -race -count=10 がすべて通る。
  • 直近 40 件の失敗した CI で、このテストの失敗は今回だけ。

Add three opt-in pieces of generic evidence. No existing provider states
them, so every existing language resolves exactly as before.

- AliasDraft.Members: the alias reads only the identities below its own
  (Q::x names T::x); Q itself names its own declaration. A constant whose
  value is a namespace is a declaration of its own: a plain alias would
  make a reference to it a Candidate of the constant and its target.
- AliasDraft.ConfidenceCap: every resolution that follows the alias is at
  most the cap, however it reaches it (directly, below it, through another
  alias). It only lowers.
- ReferenceDraft.TentativeIdentity: the identity names the target only if
  a declaration carries it. A miss is Unresolved without claiming the
  referent is outside the repository, and without excluding same-named
  declarations from completeness (IdentityInRepository claims both).

ExpandAliases asks for an identity's aliases as itself or as a leading
part of a longer path; the index keeps member aliases apart for
SymbolsThroughAliases the same way. Golden snapshots render the new
fields only when set, so existing snapshots are unchanged.
Add Zig (.zig) at Graph level, for the language as Zig 0.15 to 0.17
define it, parsed by tree-sitter-grammars/tree-sitter-zig as gotreesitter
v0.56.0 bundles it (pure Go, no new dependency).

A Zig file is a struct and a relative @import names a file by its path,
so every identity is derived from the file's own path
(src/engine.zig::Engine::run) and every file is IdentityOnly: no name
match ever resolves a Zig reference. The provider resolves names by
lexical scope (locals, parameters and captures are not observed;
usingnamespace leaves a name unresolved), members of the file's
containers, of imported files and of constants naming either, @this(),
re-exports across files, generic type factories whose one return is a
container (F::()), and method calls on receivers whose type declarations
prove: declared types, constructions, a function's written result type
(also of another file, as a tentative identity), struct fields. A
comptime choice between namespaces states each alternative (Candidate).
Named modules that build.zig maps literally (addModule, addImport,
.imports) resolve at Strong at most; build.zig is read, never run; std,
builtin and root are never mapped.

The MCP target lookup also tries a member written with "." against
identities that separate members with "::" (Engine.run).

Evaluated on TigerBeetle (0450c32) and Ghostty (83fb956): 0 and 6 files
with parse errors, 40,753 and 89,785 graph edges; 230 randomly sampled
edges checked by hand, no false exact or strong edge. Sixteen soundness
mutations (receiver types, lexical scope, module boundaries, named
imports, comptime alternatives, caps, tentative identities, @this(),
usingnamespace, duplicate declarations, field calls) are each killed by
the tests.

Add Zig unit, graph, golden, conformance, fuzz, scaling, determinism and
MCP integration (incremental reindex, cache) coverage.
ARCHITECTURE.md records Zig's identity and resolution boundary and the
three generic extensions it introduced (member aliases, capped aliases,
tentative identities), with their authorities and tests.
docs/language-support.md describes what Zig resolves, what it leaves a
Candidate or unresolved by design, and what is not implemented;
docs/development/zig.md records the version and parser choice, the
design decisions and the evaluation on TigerBeetle and Ghostty;
docs/performance.md adds their MCP server timings.
…tity

An adversarial review found valid Zig that Ark resolved Exact to a
declaration the code may not denote; each is reproduced in
TestReview_NoExactPastUnprovenAlternatives.

- A comptime choice between namespaces whose other alternative lies
  outside the repository (std.Thread.Mutex, a file out of the root, a
  named module) or holds the member as a field: the alternatives' aliases
  are now capped at Candidate, so a member found in one alone is a
  Candidate.
- Containers sharing an identity (unnamed payload structs of a union,
  same-named containers of two blocks, two comptime blocks or unnamed
  tests): a member through one is capped at Candidate, since what one
  declares may be the other's field.
- A comptime var holding a type may be reassigned: it is no namespace
  (a runtime var's type, which never changes, is still read).
- A quoted name containing an identity separator (::, ., braces,
  parentheses) claims no name; a generic factory's returned name must be
  declared once in its body.

On TigerBeetle and Ghostty this turns 69 and 172 edges into Candidates.
The four fixes are covered by four more mutations (twenty in all), each
killed by the tests.
…t through shared alternatives or quoted primitives

A pre-merge audit found valid Zig (each shape checked with Zig 0.17.0)
whose calls the index ruled out as any repository declaration's caller,
so get_callers read complete while they existed: a call through a value
of another file, a namespace constant of another file the file cannot
denote, a named module no build.zig maps literally (TigerBeetle's vsr),
root, a re-export chain past the alias depth. A member past another
file's member, or of a named module other than std and builtin, now
names its target only if declared (TentativeIdentity): resolved as
before when declared, attributable when not. The graphs of TigerBeetle
and Ghostty are unchanged by it.

Also: an alias to an identity unnamed alternatives share is capped at
Candidate (the call may be the other's field holding a function), and an
unquoted primitive is never a quoted declaration of its name (@"u0").
… claim what a build may bind otherwise

A module's import table is the module's own, and Zig looks @import's
string up in it before any file. Ark read a build's registrations
(b.addModule) as every file's bindings, repository-wide: an import of a
name two modules bind apart resolved Strong to one (F7), @import("x.zig")
resolved Exact to the file beside it while a table bound the name (F5),
and an import no build bound read every file's importers as complete.

Named modules now resolve by module environments: a file sees the tables
of the modules rooted in its directories (Zig rejects an import out of a
module's directory), build.zig states each module's directory and table,
read by a static evaluator (constants, parameters through the file's
calls, tuples, labeled blocks, artifacts' root_module, dep.module by
registration, comptime choices and copies of tables). Every occurrence of
a module the evaluator names is classified where it is written; an
operation it does not read leaves the module's names unknown, an
assignment of a root or a table any module's. Unknown bindings cap at
Candidate and are open: completeness counts an open resolution as an
unresolved one besides its candidates.

Generic, unused by other providers: AliasDraft.Open and Fallback,
Resolution.Open, ReferenceDraft.PathCaseFold (a path apart in case alone:
a Candidate) and MayBeAnyFile (an import that may be any file counts
toward every file's importers); alias evidence names only the aliases on
a chain to the declaration; aliases into identities nothing declares are
not expanded.

TigerBeetle gains the vsr module's strong edges (+986), Ghostty loses the
strong edges from src/ into pkg/ (1,085: their tables are bound in
src/build/*.zig, which no build.zig is) and 127 exact edges of
pkg/wuffs/src (its build binds names computed at build time). Other
languages' graphs, evidence and completeness are unchanged.
…h or an escaped name

A fifth adversarial review found shapes beside build code:

- a re-export chain longer than MaxAliasDepth dropped the caps of the
  aliases past the cut: a chain cut short is now an open Candidate;
- a binding to a root the index does not hold (vendored, skipped) left
  the other binding alone, Strong: such a root now stands for the
  unknown through AliasDraft.Fallback, which is read only where its
  identity names nothing else and on a reading that found no declaration
  (it was read wherever a reading below it found none);
- paths apart in Unicode normalization or full case folding (NFC/NFD,
  sigma, Kelvin) were apart for Ark: source.FoldPath folds them for
  module directories and PathCaseFold;
- names with escapes (@import("hel\x70er.zig"), h.@"wh\x69ch"()) and
  @field(x, "name") were not observed: escapes decode, @field is a
  reference;
- a build script named apart in case, and another package's build run
  with this b (sub.build(b) of a build.zig imported by name or path), now
  leave every module's names unknown.

TigerBeetle's and Ghostty's graphs are unchanged; other languages' are
unchanged.
Five reviews of the build.zig evaluator did not converge: what it misread
was a premise ("code Ark does not read binds nothing"), not a checked
fact. A build's module facts now ground a Strong (and the absence of a
binding a relative Exact) only when the build is closed: Ark follows every
use of a value that reaches the build graph.

Zig 0.17.0's std keeps the graph in no global, so code reaches it only
through the builder and what is derived from it. closed.go taints those
values (by binding, resolved by Zig's scopes, flow-insensitively) and
allows a use only through the file's own functions, the std.Build methods
whose 0.17.0 bodies create, root, bind and register no module, containers,
std functions that only read, settings fields, and the forms buildEval
reads. Anything else - a helper file or a dependency's build code given
the builder, an unknown method, a function pointer, a pointer cast, a
container-level var, another version's options, syntax outside the
grammar forms read, nesting past the bound - opens the build: every
module's names are unknown, for every file.

A relative import now has the importer's identity ({zigimp}path, aliased
to the file and to what a table may bind under the spec): the file's own
references keep the file's identity and stay Exact in an open world; they
were Candidates wherever a build was unknown.

TestClosedTablesAgainstStd checks the tables against a std tree
(ARK_ZIG_STD). On TigerBeetle and Ghostty the root builds are open
(make-time function pointers, @fieldParentPtr; build helpers given the
builder): their imports are Candidates now.
The first review of closed builds laundered the builder past the taint
(@ptrFromInt of @intFromPtr, many-pointer arithmetic, a container written
through a pointer parameter, LazyPath.dupe and a literal's .name read as
graph-free, a namespace in a tainted literal), gave a dependency's build a
path, rooted a module at a test runner, called build with another
builder, and hid a case-folded file behind a table binding.

Rather than one condition per shape, what a closed build may run no
longer rests on the taint: a call of another file's code (a namespace an
import of it is, a value of its types, a function value, a method of an
anytype parameter) opens the build whatever it is given, and so do
pointer-making builtins and writes through a pointer or into an element
(a container slot of getOrPut excepted). The taint closes its own leaks
besides: arithmetic keeps it, `&y` and container results alias, a
parameter's taint flows back to the arguments, graph-free fields and
results hold only on the builder, a method is the file's only on the
file's types. A dependency given a graph value, a test runner and a build
function the file calls with another builder open the build or its paths.

The resolver adds the files a case-insensitive file system opens for a
path besides what an identity's aliases lead to (a Candidate).
A second review read builds as closed that ran another file's code
without calling it by name (a value of its type through a branch's value,
a switch capture, a type parameter, std calling its format), rewrote a
module through a pointer std made from its address (bytesToValue of an
@intFromPtr, a formatted address, then std.mem.swap through `const mem =
std.mem`), or let a method of the file named createModule, addExecutable
or module stand for std's.

A closed build now imports no other file's code at all (the only way to
name it), calls of std's namespaces only functions that read, make a
container type or operate on paths and files (through any constant
naming a part of std), and declares no function named like a std.Build
call buildEval reads. An address keeps its value's taint. Paths through a
symbolic link are outside the boundary (the index follows no link).
…the compiler adds

A third review found modules the evaluator does not see because the
compiler, not the Module API, makes them - a test runner given in a typed
literal, a zig_lib_dir or a table binding std (another standard library),
an import name the command line splits (`a=b`) - and code that runs
without a call of the file: std calling a top-level format by `{f}`, a
dependency named `cfg.zon` imported as data, a dependency's build sharing
this build's graph (so a fetched package may rebind its steps' modules
given nothing), a getOrPut of the file's own posing as a map's slot, and
`b.root` reassigned through a field read as graph-free.

A closed build now runs no dependency's build and imports no ZON, declares
no function std calls by name, makes no test runner or library directory,
binds no std and no name a command line splits; an assignment to a field
of a graph value is checked whatever the field holds. With std made open,
an import of std may be any file. Compilations by commands a build runs,
and modules rooted at copies, are outside the boundary, as command lines.
Mutation testing showed rules the review shapes exercised only together
(a later, broader rule catching them first). Each rule now has a test of
its own: a form only it reads opens the build, and the taint flows through
a literal's field, an address's arithmetic, a container given by pointer
and one reached through `&`. The last found a constant pointing to a
runtime value not read as one: it is now, so a container written through
it holds what it is given.
…urements

The fourth review found no shape in the graph; a build that rewrites a
source file, and a build.zig the index does not hold, are outside the
boundary. Its pins of the shapes that hold are kept. The evaluation record
and the performance page carry the measurements of the closed-build rules:
both repositories' builds are open, every import a Candidate, 29,632 and
63,842 edges within files; the restore takes a third to a half longer.
…urce, as unknown

The maintainers kept rewritten sources and compilations by commands
outside the boundary, and asked for what a build states statically to be
detected rather than trusted: a module rooted at a path through a
symbolic link, under a directory the index skips, and a module rooted at
a copy of the repository's sources.

AliasDraft.Absent states an alias that holds only where no file of the
language has a path in the index. A build states, for each module root,
aliases from {zigany} and {zigspec}. to the module's table, capped at
Candidate and absent on the root: while the root is a file of the
index nothing changes; when it is not, the module may hold any file and
every import is a Candidate. A copy of a Zig source, a directory or a
path Ark cannot read (WriteFile.addCopyFile / addCopyDirectory) opens the
build; a copy of data does not. A build.zig the index skips and no read
build names stays outside the boundary (the walk does not report what it
skips).

The evaluation record carries the final graph (29,630 and 63,836 edges:
8 fewer, members of anonymous structs passed to std generics, which the
open std alias caps) and the follow-ups: two edge kinds, the restore's
time, recall, skipped builds.
@magicdrive
magicdrive merged commit 6c6523e into main Oct 28, 2026
54 of 56 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant