diff --git a/docs/evidence/next-bug-class-2026-10/.gitignore b/docs/evidence/next-bug-class-2026-10/.gitignore new file mode 100644 index 00000000..97b72027 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/.gitignore @@ -0,0 +1,4 @@ +repro/c_runtime_dictionary/BuggyMeziantou.cs +**/bin/ +**/obj/ +repro/c_analyzers/BuggyMeziantou.cs diff --git a/docs/evidence/next-bug-class-2026-10/00-preregistration.md b/docs/evidence/next-bug-class-2026-10/00-preregistration.md new file mode 100644 index 00000000..e9a1d388 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/00-preregistration.md @@ -0,0 +1,63 @@ +# Next real-bug class — pre-registration (Phase 0) + +Записано 2026-10-18 до любых экспериментов. Own.NET `main` = `2fefb19f` (merge #411). Worktree +`ownnet-research-next` (detached, без изменений). Отчёты — `ownnet-evidence/next-class/` (вне репозитория, +чтобы рабочее дерево оставалось чистым). + +## Общий бюджет + +Один агентский рабочий день на все три кандидата. Порядок: Phase 1 (три дешёвых воспроизведения) ≤ 20 %, +Phase 2 (реальные баги) ≤ 45 %, Phase 3 (только лучший кандидат) ≤ 20 %, отчёт ≤ 15 %. Не больше 15 реальных +кандидатов «в глубину» на все три класса. После провала необходимого условия — KILL этого кандидата, его +остаток бюджета переходит к сильнейшему, а не делится поровну. + +Порог GO (предложенный, не установленный ранее): +- ≥ 2 независимо подтверждённых реальных дефекта из разных репозиториев, адресуемых кандидатом, без + продемонстрированной неприемлемой потери точности; ИЛИ +- 1 независимо подтверждённый дефект высокой важности, пропущенный Own.NET и хотя бы одним успешно + запущенным релевантным анализатором, плюс малый правдоподобный путь реализации и негативные контроли. + +## A — #382 canonical first-party call facts + +Что должно быть правдой: существуют реальные дефекты владения, которые Own.NET пропускает (или ложно +сообщает) ИМЕННО потому, что первопартийный вызов свёрнут фронтендом в `release`/`use`/escape, и которые +ядро нашло бы, получив `call`. + +| # | Необходимое условие | Самый дешёвый контрпример | KILL | +|---|---|---|---| +| A1 | На `main` фронтенд теряет вызов: `ForwardDynamic` → `use`, summary `forwarded = no` | извлечь `GuardedConsumeSample.cs`, посмотреть факты и `summaries` | вызов уже сохраняется или summary уже `may` | +| A2 | Ядро даёт `may` / OWN051, если подать `call` (временная правка фактов, вне репо) | повторить пробу из #382 на `main` | ядро не выводит `may` из `call` | +| A3 | Синтетический FN: вызывающий, который использует ресурс после guarded-forward, молчит на `main` и получает диагностику при `call` | одна синтетическая пара, факты правятся вручную | разницы нет | +| A4 | Реальный дефект: пара bug/fix, где владение передаётся через первопартийную обёртку (forward / guarded dispose / dispose-flag) и Own.NET на buggy молчит из-за свёртки | поиск в имеющихся парах исследования (60 пар) и узкий поиск на GitHub | 0 реальных дефектов этого механизма после ≤ 6 глубоких кандидатов | + +Предупреждение до начала: известные реальные свидетели #382 (C12/C13/C16 dotnet/runtime) — ложные +срабатывания, уже снятые #381; «recall side» в #382 — гипотеза без реального бага. Свидетели, которые +сводятся к уже известному ограничению, считаются DUPLICATE, не новым багом. + +## B — #406 closure capture, non-escaping synchronous delegate + +Что должно быть правдой: реальный код теряет освобождение локала, захваченного синхронно вызываемым +локальным делегатом, и разработчики это исправляли. + +| # | Необходимое условие | Самый дешёвый контрпример | KILL | +|---|---|---|---| +| B1 | FN воспроизводится на `main` из-за правила «захват = escape» | сэмпл из #406 + проверка, что без лямбды OWN001 есть | OWN001 и так выдаётся, или причина другая | +| B2 | ShareX-подобный async-случай молчит на `main` (контроль, который нельзя сломать) | `ForEachAsync`-форма | — (контроль) | +| B3 | Реальный дефект: пара bug/fix, где утёкший disposable захвачен **синхронно** вызываемой локальной лямбдой/локальной функцией | поиск в парах исследования + узкий GitHub-поиск | 0 реальных после ≤ 5 глубоких кандидатов | + +## C — #400 CollectionsMarshal borrowed-view invalidation + +Что должно быть правдой: реальный код использует `CollectionsMarshal.AsSpan` после структурной мутации +списка, это наблюдаемо ломается, и разработчики это исправляли; существующие анализаторы это не ловят. + +| # | Необходимое условие | Самый дешёвый контрпример | KILL | +|---|---|---|---| +| C1 | Наблюдаемый stale-view при принудительной реаллокации; негативные контроли чисты | маленькая консольная программа (runtime) | эффект не наблюдается | +| C2 | Существующие .NET-анализаторы не покрывают узкий паттерн | поиск правил CAxxxx / IDE / известных анализаторов | есть зрелое правило | +| C3 | Реальный дефект: пара bug/fix «AsSpan → Add/Remove → использование вида» | поиск коммитов/PR/issue на GitHub | 0 реальных после ≤ 4 глубоких кандидатов | + +## Запреты (из задания) + +Без production-кода, OwnIR, широкого рефакторинга ядра, полного перепрогона корпуса без дешёвого гейта, +инфраструктуры для конкурентов, расширения поиска после проваленного гейта, P-037 (#304 заморожен), +#407 (deferred), без issue/PR/push. Временные пробы — только во временных каталогах. diff --git a/docs/evidence/next-bug-class-2026-10/REPORT.md b/docs/evidence/next-bug-class-2026-10/REPORT.md new file mode 100644 index 00000000..4693071b --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/REPORT.md @@ -0,0 +1,135 @@ +# Next real-bug class selection (#382 / #406 / #400) — kill-first research report + +**Result:** `GO-400`, narrowed. The first research slice of #400 moves from `List`/`AsSpan` to +`Dictionary` with `CollectionsMarshal.GetValueRefOrAddDefault` / `GetValueRefOrNullRef`. Two independent real +defects were found in that shape, and none in the original slice. #382 and #406: **NO-GO within the current evidence +budget**. No real defect was found within the pre-registered budget. That is not evidence that either direction is +useless, and both issues stay open. + +**Owner decision (2026-10-18):** `ACCEPT RESEARCH RESULT / GO-400-DICTIONARY-PILOT`. One bounded Roslyn PoC, outside +production, is authorised after this report is published. Its acceptance gates are in §6. + +Pre-registration, written before any experiment: [`00-preregistration.md`](00-preregistration.md). + +## 1. Frozen experiment identity + +| What | Value | +|---|---| +| Own.NET | `main` `2fefb19f` (merge #411); the research was run in a detached worktree, left clean afterwards | +| Extractor | built from `2fefb19f`, Release, net8.0; run with `--flow-locals [--stats]` | +| Python core | `python -m ownlang ownir|summaries` at `2fefb19f`; Python 3.12.10 | +| Rust core | `own-cli 0.1.0` built from `f8bd59be`. `rust/` is unchanged between `f8bd59be` and `2fefb19f`: #409 and #411 touched only the extractor, tests and `scripts/` | +| .NET | SDK 10.0.401; runtimes 6.0.36, 8.0.31, 10.0.12; the probes target net8.0 | +| Built-in analyzers | `Microsoft.CodeAnalysis.NetAnalyzers` from SDK 10.0.401, `AnalysisMode=All`, `AnalysisLevel=latest` | +| GitHub tooling | `gh` 2.96.0 for PR/issue/code search, `gh pr diff`, and raw files at pinned SHAs | +| Corpus 1 | the 58 bug/fix pairs of the Own.NET direction research. That corpus is private, so the triage script needs access to it; its output is committed in [`data/triage_pairs.tsv`](data/triage_pairs.tsv) | +| Corpus 2 | a GitHub code-search sample of 60 files from 31 repositories that use `CollectionsMarshal.GetValueRefOrAddDefault`, pinned by blob SHA in [`data/sample-index.tsv`](data/sample-index.tsv). The files themselves are not committed; `repro/sample.sh` re-fetches them | +| Queries | [`repro/search.sh`](repro/search.sh): 8 PR/issue queries, output in [`data/search1.txt`](data/search1.txt). Diffs: [`repro/prs_c.sh`](repro/prs_c.sh), [`repro/prs_ab.sh`](repro/prs_ab.sh) | + +Budget consumed: about 0.6 agent-day (estimated). Bug-fix candidates inspected in depth: 10 of the 15 allowed. + +## 2. Candidate comparison + +| Candidate | Gap reproduced | Real bugs | Independent repos | Existing-tool coverage | Cost | Verdict | +|---|---|---|---|---|---|---| +| A #382 canonical calls | yes: call lost (A1), core capability (A2). For guarded shapes the core gives only advisory OWN051 even with a `call` (A3). For a local leaked through a first-party *borrowing* method the core gives OWN001 once fed a `call` | 0 | 0 | n/a | high: frontend/core boundary, two engines, parity | **NO-GO within current evidence budget** | +| B #406 closure capture | yes: a sync lambda and a local function are silent, the same leak without a lambda is OWN001; the ShareX control is silent | 0 | 0 | n/a | medium | **NO-GO within current evidence budget** | +| C #400 view invalidation | yes, at runtime: stale write lost on reallocation, shifted view after `RemoveAt`; 4 negative controls clean | **2 CONFIRMED** stale-ref defects | **2** (meziantou, dotnet/maui) | built-in NetAnalyzers (`All`): no rule fires. Third-party: NOT_TESTED | medium–high in production: new invalidation semantics, probably an OwnIR change per #400 | **GO**, narrowed slice | + +## 3. Evidence + +### Stale-ref mechanism (the PoC target) + +| # | Repo / PR | Buggy → fixed | File / method | Mechanism | Status | +|---|---|---|---|---|---| +| C-1 | [meziantou/Meziantou.Framework#2825](https://github.com/meziantou/Meziantou.Framework/pull/2825) | `7addb26c` → `cf82931e` | `src/Meziantou.Framework/DictionaryExtensions.cs` `GetOrAdd(dict, key, valueFactory)` and `TryUpdate(dict, key, valueFactory)` | `ref var v = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, …)` (`GetValueRefOrNullRef` for `TryUpdate`), then `v = valueFactory(key)`. The write location is evaluated before the call. A factory that mutates the same dictionary grows it, so the write lands in the old bucket array | **CONFIRMED_BUG**: measured by the author and fixed with regression tests. **Reproduced independently** on the verbatim buggy code ([`data/c_runtime_dictionary.out`](data/c_runtime_dictionary.out)): `returned=computed stored=`. Control (non-reentrant factory) is correct | +| C-2 | [dotnet/maui#37148](https://github.com/dotnet/maui/pull/37148) (issue #36744) | `7b051d1b` → `2db8b6af` | `src/Controls/src/Core/BindableObject.cs` `GetOrCreateContext` | The same ref is held across `CreateContext(property)` → user `DefaultValueCreator` → `SetValue` on other properties → `_properties` grows → stale ref → a `null` placeholder stays → `NullReferenceException` in `ApplyBindings` | **CONFIRMED_BUG**: user report (Telerik RadDataGrid, Android) plus a regression test in the PR. **Shape reproduced** (`stored=`) | +| C-2d | [dotnet/maui#36030](https://github.com/dotnet/maui/pull/36030) | — | same | same defect, earlier fix attempt | DUPLICATE (C-2) | + +Independent stale-ref defects: **2**, in **2** repositories. + +### Other findings + +| # | Candidate | Repo / PR | Status | +|---|---|---|---| +| C-1b | C, different mechanism | Meziantou #2825, the same PR: `GetValueRefOrAddDefault` inserts `default(TValue)` **before** the factory runs, so a throwing factory leaves the key mapped to `null` permanently (`ContainsKey=True value=`, reproduced) | **CONFIRMED_BUG** for an exception-safety mechanism (premature insertion). It is **not** stale-ref invalidation, is not counted for the stale-ref class, and is **not** the PoC target | +| C-3 | C | [rtkelly-labs/Parquet.SourceGenerator#639](https://github.com/rtkelly-labs/Parquet.SourceGenerator/pull/639) (`ed22cb62` → `4ca337a8`): a defensive length check against the caller's concurrent list mutation | UNSUPPORTED: concurrency, not a same-thread use after mutation | +| B-1 | B | KerckhoffsLabs.Pkcs11 pair 60 (corpus 1): a new test whose capture happens in `Task.Run`, so asynchronously | UNSUPPORTED | +| AB-1..5 | A/B | [mono/SkiaSharp#4814](https://github.com/mono/SkiaSharp/pull/4814) (open), [ChrisBrooksbank/Vox#41](https://github.com/ChrisBrooksbank/Vox/pull/41), [thomhurst/ModularPipelines#6634](https://github.com/thomhurst/ModularPipelines/pull/6634), [blxkstxllion/shopkeeper#119](https://github.com/blxkstxllion/shopkeeper/pull/119), [Flakroup/FEx#172](https://github.com/Flakroup/FEx/pull/172) | none has the A or B shape: library-internal ownership, an exception path, a BCL callee, or a mixed PR. UNSUPPORTED (FEx was skimmed only) | +| corpus 1 | A/B/C | 58 pairs: 7 locals gain a release in the fix. None has the A shape, one has the B shape (= B-1), and no pair touches `CollectionsMarshal` | — | + +PLAUSIBLE_UNCONFIRMED sites in corpus 2 with the C-2 shape. They have no fix history and are **not counted**: +- dotnet/macios `NSObject2.cs:733` (`DynamicConformsToProtocol`) and `Runtime.cs:2440` (`SlowIsUserType`); +- maxtoroq/DbExtensions `SqlTable.cs:93` (a reflective factory `Invoke`); +- nightroman/FarNet `Format.cs:483` (a PowerShell `InvokeCode`). + +## 4. Kill-gate ledger + +| Gate | Necessary condition | Cheapest falsifier | Measured observation | Verdict | +|---|---|---|---|---| +| A1 | the frontend loses the call | facts + summaries of `GuardedConsumeSample.cs` ([`repro/a_382_probe.py`](repro/a_382_probe.py), [`data/a_382_probe.out`](data/a_382_probe.out)) | `ForwardDynamic` body = `use forwarded`; `forwarded = no`; caller records not emitted | PASS | +| A2 | the core derives `may` from a `call` | the same probe with a hand-edited `call` | `forwarded: no → may`; the other summaries are unchanged | PASS | +| A3 | the synthetic FN becomes a finding | the same probe | both callers (guard true and false) get only **OWN051 advisory** in both engines (INF-A5). A borrow leak (`Borrow2(d)`, never disposed) is silent on `main` and **OWN001** with the `call`, in both engines. Multi-level definite forwards already give OWN002 on `main` | partial | +| A4 | a real bug of shape A | corpus 1 + 5 leak-fix PRs | 0 | **NO-GO** (budget) | +| B1 | the FN comes from the capture-is-escape rule | [`repro/b_406`](repro/b_406) | the sync lambda and the local function are silent; the no-lambda leak is OWN001 (the `capturedInClosure` rule in `Program.cs`) | PASS | +| B2 | the ShareX control stays silent | the same | silent | PASS (control) | +| B3 | a real bug of shape B | corpus 1 + the same PRs + "captured lambda dispose" queries | 0; the only candidate is asynchronous | **NO-GO** (budget) | +| C1 | an observable effect with clean controls | [`repro/c_runtime_list`](repro/c_runtime_list), [`data/c_runtime_list.out`](data/c_runtime_list.out) | the stale write is lost; the view reads `[2,3,3]` while the list is `[2,3]`; with spare capacity the effect is not observable (contract violation only); 4 controls clean | PASS | +| C2 | existing tools do not cover the pattern | [`repro/c_analyzers`](repro/c_analyzers) | only CA1062 fires, which is unrelated; no rule found by search. Meziantou.Analyzer, Roslynator and ErrorProne.NET are **NOT_TESTED** (package download not authorised; about 30 minutes once authorised) | PASS for what was tested | +| C3 | real bugs of shape C | PR/issue search + diffs | 2 CONFIRMED stale-ref defects in 2 repos, both reproduced | PASS | +| C-scope | the proposed #400 first slice catches them | mechanism comparison | **no**: both are `Dictionary` ref-returns, and the mutation runs inside a callee or a delegate, not as a direct `list.Add` in the method | slice re-targeted | +| C-prec | precision is not unacceptable | crude classification of 81 sites in 27 repos ([`repro/classify_sample.py`](repro/classify_sample.py), [`data/sample_classified.txt`](data/sample_classified.txt)) | 27 sites have nothing between taking the ref and using it; about 4 are plausibly hazardous; the rest are constructors and pure calls, so an "any call" rule would be noisy. **No precision was measured** | NOT MEASURED; this is the first PoC gate | +| C-Own.NET | Own.NET misses them | current extractor + core on the buggy and fixed files | 0 findings, coverage 0/0: the area is not modelled | confirmed (UNSUPPORTED, not a bug in a modelled path) | + +## 5. Recommendation + +`GO-400`, narrowed to the stale-ref mechanism of C-1 and C-2: + +- The threshold of ≥ 2 independently validated defects from different repositories is met. Maintainers fixed both + with regression tests, and both were reproduced on the original code. +- The built-in .NET analyzers do not flag either defect, and Own.NET does not model this area. +- **Not established:** precision; frequency beyond these two 2026 PRs; results from third-party analyzers, CodeQL and + Infer#, all NOT_TESTED. GO therefore authorises a bounded PoC with a precision gate, not production work. + +For #382 and #406, the verdict is NO-GO within the current evidence budget; both issues stay open. For #382 the only +measured detection gain is a local leaked through a first-party borrowing method, and no real witness was found. #304 +is not affected. + +## 6. PoC specification (owner-authorised, bounded) + +**Target (C-1 and C-2 mechanism only).** A ref obtained via `CollectionsMarshal.GetValueRefOrAddDefault` / +`GetValueRefOrNullRef` for dictionary `D` is read or written after an invocation that may run code able to mutate `D` +while the ref is live. Two cases: +- a delegate invocation, the Meziantou shape; +- a first-party method whose body transitively invokes a delegate or mutates `D`, the MAUI shape. + +The ref write `v = f(...)` counts: the location of `v` is evaluated before `f` runs. + +**Gates:** +- Both buggy revisions produce a correctly localised warning: Meziantou `7addb26c` on `GetOrAdd` / `TryUpdate` + (factory overloads), MAUI `7b051d1b` on `GetOrCreateContext`. +- Both fixed revisions (`cf82931e`, `2db8b6af`) are clean. +- Mandatory negative controls, all clean: + - no mutation; + - mutation of a different dictionary; + - a safe factory; + - the ref used only before the potential mutation. +- Not every call between taking the ref and using it is dangerous. +- Precision of at least 80 % on an independent labelled sample. Insufficient data means INCONCLUSIVE. +- Third-party analyzers are compared where available, and NOT_TESTED is never reported as a MISS. +- Reuse of existing OwnIR is preferred, but never at the cost of false semantic claims. + +**Hard KILL:** either real bug not found; a FP on a mandatory safe control that cannot be fixed within budget; too few +precise warnings to be practically useful; or the budget is exceeded. + +**Budget:** one agent-day. No production OwnIR change, no new diagnostic rule, no golden update, no implementation PR. +The PoC ends with `GO-PRODUCTION-DESIGN`, `KILL` or `INCONCLUSIVE`. + +## Limitations + +- One labeller, no blind control. +- The GitHub search was limited to 8 PR/issue queries and one code search of 60 files. The A/B result means "not + found within this budget", not "does not exist". +- CodeQL, Infer# and third-party Roslyn analyzers were NOT_TESTED. +- Corpus 1 is private research data. Its triage output is committed, but re-running the script needs access to the + corpus. diff --git a/docs/evidence/next-bug-class-2026-10/data/a_382_probe.out b/docs/evidence/next-bug-class-2026-10/data/a_382_probe.out new file mode 100644 index 00000000..ca3f1121 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/data/a_382_probe.out @@ -0,0 +1,33 @@ +A1 ForwardDynamic body on main: [{"op": "use", "var": "forwarded", "line": 121}] +A1 caller records emitted: [] + summary BorrowingWrapper [('borrowed', 'no')] + summary ForwardDynamic [('forwarded', 'no')] + summary MaybeClose [('s', 'may')] +A2 with a hand-made call: + summary BorrowingWrapper [('borrowed', 'no')] + summary ForwardDynamic [('forwarded', 'may')] + summary MaybeClose [('s', 'may')] +A3 caller diagnostics with the call (both callers): + python: + warning: [OWN051] cannot verify whether 'GuardedConsumeSample.ForwardDynamic' takes ownership of 'x' (inferred contract: may); optimistically assuming it does � 'x' is not checked past this call [resource: ownership transfer] + warning: [OWN051] cannot verify whether 'GuardedConsumeSample.ForwardDynamic' takes ownership of 'y' (inferred contract: may); optimistically assuming it does � 'y' is not checked past this call [resource: ownership transfer] + warning: [OWN002] IDisposable local 'handoffStream' is used after it is disposed [resource: disposable] + warning: [OWN002] IDisposable local 'finallyStream' is used after it is disposed [resource: disposable] + rust: + warning: [OWN051] cannot verify whether 'GuardedConsumeSample.ForwardDynamic' takes ownership of 'x' (inferred contract: may); optimistically assuming it does — 'x' is not checked past this call [resource: ownership transfer] + warning: [OWN051] cannot verify whether 'GuardedConsumeSample.ForwardDynamic' takes ownership of 'y' (inferred contract: may); optimistically assuming it does — 'y' is not checked past this call [resource: ownership transfer] + warning: [OWN002] IDisposable local 'handoffStream' is used after it is disposed [resource: disposable] + warning: [OWN002] IDisposable local 'finallyStream' is used after it is disposed [resource: disposable] +A3 multi-level definite forward and borrow leak on main: + python: + warning: [OWN002] IDisposable local 'b' is used after it is disposed [resource: disposable] + rust: + warning: [OWN002] IDisposable local 'b' is used after it is disposed [resource: disposable] +A3 borrow leak with a hand-made call: + python: + warning: [OWN002] IDisposable local 'b' is used after it is disposed [resource: disposable] + warning: [OWN001] IDisposable local 'd' is never disposed (leak) [resource: disposable] + rust: + warning: [OWN002] IDisposable local 'b' is used after it is disposed [resource: disposable] + warning: [OWN001] IDisposable local 'd' is never disposed (leak) [resource: disposable] +(work dir: ) diff --git a/docs/evidence/next-bug-class-2026-10/data/c_runtime_dictionary.out b/docs/evidence/next-bug-class-2026-10/data/c_runtime_dictionary.out new file mode 100644 index 00000000..478bae17 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/data/c_runtime_dictionary.out @@ -0,0 +1,4 @@ +meziantou reentrant: returned=computed stored= (bug: True) +meziantou throwing: ContainsKey=True value= +control non-reentrant: stored=ok (ok: True) +maui shape: stored= (bug: True) diff --git a/docs/evidence/next-bug-class-2026-10/data/c_runtime_list.out b/docs/evidence/next-bug-class-2026-10/data/c_runtime_list.out new file mode 100644 index 00000000..1c34e20a --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/data/c_runtime_list.out @@ -0,0 +1,7 @@ +bad: list[0]=10 (stale write lost: True) +bad-spare-capacity: list2[0]=99 (observable: False), v2.Length=1 list2.Count=2 +bad-removeat: v3=[2,3,3] list3=[2,3] +ok-no-mutation: True +ok-other-list: True +ok-no-use-after: True +ok-refreshed: True diff --git a/docs/evidence/next-bug-class-2026-10/data/sample-index.tsv b/docs/evidence/next-bug-class-2026-10/data/sample-index.tsv new file mode 100644 index 00000000..27f8579f --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/data/sample-index.tsv @@ -0,0 +1,60 @@ +ravendb/ravendb src/Corax/Utils/FieldsCache.cs b99f8d67dfc5fee474dc8f7b4d242c52eb640362 +dotnet/efcore src/EFCore/Infrastructure/Internal/LazyLoader.cs e2a2a409d5e803b8e303280e26d34fe40b2169e7 +dotnet/orleans src/Orleans.Core/Messaging/CachingSiloAddressCodec.cs 1c0a84c678a0bfa2b24d3bd602086a2650f2e08f +kurrent-io/KurrentDB src/KurrentDB.Core/Services/VNode/VNodeFSM.cs 9ffa7daafd5968a410c38111ced54069895066ee +TrinityCore/WowPacketParser WowPacketParser/Misc/ZLibHelper.cs f031d5120daee56ecf908998142963b84fc6ab8e +ravendb/ravendb src/Voron/Data/Graphs/Hnsw.cs 41f318ae4c6afde48d25fa65eb2bff007c38b45e +ravendb/ravendb src/Voron/Impl/FreeSpace/FreeSpaceHandling.cs 408d1491f7f27e5126f3882f694445803cfc4b22 +ravendb/ravendb src/Corax/Indexing/IndexWriter.cs c045859b217090c189c6177d81b13a1b56bf2459 +dotnet/macios src/Foundation/NSObject2.cs 7b57eac8945eb7039c72a9dcffeffe15c8cfb31a +dotnet/macios src/ObjCRuntime/Runtime.cs 794d64a341ed9adc88c48130eeefdbf1b5c9089c +ravendb/ravendb src/Corax/Indexing/IndexWriter.IndexEntryBuilder.cs 582edb9d4d09cfe7bde5a8b43cdd0142ba450f8b +ravendb/ravendb src/Raven.Server/Documents/Sharding/BucketStatsHolder.cs bf7536896a0daf6191045581b0d1ef2206594d93 +lay295/TwitchDownloader TwitchDownloaderCore/Models/Render/SectionImageCache.cs 21363b6c49e66a2b6cf2c2b6fee12d7a4f7f13ab +ravendb/ravendb src/Voron/Data/Containers/Container.cs ef1b762d2fbab07da09696cf1ce14e47f94eb693 +linq2db/linq2db Source/LinqToDB/Async/AsyncExtensions.cs ca1bb65c9e344fe95c5d230fa4fba217a24054cf +friflo/Friflo.Engine.ECS src/ECS/Utils/MapUtils.cs b0cf6bbd6411a716d6ac6a30fa10f43632916003 +lay295/TwitchDownloader TwitchDownloaderWPF/Utils/LiveVideoMonitor.cs 3d1609e3fc2db24c2065202784424476ea1efe39 +ravendb/ravendb src/Voron/Debugging/StorageReportGenerator.cs 6049e3daf2d6f6ea0f23a780e0fd7a2e9773b654 +Crypto137/MHServerEmu src/MHServerEmu.Core/Extensions/DictionaryExtensions.cs d6d16412e091b7cb2c54e1a46c08792e8cec346e +friflo/Friflo.Engine.ECS src/ECS/Base/CompilerServices.cs b391297f1fba13bfeb46ce22d526501fd9331a89 +lay295/TwitchDownloader TwitchDownloaderCore/Models/VideoQualities.cs a747e73fc70296a1917669adcedf55c9f19ffda3 +reactiveui/Akavache src/Akavache.Core/InMemoryBlobCacheBase.cs 516d8ae70c925e5ac63ec49c5d1027bca3a304d8 +sn4k3/UVtools UVtools.Core/MeshFormats/WRLMeshFile.cs e613703f844276c85ef16b50282f0b9626d2ed8d +sn4k3/UVtools UVtools.Core/MeshFormats/OBJMeshFile.cs 0bed0c6eefa923ec5a4546c2323d7052f6f80471 +sn4k3/UVtools UVtools.Core/MeshFormats/OFFMeshFile.cs 2c58c4dcb4ead402a4b19e3e7c545798dd6bc948 +bleroy/lunr-core LunrCore/Extensions/DictionaryExtensions.cs 47149c02a8ee9b079ff5a3c37f60a368bb2cb828 +sn4k3/UVtools UVtools.Core/MeshFormats/AMFMeshFile.cs 98ae1b4be63c7abbd0dc72c6d7afcbb46e18dd0b +lay295/TwitchDownloader TwitchDownloaderCore/ChatRenderer.cs 1bab1ec8923e152576b1e926e7df4b2e7dbca810 +sn4k3/UVtools UVtools.Core/MeshFormats/PLYMeshFile.cs 80fbfa588ebf5229ea37d66cf59cdc5aeb29402c +lay295/TwitchDownloader TwitchDownloaderCore/Tools/HighlightIcons.cs d4b5148b39fccfe60b6d30eb07d931a558bc1c41 +zupat/related_post_gen csharp/Program.cs 972cda2b0ebc9ee61481f5452f5984e6ddb42eb9 +meziantou/Meziantou.Framework src/Meziantou.Framework.TextDiff/HistogramDiff.cs 6352afc49d7f4d2aa35765d8771f65da3db5000d +OctoAwesome/octoawesome OctoAwesome/OctoAwesome/InteractService.cs 526508e83c0c5bbcad9e5b90a61cfa5f19ec27f9 +zupat/related_post_gen csharp_con/Program.cs b643641e7a4a74bb69d157809fb5d334648588ae +friflo/Friflo.Engine.ECS src/ECS/EventFilter/EntityEvents.cs a027ab8092782e9df96e4122642549c9cb6028c9 +meziantou/Meziantou.Framework src/Meziantou.Framework.TextDiff/PatienceDiff.cs 43c6805ad7fb56c044e497eaea8b1e2145c812ff +opensim/opensim OpenSim/Framework/ExpiringKey.cs f9261b642d17c0d9a8c05d4d9f561e67829f7281 +opensim/opensim OpenSim/Framework/ExpiringCacheOS.cs 9291f9855f7c7a6b8aa00cbd7ec330f94508fe39 +friflo/Friflo.Json.Fliox Engine/src/ECS/Utils/MapUtils.cs b0cf6bbd6411a716d6ac6a30fa10f43632916003 +meziantou/Meziantou.Framework src/Meziantou.Framework.TextDiff/MyersDiff.cs 3e9c51c888d5b91477b69dabd3cbfae64c40fca3 +nightroman/FarNet Vessel/Actor.cs 93bbd29c67df20475d2e2da706c4bd8813030988 +VahidN/DNTCommon.Web.Core src/DNTCommon.Web.Core/Collections/DictionaryExtensions.cs 469b5819ed169bc0186f905bc91638a65c936bb6 +zamgi/m3u8 m3u8.download.manager/[Common]/UrlsUniquer.cs 87383d98550222d828a6960adfe6b928baa07c7f +friflo/Friflo.Json.Fliox Engine/src/ECS/Base/CompilerServices.cs b391297f1fba13bfeb46ce22d526501fd9331a89 +meziantou/Meziantou.Framework src/Meziantou.Framework.Toml/Model/TomlTable.cs 496d234650e47dd72e8921735ed48b6ea39b5503 +maxtoroq/DbExtensions src/DbExtensions/SqlTable.cs 473a62219ebb47af5f0478e598ef986a5dd5be94 +CollapseLauncher/Collapse CollapseLauncher/Classes/Plugins/PluginManager.cs 6888a7b215389e380b0579100ffae0ab224a1123 +dubiousconst282/DistIL src/DistIL/Utils/Collections/CollectionsExt.cs 977ab391d0dd51a67ed88afb1f00089cf18d048a +maxtoroq/DbExtensions src/DbExtensions/PocoMapper.cs c70cd6d56a77e76bd8aacbdda2d562771257f391 +opensim/opensim OpenSim/Framework/AvatarAppearance.cs 9c4b0c2ef0d2f3d729a65244a1e7b4e95f9e7313 +xoofx/Tomlyn src/Tomlyn/Model/TomlTable.cs 610cb62b4e75cbf7e9efa94431b7e945d4e5a89b +rampaa/JL JL.Core/Deconjugation/DeconjugatorUtils.cs 560102acea3b7416cb07241a9727243936126886 +drewnoakes/string-theory StringTheory.Analysis/HeapAnalyzer.cs 9f993d9457df26a23ff7e8b853700e8f34058614 +opensim/opensim OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs f496248e197b7d11e7ed8e709ce99298ba5d00ee +AAEmu/AAEmu AAEmu.Game/Models/Game/Items/Loots/LootPack.cs 8a6bf9e6e749308808d6ae022ac31ae481c7cfc5 +nightroman/FarNet PowerShellFar/Format.cs b144c8b6c42ecc4c6dd241d2f0ea878a76a2694e +TKMM-Team/Tkmm src/Tkmm.Core/TkOptimizer/TkOptimizerStore.cs 40487528367c2d0fa688f6425114f82ea7f324d8 +CollapseLauncher/Collapse CollapseLauncher/Classes/Helper/WindowUtility.cs af1981e3ed4140c960f34d47d9a14273b3b7fb4b +alnkesq/AppViewLite src/AppViewLite/UserPairEngagementCache.cs 902c759e4504cea3f99425f944b50aebbc387c06 +Treit/MiscBenchmarks IncrementDictionaryValue/Benchmark.cs e15b7a4ba0d13c3e2037eaa56be6018879e48c2b diff --git a/docs/evidence/next-bug-class-2026-10/data/sample_classified.txt b/docs/evidence/next-bug-class-2026-10/data/sample_classified.txt new file mode 100644 index 00000000..1fd41c15 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/data/sample_classified.txt @@ -0,0 +1,55 @@ +sites 81 repos 27 {'CALL': 7, 'SELF': 45, 'DELEGATE': 2, 'NONE': 27} +ravendb/ravendb src/Corax/Utils/FieldsCache.cs 27 fieldRootPage CALL ['tree.CompactTreeFor', 'tree.GetLookupRootPage'] +dotnet/efcore src/EFCore/Infrastructure/Internal/LazyLoader.cs 123 refIsLoadingValue SELF ['TaskCompletionSource'] +dotnet/efcore src/EFCore/Infrastructure/Internal/LazyLoader.cs 197 refIsLoadingValue SELF ['TaskCompletionSource'] +dotnet/orleans src/Orleans.Core/Messaging/CachingSiloAddressCodec.cs 51 cacheEntry SELF ['ReadSiloAddressInner', 'SharedCache.GetOrAdd', 'payloadSpan.SequenceEqual', 'payloadSpan.ToArray', 'result.InternalSetConsistentHashCode', 'static'] +dotnet/orleans src/Orleans.Core/Messaging/CachingSiloAddressCodec.cs 115 cacheEntry DELEGATE ['PooledBuffer', 'PurgeStaleEntries', 'SharedCache.GetOrAdd', 'WriteSiloAddressInner', 'Writer.Create', 'duplicates.'] +kurrent-io/KurrentDB src/KurrentDB.Core/Services/VNode/VNodeFSM.cs 41 node CALL ['.IsAssignableFrom'] +TrinityCore/WowPacketParser WowPacketParser/Misc/ZLibHelper.cs 22 zStream SELF ['InflateInit2'] +TrinityCore/WowPacketParser WowPacketParser/Misc/ZLibHelper.cs 31 zStream SELF ['Inflate', 'InflateInit2', 'fixed'] +ravendb/ravendb src/Voron/Data/Graphs/Hnsw.cs 543 nodeIdx SELF ['AllocateNodeIndex', 'CopyNodeFromCache', 'GetNodeByIndex', 'InvalidOperationException', 'Node.Decode', 'TryGetLocationForNode'] +ravendb/ravendb src/Voron/Data/Graphs/Hnsw.cs 1106 postingList SELF ['Convert.ToBase64String', 'PortableExceptions.Throw', 'VectorId', '_nodesByVectorId.TryGetValue', '_searchState.GetNodeIndexById', '_searchState.Llt.Allocator.Release'] +ravendb/ravendb src/Voron/Impl/FreeSpace/FreeSpaceHandling.cs 231 metadata SELF ['SectionMetadata'] +ravendb/ravendb src/Voron/Impl/FreeSpace/FreeSpaceHandling.cs 305 metadata SELF ['SectionMetadata'] +ravendb/ravendb src/Voron/Impl/FreeSpace/FreeSpaceHandling.cs 313 nextMetadata SELF ['SectionMetadata'] +ravendb/ravendb src/Corax/Indexing/IndexWriter.cs 657 termLocation SELF ['EntriesModifications', 'field.Storage.AddByRef', 'field.Storage.GetAsRef'] +ravendb/ravendb src/Corax/Indexing/IndexWriter.cs 676 longTermLocation SELF ['EntriesModifications', 'field.Storage.AddByRef', 'field.Storage.GetAsRef', 'indexes'] +ravendb/ravendb src/Corax/Indexing/IndexWriter.cs 692 doubleTermLocation SELF ['EntriesModifications', 'field.Storage.AddByRef', 'field.Storage.GetAsRef'] +ravendb/ravendb src/Corax/Indexing/IndexWriter.cs 709 termLocation SELF ['EntriesModifications', 'field.Storage.AddByRef', 'field.Storage.GetAsRef'] +dotnet/macios src/Foundation/NSObject2.cs 730 map CALL ['CollectionsMarshal.GetValueRefOrAddDefault'] +dotnet/macios src/Foundation/NSObject2.cs 733 result SELF ['DynamicConformsToProtocol'] +dotnet/macios src/ObjCRuntime/Runtime.cs 2440 result SELF ['SlowIsUserType'] +ravendb/ravendb src/Corax/Indexing/IndexWriter.IndexEntryBuilder.cs 178 termLocation SELF ['EntriesModifications', 'Math.Max', 'NativeList', 'field', 'field.EntryToTerms.AddByRefUnsafe', 'field.EntryToTerms.EnsureCapacityFor'] +ravendb/ravendb src/Corax/Indexing/IndexWriter.IndexEntryBuilder.cs 241 doublesTermsLocation SELF ['CollectionsMarshal.GetValueRefOrAddDefault', 'EntriesModifications', 'field.Storage.AddByRef', 'field.Storage.GetAsRef'] +ravendb/ravendb src/Corax/Indexing/IndexWriter.IndexEntryBuilder.cs 251 longsTermsLocation SELF ['EntriesModifications', 'doublesTerm.Addition', 'field.Storage.AddByRef', 'field.Storage.GetAsRef'] +lay295/TwitchDownloader TwitchDownloaderCore/Models/Render/SectionImageCache.cs 26 bucket SELF ['SectionImage', 'bucket.RemoveAt'] +ravendb/ravendb src/Voron/Data/Containers/Container.cs 1108 valRef SELF ['NotSupportedException'] +linq2db/linq2db Source/LinqToDB/Async/AsyncExtensions.cs 593 grouping SELF ['Add', 'TryGetValue'] +lay295/TwitchDownloader TwitchDownloaderWPF/Utils/LiveVideoMonitor.cs 70 state SELF ['VideoState'] +lay295/TwitchDownloader TwitchDownloaderCore/Models/VideoQualities.cs 236 count CALL ['Debug.Assert', 'constructQuality', 'qualities.Add'] +reactiveui/Akavache src/Akavache.Core/InMemoryBlobCacheBase.cs 139 value SELF ['_keyToType.TryGetValue', '_typeIndex.TryGetValue', 'previousSet.Remove'] +reactiveui/Akavache src/Akavache.Core/InMemoryBlobCacheBase.cs 186 value SELF ['_keyToType.TryGetValue', '_typeIndex.TryGetValue', 'previousSet.Remove'] +sn4k3/UVtools UVtools.Core/MeshFormats/WRLMeshFile.cs 93 index SELF ['MeshTextWriter', 'writer.Append', 'writer.CopyTo'] +sn4k3/UVtools UVtools.Core/MeshFormats/OBJMeshFile.cs 91 index SELF ['MeshTextWriter', 'writer.Append', 'writer.CopyTo'] +sn4k3/UVtools UVtools.Core/MeshFormats/OFFMeshFile.cs 91 index SELF ['MeshTextWriter', 'writer.Append', 'writer.CopyTo'] +sn4k3/UVtools UVtools.Core/MeshFormats/AMFMeshFile.cs 108 index SELF ['MeshTextWriter', 'writer.Append', 'writer.CopyTo'] +lay295/TwitchDownloader TwitchDownloaderCore/ChatRenderer.cs 2166 fallbackPaint SELF ['SKPaint', '_progress.LogVerbose', '_progress.LogWarning', 'fontManager.MatchCharacter'] +sn4k3/UVtools UVtools.Core/MeshFormats/PLYMeshFile.cs 104 index SELF ['BinaryPrimitives.WriteSingleLittleEndian', 'MeshStream.Write', 'MeshTextWriter', 'writer.Append', 'writer.CopyTo'] +lay295/TwitchDownloader TwitchDownloaderCore/Tools/HighlightIcons.cs 273 iconPaint SELF ['SKPaint'] +zupat/related_post_gen csharp/Program.cs 37 stack SELF ['LinkedList'] +zupat/related_post_gen csharp_con/Program.cs 38 stack SELF ['LinkedList'] +opensim/opensim OpenSim/Framework/ExpiringCacheOS.cs 343 ret CALL ['Util.GetTimeStampMS'] +VahidN/DNTCommon.Web.Core src/DNTCommon.Web.Core/Collections/DictionaryExtensions.cs 20 currentValue SELF ['TValue'] +zamgi/m3u8 m3u8.download.manager/[Common]/UrlsUniquer.cs 24 cnt SELF ['_Urls.TryGetValue'] +meziantou/Meziantou.Framework src/Meziantou.Framework.Toml/Model/TomlTable.cs 264 indexRef CALL ['CollectionsMarshal.AsSpan'] +maxtoroq/DbExtensions src/DbExtensions/SqlTable.cs 67 table SELF ['SqlTable'] +maxtoroq/DbExtensions src/DbExtensions/SqlTable.cs 93 table DELEGATE ['.Invoke', '.MakeGenericMethod', 'SqlTable'] +CollapseLauncher/Collapse CollapseLauncher/Classes/Plugins/PluginManager.cs 109 dict SELF ['Dictionary'] +opensim/opensim OpenSim/Framework/AvatarAppearance.cs 545 atlst SELF ['List', 'prev.ItemID.Equals'] +rampaa/JL JL.Core/Deconjugation/DeconjugatorUtils.cs 83 list SELF ['Debug.Assert', 'List'] +rampaa/JL JL.Core/Deconjugation/DeconjugatorUtils.cs 106 rulesForConTag SELF ['Debug.Assert', 'List'] +drewnoakes/string-theory StringTheory.Analysis/HeapAnalyzer.cs 82 tally SELF ['ObjectTally'] +drewnoakes/string-theory StringTheory.Analysis/HeapAnalyzer.cs 173 tally SELF ['.Add', 'ObjectTally', '_heap.GetSegmentByAddress', 'strSeg.GetGeneration'] +opensim/opensim OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs 223 spkt SELF ['Stack', 'spkt.Push'] +nightroman/FarNet PowerShellFar/Format.cs 483 result CALL ['A.InvokeCode'] +TKMM-Team/Tkmm src/Tkmm.Core/TkOptimizer/TkOptimizerStore.cs 44 optimizerProfile SELF ['TkOptimizerProfile'] diff --git a/docs/evidence/next-bug-class-2026-10/data/search1.txt b/docs/evidence/next-bug-class-2026-10/data/search1.txt new file mode 100644 index 00000000..48739a57 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/data/search1.txt @@ -0,0 +1,218 @@ +### prs :: "CollectionsMarshal" stale --language C# +KinsonDigital/Velaptor #1208 open perf: replace ConcurrentDictionary with Dictionary + CollectionsMarshal in content loaders and KeyboardDataService +tedd/Tedd.DictionaryUtils #13 open ⚡ Bolt: AddExtensions.GetOrAdd Optimization +thomhurst/Respire #1565 merged chore(deps): update dependency polyfill to 11.4.5 +BasisVR/Basis #1078 open Feat/cilbox network behaviour overrides +dotnet/runtime #134313 merged Pool TLS 1.3 session tickets per host instead of keeping one +NethermindEth/nethermind #14450 merged perf(zkevm): trim per-transaction tracer reads and journal clearing in the guest +meziantou/Meziantou.Framework #2825 merged Do not hold a dictionary ref across a user-supplied value factory +dotnet/maui #37148 merged Fix NullReferenceException in ApplyBindings when DefaultValueCreator mutates other BindableProperties +rtkelly-labs/Parquet.SourceGenerator #639 merged fix(emitter): check the list span length before the unchecked write loop +NethermindEth/nethermind #14222 merged perf(zkevm): OptimizedDictionary/OptimizedHashSet with 64-bit guest hash tables on the state path +NethermindEth/nethermind #13459 merged fix(eip8141): restore the snapshot on every frame-tx pre-flight rejection +Yesanith/RegionsOfXIV #15 merged Fix Turkish letters rendering and improve font handling +dotnet/runtime #131142 merged Avoid array allocations when evaluating connections for eviction +nasytnyk/csharp-mistakes-explained #14 merged Add exhibit #0040: the-span-left-behind +NethermindEth/nethermind #13967 merged fix(rpc): serve code written by SETCODEFROM and EIP-7702 through code overrides +NethermindEth/nethermind #13312 merged perf(bal): bypass journaling for immutable parent reads +alexswi/LinkPulse #28 merged refactor: clarify presence count semantics and halve dict lookups +dotnet/skills #1134 merged Weekly plugin version sync +NethermindEth/nethermind #12998 merged perf(state): unswitch tracing branches in commit loops +Cysharp/ObservableCollections #110 merged Fix `List` to `ListView` casting bug on .NET Framework +XeldarAlz/FFXIV-Aetherphone #161 merged feat(uno): house rules +ParadiseEngine/ParadiseEngine #241 merged A frame graph over the command stream: named events, owned targets, derived reads, inferred store ops, and a feature seam +dotnet/maui #36030 closed [Controls] Fix BindableObject DefaultValueCreator re-entrancy regression +open-astro/openastro-ara #810 merged perf: §64/§59 — StarDetector measures only the brightest-N when capped +NethermindEth/nethermind #13447 merged Merge master into eip8141-frame-txs-devnet7 +b-editor/beutl #1990 merged fix(engine): dispose truncated FormattedText glyph resources on shrink +Toys0125/Basis #17 closed Ci/sync sim tests +dotnet/orleans #10693 open feat(messaging): add durable grain inbox and outbox messaging +ARKlab/Ark.Tools #1113 merged chore(deps): update all non-major dependencies +NethermindEth/nethermind #12018 closed feat(witness): generate the storage witness from the world-state scope +### prs :: "CollectionsMarshal.AsSpan" fix --language C# +dotnet/skills #1248 open test(dotnet-diag): strengthen evaluation quality +Doraku/DefaultDocumentation #302 open Bump Polyfill from 11.4.3 to 11.4.5 +Doraku/DefaultUnDo #79 open Bump Polyfill from 11.4.3 to 11.4.5 +tedd/Tedd.HashSetExtensions #5 open 📜 Codex: Documentation Synchronization and Architectural Articulation +Doraku/DefaultCSharp #156 open Bump Polyfill from 11.4.3 to 11.4.5 +ChrisPulman/Spectre.Console.Rx #279 open Update dependency Polyfill to 11.4.5 +dailydevops/extensions.test #1509 open chore(deps): update dependency polyfill to 11.4.5 +ChrisPulman/IoT-DriverCore #146 open Update dependency Polyfill to 11.4.5 +spectreconsole/spectre.console #2224 open chore: Update dependency Polyfill to 11.4.5 +reactiveui/ReactiveUI.Validation #988 open chore(deps): update dependency polyfill to 11.4.5 +dailydevops/arguments #827 open chore(deps): update dependency polyfill to 11.4.5 +RocketSurgeonsGuild/MSBuild.Targets #1621 open Update dependency Polyfill to 11.4.5 +RocketSurgeonsGuild/Testing #2322 open Update dependency Polyfill to 11.4.5 +Cysharp/R3 #395 open fix: stop assuming List's private field layout (#394) +MarcelRoozekrans/roslyn-codelens-mcp #497 merged build(deps): remove NetFabric.Hyperlinq.Analyzer +code-of-chaos/CodeOfChaos.Unions #128 open Chore(deps): Bump Polyfill from 11.3.0 to 11.4.5 +ChrisPulman/XamlConverters #112 open Update dependency Polyfill to 11.4.5 +dwierzbicki11/SlavicGame #357 merged perf: retire menu vertex buffers without device stall +NethermindEth/nethermind #14626 merged Collect a transaction's account changes without allocating +BasisVR/Basis #1078 open Feat/cilbox network behaviour overrides +BasisVR/Basis #1067 open fix(sdk): survive platform switches during multi-target bundle builds +CodeMazeBlog/CodeMazeGuides #2261 merged Array Slicing in C#: retarget SliceArrays to .NET 10 and fix the crash on run +dotnet/skills #1309 open Complete the Vally 0.18 evaluation migration +alttpr/csrando #35 open Fix randomizer hanging with conservative door search optimizations and targeted DungeonKeySolver integration +RocketSurgeonsGuild/Conventions #2449 open Update dependency Polyfill to v11 +thomhurst/Sourcy #142 merged Update dependency Polyfill to 11.4.5 +Zektopic/sharpemu #85 closed ⚡ Bolt: Optimize TryValidateRange bounds checks in VirtualMemory +RocketSurgeonsGuild/LaunchPad #3031 open Update dependency Polyfill to v11 +b-editor/beutl #3057 merged refactor!: remove CoreListMarshal from Beutl.Core +RocketSurgeonsGuild/Extensions #1961 open Update dependency Polyfill to v11 +### issues :: "CollectionsMarshal.AsSpan" bug --language C# +Cysharp/ObservableCollections #128 open Update TargetFrameworks: add net10.0 now, drop net6.0/net8.0 after November 2026 +naudio/NAudio #1419 closed [v3.0] Mp3FileReaderBase - CurrentTime setter does not update mp3Stream.Position on files with VBR header +credfeto/credfeto-dotnet-codeanalysis-override #44 closed IniFile parsing: quadratic ImmutableArray.Add accumulation and redundant line-buffer copies +AvaloniaUI/Avalonia #8764 open For Avalonia.Controls.ListBox for Items Property, Support for IList without non-generic IList +SharpAstro/tianwen #1343 closed Audit: duplicated code and guideline violations in the planetary wave of 2026-10-08 +Cysharp/R3 #142 closed SystemRuntime.CompilerServices.Unsafe missing +pal-tamas/rask #683 closed Builder surface is 18% slower than the factory it replaces — the unconditional reset of five shared props +dotnet/runtime #114262 open [net9] System.Private.CoreLib occurs InvalidOperationException and InvalidCastException +dotnet/runtime #81523 open Reconsider version checks in collections? +richlander/dotnet-inspect #1142 closed Decompiler ledger frontier evidence burndown +pedrosakuma/B3MatchingPlatform #342 closed perf: profile SortedDictionary in LimitOrderBook before any container swap (#17 go/no-go) +dotnet/runtime #115400 closed [API Proposal]: BitArray.AsBytes() +SciSharp/LLamaSharp #891 closed [BUG]: KernelMemory.AskAsync() does not work - exception: object reference not set to an instance of an object +dotnet/runtime #111813 closed [API Proposal]: Expose a way to get either directly to the backing array of an ImmutableArray.Builder, or get it as a span. +ltrzesniewski/InlineIL.Fody #32 closed FieldRef to generic class can not be resolved +apache/arrow-adbc #1690 closed csharp: native memory leaks in CAdbcDriverImporter.cs +dotnet/runtime #96876 closed CollectionsMarshal.AsSpan assertion failure in System.Text.Json tests +dotnet/roslyn #70656 closed IDE0305 recommends collection expression that results in worse IL +dotnet/runtime #76205 closed LINQ Usage Survey +### issues :: "CollectionsMarshal" "Add" span invalid --language C# +ais-dotnet/Ais.Net #200 open Process various AI perf suggestions +dotnet/runtime #41418 open Enumerating type safety guarantees in MemoryMarshal and friends +AvaloniaUI/Avalonia #8764 open For Avalonia.Controls.ListBox for Items Property, Support for IList without non-generic IList +castleproject/Core #663 open Support by-ref-like (`ref struct`) parameter types such as `Span` and `ReadOnlySpan` +dotnet/runtime #101244 closed [API Proposal]: Expose flattened index get/set on Array +MihuBot/runtime-utils #1374 open [JitDiff X64] xtqqczze/dotnet-runtime/memorymarshal-getspan +dotnet/runtime #114262 open [net9] System.Private.CoreLib occurs InvalidOperationException and InvalidCastException +dotnet/runtime #14477 open [Proposal] Use B+-trees instead of AVL trees for Immutable Collections +SciSharp/LLamaSharp #891 closed [BUG]: KernelMemory.AskAsync() does not work - exception: object reference not set to an instance of an object +dotnet/runtime #56773 closed Optimize `CollectionsMarshal.AsSpan` with inlining and bounds check removal +dotnet/runtime #27062 closed Developers can use CollectionsMarshal ref accessors for Dictionary +### prs :: dispose captured lambda leak --language C# +forge-trust/AppSurface #884 open feat(evidence): add bounded HTTP readiness and Aspire fixtures +dotnet/maui-labs #178 open [AI] Windows AI chat and structured JSON support +openai/openai-dotnet #1431 open Add Content-Type parameter to Multipart convenience methods +refusedguy/Harbor-Harness #524 merged fix(overlays): OverlayController detaches from the singleton stack on dispose (#476) +decentraland/unity-explorer #9909 open fix: texture disk-cache use-after-free during async writes +microsoft/ai-dev-gallery #545 open [Fix]IDisposable Analyzer Warnings +openai/openai-dotnet #1349 open Migrate old CollectionResult/AsyncCollectionResult to AsyncStreamingCollectionResult for streaming in Responses/Audio/Images +microsoft/aspire #18770 open Add preview Aspire.Mcp.Client integration +CommunityToolkit/Maui #2897 open Android service update +Systemorph/MeshWeaver #6311 merged fix(mesh): copy and move deadlines no longer retain the handler's hub +decentraland/unity-explorer #10407 open feat: avatar preview renderer parity with the Babylon preview +community-outpost/GenHub #540 open feat(online): joinable LAN servers for zerohour with virtual LAN tab +HartsyAI/HartsyInference #201 merged Engine: synthesizer and transcriber runner leases (voice agent PR8a) +microsoft/Agent365-dotnet #224 open fix: propagate CancellationToken through async MCP tooling and observability call chains +community-outpost/GenHub #600 open feat(online): Generals Online lobby browser tab with CRC matchmaking +zacala1/InterprocessMemory #3 merged Fix Linux lock-owner detection and lock-wait robustness; opt-in lock takeover; crash-recovery tools; narrow Dispose races +Systemorph/MeshWeaver #996 merged A pending Rx timer kept disposed hubs alive — in four watchers, three of them on the live thread path +roo-oliv/monodreams #120 open feat(demos): deterministic fixed-step clock for headless runs +HavenDV/H.NotifyIcon #281 merged fix(winui): release second-window context menu +squirix/squirix #544 merged chore: fix Rider findings across server and test projects +NethermindEth/nethermind #14220 open [WIP] Lean proof and devp2p benchmarks +community-outpost/GenHub #669 open feat(genhotkeys): add tooltip and cameo customization with in-game replica preview +BookBeat/knightbus #193 merged fix(knightbus): release singleton lock when the receiver stops +unoplatform/uno #24236 open fix(binding): Drop dead INotifyPropertyChanged subscriptions +El1rans/porchlight #10 merged feat(lighting): RGB control via OpenRGB +abpframework/abp #26122 merged Fix event handler leak in ChildUnitOfWork +pnp/Microsoft365-Analytics-Insights #709 merged Importer: run the daily usage reports in the background, off the import loop, so Copilot/audit data isn't held behind them (#706) +unoplatform/uno #23754 open feat(webview2): Improve API parity across platforms +tomwolfgang/piper #44 merged Relay response bodies as they arrive instead of buffering them whole +parse-community/Parse-SDK-dotNET #411 open feat: Add livequery support +### prs :: "local function" dispose leak --language C# +dotnet/runtime #133659 open Invoke debugger function evaluations through managed UCO +IvanJosipovic/KubeUI #1565 open feat: open Properties view follows list selection +valence-works/elsa-control #845 open spike: AC1 Shared shell memory and cold-start harness (#798) +JKamsker/LiteDB #133 open Add explicit transaction handles and operation lifetime protection +litedb-org/LiteDB #3085 open Add thread-independent transaction handles (#3064) +SergeyTeplyakov/ErrorProne.NET #288 open Add lightweight disposable ownership analysis +dotnet/roslyn #20111 open Fix cancellation leaks +jafin/AngleSharp #3 open Keep auxiliary browsing contexts alive until their opener is gone +microsoft/node-api-dotnet #500 open Improve object safety +linq2db/linq2db #5959 open Coerce ClickHouse date operands to DateTime64 before the epoch functions +litedb-org/LiteDB #3087 open Reuse Shared transaction-handle holder threads and child wrappers +unoplatform/uno #23313 open fix(SpeechRecognizer): report WASM recognition outcomes via Status instead of throwing +vein-lang/vein #295 open Feature/generics runtime +decentraland/unity-explorer #9059 open feat: poc enable comms for PEXs interacting with the authoritative-server +Yeusepe/Yeusepes-Modules #8 open Add option to trigger song recognition via VRCOSC UI +cyrildewit/morti #24 open Bump the nuget group with 13 updates +cyrildewit/morti #23 open Bump Aspire.Hosting.AppHost and 12 others +microsoft/aspire #19521 open Add terminal-driven CLI smoke scenarios +bitfoundation/bitplatform #12426 open Use fast invoke JS extension methods in sync calls in BlazorUI (#9917) +CommunityToolkit/Maui #2502 open Add support for local files and package resources to MediaElement +microsoft/Agent365-Samples #313 open feat(nodejs): Add Google ADK sample agent with Microsoft OpenTelemetry distro +mrazza/smoc #40 open feat(cast): add Google Cast support and :output command +EvilBeaver/OneScript #1745 open ФоновоеЗадание.Отменить() +nobigllebowski/Veil #24 open Bump the dotnet group with 20 updates +dotnet/runtime #131544 open Fix EventPipe dispose-in-callback deadlock +unoplatform/uno #23027 open fix(xbind): Observe INPC through a static-class root +HampusVictoren/trading-agent-system #73 open Bump the nuget group with 9 updates +Belphemur/SoundSwitch #2475 open feat(banner): customize monitor, color, and scale +bitfoundation/bitplatform #13856 open feat(blazorui): add seekable PDF stream sources #13702 +thrtn70/WatchDog #82 open fix: automated audit fixes — CI build, security, and resource leaks +### prs :: "not disposed" stream passed helper --language C# +sytone/botnexus #4968 open test(process): avoid waiting on a disposed root handle +MDA2AV/ioxide #383 open http3: a request whose body does not match its content-length is refused with H3_MESSAGE_ERROR instead of being served +bitfoundation/bitplatform #13856 open feat(blazorui): add seekable PDF stream sources #13702 +mono/SkiaSharp #4814 open [memory-leak] Fix SKStreamAsset leak in ToHarfBuzzBlob copy branch +dotnet/android #13078 open [Unsafe] Opt tooling into updated C# memory-safety rules +adamhathcock/sharpcompress #1454 open Reduce archive allocations across types +JKamsker/DotBoxD #1434 open [surprise-red-test] RpcPeer disposal deadlocks when outbound stream callbacks await teardown +couchbase/couchbase-net-client #221 open NCBC-3625: Add GetAllReplicasStreamAsync +linq2db/linq2db #6014 open SignalR remote transport: cancellable streaming calls, concurrency limits, size pre-flight, restartable DI connection +microsoft/durabletask-dotnet #814 open Honor explicit abandonment in the in-process test host +ysy95109/jellyfin-plugin-metatube #8 open Propagate cancellation through enrichment and tasks, and give HTTP responses explicit owners +bjorkstromm/azure-functions-test-framework #143 open Capture mixed HTTP + output bindings via GetOutputData() (#142) +dotnet/maui-labs #178 open [AI] Windows AI chat and structured JSON support +openai/openai-dotnet #1431 open Add Content-Type parameter to Multipart convenience methods +openai/openai-dotnet #1226 open Defer streaming request content creation until enumeration +NethermindEth/nethermind #14727 open fix(debug): classify transaction native tracer configuration errors +dotnet/android #13150 open [Build.Tasks] Emit resource designers with System.Reflection.Metadata +Azure/durabletask #1402 open Add opt-in guard for duplicate awaited sub-orchestration IDs +NethermindEth/nethermind #14574 open fix(debug): preserve block-number timeout error precedence +sbroenne/mcp-server-excel #1095 open feat(cli): support incremental batch commands over stdin +dotnet/maui #38111 open [Android] Fix ObjectDisposedException when Glide restarts stream images +openai/openai-dotnet #1349 open Migrate old CollectionResult/AsyncCollectionResult to AsyncStreamingCollectionResult for streaming in Responses/Audio/Images +NethermindEth/nethermind #14834 open fix(debug): validate precompile moves before traceCall fees +microsoft/vstest #16445 open Stop sending after a partial frame write +csmashe/typewhisper-linux #119 open Send dictionary terms whole to Gladia +NethermindEth/nethermind #14522 open feat: EIP-8437 ethp2p binding with authenticated broadcast +mkelley88/NO-TIIDE #9 open Harden file-open handler: dialog result, reader disposal, error handling +litedb-org/LiteDB #3059 open Keep caller streams unchanged by stream open, checkpoint and dispose +oras-project/oras-dotnet #328 open feat: implement File Store +Gibbs-Morris/mississippi #876 open Route Aqueduct messages to connected users +semver: breaking +### prs :: fix "undisposed" stream leak --language C# +mono/SkiaSharp #4814 open [memory-leak] Fix SKStreamAsset leak in ToHarfBuzzBlob copy branch +dotnet/dotnet-api-docs #12659 open Fix Process standard stream examples to avoid resource leaks and add ownership remarks +blxkstxllion/shopkeeper #119 merged Fix void credit-balance leak, void stock lost-update, refund number race, stream leaks, invitation rate limiting +Flakroup/FEx #172 merged Fix FtpDownloader unbounded retry, undisposed responses and shared retry counter +unoplatform/uno.monaco.editor #53 open fix(tests): probe each static server candidate to readiness +dotnet/maui-labs #178 open [AI] Windows AI chat and structured JSON support +ClickHouse/clickhouse-cs #332 open BinaryImport +ChrisBrooksbank/Vox #41 merged Fix resource leaks found in a leak audit +openai/openai-dotnet #1349 open Migrate old CollectionResult/AsyncCollectionResult to AsyncStreamingCollectionResult for streaming in Responses/Audio/Images +NethermindEth/nethermind #14583 open fix(debug): preserve native tracer boolean values on null +almeidajr/Kongroo.OpcUa #11 open chore: Bump the nuget group with 11 updates +sneaky-tester/validation-core #7 open Simplify empty-enumerable evaluation +sneaky-tester/validation-core #9 open Simplify EmptyValidator conditions +PinataLabs/ImagePastel #49 merged Harden the WebP decoder against container oddities +Sendspin/sendspin-dotnet #358 merged fix: keep unadmitted host connections from stopping shared pipelines +microsoft/Agent365-Samples #277 open Add Perplexity AI Sample Agent (.NET) +thomhurst/ModularPipelines #6634 merged fix: dispose downloader requests and configured content +adbc-drivers/databricks #544 open fix(csharp): metadata(csharp): GetTables TABLE_TYPE/REMARKS/TABLE_NAME value normalization differs (Thrift REMARKS=UNKNOWN vs SEA "") (#527) +litedb-org/LiteDB #3085 open Add thread-independent transaction handles (#3064) +woutervanranst/Arius7 #195 open perf: reduce per-item allocation churn in Arius.Core +BepInEx/BepInEx.Debug #21 merged ScriptEngine - Support hot reload of interconnected assemblies +HartsyAI/HartsyInference #418 merged Chatterbox: derive zero-shot conditioning from the reference as upstream does +ray-amjad/hyperwhisper-app #1068 merged fix(linux): SIGTERM/SIGINT run the real quit, so the sink and mic are restored (#1038) +SharpAstro/tianwen #1067 merged fix(hosting): a start's lease goes back on a throw, and the build's 27 warnings are gone +AntiNotAnti/Project-Prime #495 merged Dispose Music engine after failed playback-device startup +dotnet/orleans #10588 open feat(streaming)!: unify stream partition recovery +jayanth-org1/Green-Bus-Server #7 open added bugs +lukislp/studylife #208 merged fix(client): release AppStateService resources on dispose, dialog semantics for modals +galapa-dqx/GalapaLauncher #9 open feat(patcher): DQX ZiPatch installer + architecture docs (oracle-validated) +VectorNNTP/VectorNNTPD #139 merged fix(nntp): prevent idle session leaks and disposal races diff --git a/docs/evidence/next-bug-class-2026-10/data/triage_pairs.tsv b/docs/evidence/next-bug-class-2026-10/data/triage_pairs.tsv new file mode 100644 index 00000000..3a5e0711 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/data/triage_pairs.tsv @@ -0,0 +1,7 @@ +AdCodicem_AdCodicem.Pdf__248 tests/AdCodicem.Pdf.Tests/DocumentReaderTests.cs stream - +BoBoBaSs84_BB84.IPTV__36 src/BB84.IPTV.M3U.Editor.Application/ViewModels/GuideOverviewViewModel.cs tokenSource O +CodeForFire_lagebuch__206 src/LageBuch.AppLogic/ViewModels/ChecklistViewModel.cs item - +KerckhoffsLabs_KerckhoffsLabs.Security.Cryptography.Pkcs11__60 src/KerckhoffsLabs.Security.Cryptography.Pkcs11.Tests/Integration/ThreadSafety/LibraryDisposeRaceTests.cs library B +digirati-co-uk_digital-preservation__332 src/DigitalPreservation/Storage.API/Features/Import/Requests/ExecuteImportJob.cs transactionMonitor - +digirati-co-uk_digital-preservation__332 src/DigitalPreservation/Storage.API/Features/Import/Requests/FedoraTransactionMonitor.cs cancellationTokenSource - +whizbang-lib_whizbang__1204 src/Whizbang.Transports.AzureServiceBus/AzureServiceBusConnectionRetry.cs client - diff --git a/docs/evidence/next-bug-class-2026-10/repro/a_382_probe.py b/docs/evidence/next-bug-class-2026-10/repro/a_382_probe.py new file mode 100644 index 00000000..7b9a34b1 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/a_382_probe.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +"""#382 gates A1-A3: what the frontend loses, and what the core does with a hand-made `call`. + +Research probe, not an implementation. Everything is written to a temporary directory; the +checkout is not modified. The `call` ops are edited into the extracted facts by hand. + +Usage (from the repository root): + python docs/evidence/next-bug-class-2026-10/repro/a_382_probe.py [--rust path/to/own-cli] +""" + +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import tempfile + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), *[".."] * 4)) +EXT = os.path.join(ROOT, "frontend", "roslyn", "OwnSharp.Extractor") +SAMPLE = os.path.join(ROOT, "frontend", "roslyn", "samples", "GuardedConsumeSample.cs") + +CALLERS = """ + public static void CallerUsesAfterForward() + { + var x = new MemoryStream(); + ForwardDynamic(x, true); + x.WriteByte(1); + } + public static void CallerLeaksAfterBorrow() + { + var y = new MemoryStream(); + ForwardDynamic(y, false); + y.WriteByte(1); + } +} +""" + +CHAIN = """using System.IO; + +namespace Repro; + +public static class Chain +{ + public static void Close1(Stream s) { s.Dispose(); } + public static void Close2(Stream s) { Close1(s); } + public static void UseAfterTwoLevels() + { var b = new MemoryStream(); Close2(b); b.WriteByte(1); } + public static void Borrow(Stream s) { s.WriteByte(0); } + public static void Borrow2(Stream s) { Borrow(s); } + public static void LeakThroughBorrow2() { var d = new MemoryStream(); Borrow2(d); } +} +""" + + +def sh(args: list[str], **kw: object) -> subprocess.CompletedProcess[str]: + env = dict(os.environ, PYTHONPATH=ROOT) + return subprocess.run(args, cwd=ROOT, capture_output=True, text=True, encoding="utf-8", + errors="replace", env=env, **kw) # type: ignore[call-overload] + + +def extract(src: str, out: str) -> dict: + r = sh(["dotnet", "run", "--project", EXT, "--", src, "-o", out, "--flow-locals"]) + if r.returncode != 0: + sys.exit(f"extractor failed: {r.stderr[-400:]}") + with open(out, encoding="utf-8") as f: + return json.load(f) + + +def cores(path: str, rust: str | None) -> None: + py = sh([sys.executable, "-m", "ownlang", "ownir", path, "--severity", "warning"]) + lines = [ln.split(": ", 1)[-1] for ln in py.stdout.splitlines() if "warning:" in ln] + print(" python:", *lines, sep="\n ") + if rust: + rs = sh([rust, "ownir", path, "--severity", "warning"]) + print(" rust: ", *[ln.split(": ", 1)[-1] for ln in rs.stdout.splitlines() + if "warning:" in ln], sep="\n ") + + +def summaries(path: str, names: tuple[str, ...]) -> None: + out = json.loads(sh([sys.executable, "-m", "ownlang", "summaries", path]).stdout) + for s in out["summaries"]: + if s["method"].endswith(names): + print(" summary", s["method"].split(".")[-1], + [(p["name"], p["transfer"]) for p in s["params"]]) + + +def main() -> int: + if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(encoding="utf-8", errors="replace") + ap = argparse.ArgumentParser() + ap.add_argument("--rust", help="own-cli binary for the Rust door (optional)") + rust = ap.parse_args().rust + work = tempfile.mkdtemp(prefix="nbc-a-") + + a = os.path.join(work, "a") + os.makedirs(a) + with open(SAMPLE, encoding="utf-8") as f: + text = f.read().rstrip().rstrip("}") + with open(os.path.join(a, "GuardedConsumeSample.cs"), "w", encoding="utf-8") as f: + f.write(text + CALLERS) + facts = extract(a, os.path.join(work, "a.json")) + fd = next(f for f in facts["functions"] if f["name"].endswith("ForwardDynamic")) + print("A1 ForwardDynamic body on main:", json.dumps(fd["body"])) + callers = [f["name"] for f in facts["functions"] if "Caller" in f["name"]] + print("A1 caller records emitted:", callers) + summaries(os.path.join(work, "a.json"), ("ForwardDynamic", "MaybeClose", "BorrowingWrapper")) + + fd["body"] = [{"op": "call", "callee": "GuardedConsumeSample.MaybeClose", + "sig": "System.IO.Stream,System.Boolean", "args": ["forwarded"], + "line": fd["body"][0]["line"]}] + for name, var in (("CallerUsesAfterForward", "x"), ("CallerLeaksAfterBorrow", "y")): + facts["functions"].append({ + "name": "GuardedConsumeSample." + name, "file": fd["file"], "sig": "", "params": [], + "body": [{"op": "acquire", "var": var, "line": 1, "column": 1, "kind": "disposable"}, + {"op": "call", "callee": "GuardedConsumeSample.ForwardDynamic", + "sig": "System.IO.Stream,System.Boolean", "args": [var], "line": 2}, + {"op": "use", "var": var, "line": 3}]}) + edited = os.path.join(work, "a.call.json") + with open(edited, "w", encoding="utf-8") as f: + json.dump(facts, f) + print("A2 with a hand-made call:") + summaries(edited, ("ForwardDynamic", "MaybeClose", "BorrowingWrapper")) + print("A3 caller diagnostics with the call (both callers):") + cores(edited, rust) + + b = os.path.join(work, "b") + os.makedirs(b) + with open(os.path.join(b, "Chain.cs"), "w", encoding="utf-8") as f: + f.write(CHAIN) + chain = extract(b, os.path.join(work, "b.json")) + print("A3 multi-level definite forward and borrow leak on main:") + cores(os.path.join(work, "b.json"), rust) + for f in chain["functions"]: + if f["name"].endswith("Borrow2"): + f["body"] = [{"op": "call", "callee": "Repro.Chain.Borrow", "sig": "System.IO.Stream", + "args": ["s"], "line": f["body"][0]["line"]}] + chain["functions"].append({ + "name": "Repro.Chain.LeakThroughBorrow2", "file": chain["functions"][0]["file"], "sig": "", + "params": [], + "body": [{"op": "acquire", "var": "d", "line": 12, "column": 1, "kind": "disposable"}, + {"op": "call", "callee": "Repro.Chain.Borrow2", "sig": "System.IO.Stream", + "args": ["d"], "line": 12}]}) + edited_b = os.path.join(work, "b.call.json") + with open(edited_b, "w", encoding="utf-8") as f: + json.dump(chain, f) + print("A3 borrow leak with a hand-made call:") + cores(edited_b, rust) + print(f"(work dir: {work})") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/evidence/next-bug-class-2026-10/repro/b_406/Capture.cs b/docs/evidence/next-bug-class-2026-10/repro/b_406/Capture.cs new file mode 100644 index 00000000..f7dcdaed --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/b_406/Capture.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Repro; + +public static class Capture +{ + public static void LeakSyncLambda() + { + var stream = new MemoryStream(); + Action use = () => stream.WriteByte(1); + use(); + } + + public static void LeakNoLambda() + { + var plain = new MemoryStream(); + plain.WriteByte(1); + } + + public static void LeakLocalFunction() + { + var lf = new MemoryStream(); + void Use() => lf.WriteByte(1); + Use(); + } + + public static Task ForEachAsync(IEnumerable items, Func body, int max) + { + SemaphoreSlim throttler = new SemaphoreSlim(max, max); + IEnumerable tasks = items.Select(async input => + { + await throttler.WaitAsync(); + try { await body(input); } finally { throttler.Release(); } + }); + return Task.WhenAll(tasks); + } +} diff --git a/docs/evidence/next-bug-class-2026-10/repro/b_406/README.md b/docs/evidence/next-bug-class-2026-10/repro/b_406/README.md new file mode 100644 index 00000000..78b682dd --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/b_406/README.md @@ -0,0 +1,9 @@ +`Capture.cs`: the #406 shapes. Run from the repository root: + +```bash +dotnet run --project frontend/roslyn/OwnSharp.Extractor -- docs/evidence/next-bug-class-2026-10/repro/b_406 -o /tmp/b406.json --flow-locals +PYTHONPATH=. python -m ownlang ownir /tmp/b406.json --severity warning +``` + +Observed on `2fefb19f`: only `plain` (no lambda) reports OWN001; `LeakSyncLambda` and `LeakLocalFunction` are silent +(the capture-is-escape rule, `Program.cs` "capturedInClosure"); `ForEachAsync` (ShareX control) is silent. diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/MauiShape.cs b/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/MauiShape.cs new file mode 100644 index 00000000..ce822962 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/MauiShape.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Repro; + +public sealed class Bindable +{ + private readonly Dictionary _properties = new(); + private readonly Func? _defaultValueCreator; + + public Bindable(Func? creator) => _defaultValueCreator = creator; + + public void Set(int id, object? value) => _properties[id] = value; + + // MAUI 7b051d1b BindableObject.GetOrCreateContext shape: a ref held across user code + public object? GetOrCreate(int id) + { + ref var context = ref CollectionsMarshal.GetValueRefOrAddDefault(_properties, id, out var exists); + if (!exists) + { + context = CreateContext(); + } + return context; + } + + private object CreateContext() => _defaultValueCreator?.Invoke(this) ?? new object(); + + // List AsSpan shape (#400 first slice) + public static int StaleSpan() + { + var list = new List(1) { 10 }; + Span view = CollectionsMarshal.AsSpan(list); + list.Add(20); + view[0] = 99; + return list[0]; + } +} diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/README.md b/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/README.md new file mode 100644 index 00000000..f69e1a09 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/README.md @@ -0,0 +1,9 @@ +Built-in .NET analyzers (`AnalysisMode=All`, SDK 10.0.401) over the three #400 shapes. + +```bash +bash ../c_runtime_dictionary/fetch.sh && cp ../c_runtime_dictionary/BuggyMeziantou.cs . +dotnet build -c Release --no-incremental -v:minimal +``` + +Observed: two CA1062 (argument null check) in the Meziantou file and nothing else; no rule fires on the +dictionary-ref, MAUI-shape or `AsSpan` patterns. `BuggyMeziantou.cs` is downloaded, not committed. diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/c_analyzers.csproj b/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/c_analyzers.csproj new file mode 100644 index 00000000..51fcdc42 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_analyzers/c_analyzers.csproj @@ -0,0 +1,5 @@ + + Librarynet8.0enableenable + trueAlllatest + truefalse + diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/Program.cs b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/Program.cs new file mode 100644 index 00000000..eacb57f9 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/Program.cs @@ -0,0 +1,28 @@ +using System.Runtime.InteropServices; +using BuggyNs; + +static class P +{ + static void Main() + { + // Meziantou 7addb26c GetOrAdd(dict, key, factory): reentrant factory + var d = new Dictionary(); + var r = d.GetOrAdd(0, _ => { for (var i = 1; i <= 64; i++) d[i] = "filler"; return "computed"; }); + Console.WriteLine($"meziantou reentrant: returned={r} stored={d[0] ?? ""} (bug: {d[0] != "computed"})"); + // Meziantou: throwing factory poisons the entry + var e = new Dictionary(); + try { e.GetOrAdd("k", _ => throw new InvalidOperationException()); } catch (InvalidOperationException) { } + Console.WriteLine($"meziantou throwing: ContainsKey={e.ContainsKey("k")} value={(e.TryGetValue("k", out var v) ? v ?? "" : "-")}"); + // control: non-reentrant factory + var c = new Dictionary(); + c.GetOrAdd(0, _ => "ok"); + Console.WriteLine($"control non-reentrant: stored={c[0]} (ok: {c[0] == "ok"})"); + + // MAUI 7b051d1b GetOrCreateContext shape: ref held across a creator that mutates the same dictionary + var props = new Dictionary(); + object Create() { for (var i = 1; i <= 15; i++) props[i] = i; return "context"; } + ref var ctx = ref CollectionsMarshal.GetValueRefOrAddDefault(props, 0, out var exists); + if (!exists) ctx = Create(); + Console.WriteLine($"maui shape: stored={props[0] ?? ""} (bug: {props[0] is null})"); + } +} diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/c_runtime_dictionary.csproj b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/c_runtime_dictionary.csproj new file mode 100644 index 00000000..f80e39a8 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/c_runtime_dictionary.csproj @@ -0,0 +1,4 @@ + + Exenet8.0enabledisable + + diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/fetch.sh b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/fetch.sh new file mode 100644 index 00000000..82e4580b --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_dictionary/fetch.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Fetch Meziantou.Framework DictionaryExtensions.cs at the BUGGY commit 7addb26c (MIT) and move it +# into namespace BuggyNs so Program.cs exercises the original code verbatim otherwise. +set -euo pipefail +cd "$(dirname "$0")" +gh api "repos/meziantou/Meziantou.Framework/contents/src/Meziantou.Framework/DictionaryExtensions.cs?ref=7addb26cfd08a6ed6119a3d0a186486befaa923b" \ + -H "Accept: application/vnd.github.raw" > BuggyMeziantou.cs +sed -i 's/^namespace Meziantou.Framework;/namespace BuggyNs;/' BuggyMeziantou.cs diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_runtime_list/Program.cs b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_list/Program.cs new file mode 100644 index 00000000..83c9b4f0 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_list/Program.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +static class P +{ + static void Main() + { + // BAD: view, structural mutation with forced reallocation, use of the old view + var list = new List(capacity: 1) { 10 }; + Span view = CollectionsMarshal.AsSpan(list); + list.Add(20); // capacity 1 -> reallocation + view[0] = 99; + Console.WriteLine($"bad: list[0]={list[0]} (stale write lost: {list[0] != 99})"); + + // BAD without reallocation: contract violation, no observable effect + var list2 = new List(capacity: 8) { 10 }; + Span v2 = CollectionsMarshal.AsSpan(list2); + list2.Add(20); + v2[0] = 99; + Console.WriteLine($"bad-spare-capacity: list2[0]={list2[0]} (observable: {list2[0] != 99}), v2.Length={v2.Length} list2.Count={list2.Count}"); + + // BAD RemoveAt: view still reads shifted storage + var list3 = new List { 1, 2, 3 }; + Span v3 = CollectionsMarshal.AsSpan(list3); + list3.RemoveAt(0); + Console.WriteLine($"bad-removeat: v3=[{string.Join(",", v3.ToArray())}] list3=[{string.Join(",", list3)}]"); + + // OK: no mutation + var ok1 = new List(1) { 10 }; + Span o1 = CollectionsMarshal.AsSpan(ok1); o1[0] = 99; + Console.WriteLine($"ok-no-mutation: {ok1[0] == 99}"); + // OK: mutation of a different list + var a = new List(1) { 10 }; var b = new List(1) { 1 }; + Span va = CollectionsMarshal.AsSpan(a); b.Add(2); va[0] = 99; + Console.WriteLine($"ok-other-list: {a[0] == 99}"); + // OK: no use after mutation + var ok3 = new List(1) { 10 }; + Span o3 = CollectionsMarshal.AsSpan(ok3); o3[0] = 5; ok3.Add(20); + Console.WriteLine($"ok-no-use-after: {ok3[0] == 5}"); + // OK: refreshed view after mutation + var ok4 = new List(1) { 10 }; + Span o4 = CollectionsMarshal.AsSpan(ok4); ok4.Add(20); o4 = CollectionsMarshal.AsSpan(ok4); o4[0] = 99; + Console.WriteLine($"ok-refreshed: {ok4[0] == 99}"); + } +} diff --git a/docs/evidence/next-bug-class-2026-10/repro/c_runtime_list/c_runtime_list.csproj b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_list/c_runtime_list.csproj new file mode 100644 index 00000000..0435410a --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/c_runtime_list/c_runtime_list.csproj @@ -0,0 +1 @@ +Exenet8.0disabledisable diff --git a/docs/evidence/next-bug-class-2026-10/repro/classify_sample.py b/docs/evidence/next-bug-class-2026-10/repro/classify_sample.py new file mode 100644 index 00000000..68074be2 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/classify_sample.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Crude classification of `ref x = ref CollectionsMarshal.GetValueRefOrAddDefault(D, ...)` sites. + +Usage: python classify_sample.py (sample-dir holds index.tsv and N.cs from sample.sh) + +What runs between taking the ref and the last textual use of the ref local in the same block? + NONE no invocation in between + DELEGATE a delegate/factory-looking call (Invoke, *Factory/*Creator/*Callback/...) + SELF a call on the same dictionary, on `this`, or an unqualified PascalCase call + (this bucket also catches constructors: it is deliberately over-inclusive) + CALL other invocations only +Textual and single-method; the suspicious sites were then reviewed by hand (see REPORT.md). +""" + +from __future__ import annotations + +import collections +import os +import re +import sys + +SITE = re.compile( + r"ref\s+(?:var|[\w<>?,\s\[\]]+?)\s+(\w+)\s*=\s*ref\s+" + r"CollectionsMarshal\.GetValueRefOrAddDefault\s*\(\s*([\w.]+)") +CALL = re.compile(r"([\w.]+)\s*(?:<[^<>()]*>)?\s*\(") +KEYWORDS = {"if", "while", "for", "foreach", "switch", "return", "nameof", "typeof", "sizeof", + "default", "new", "using", "lock", "catch", "when", "out", "ref", "Unsafe.IsNullRef"} +DELEGATE_NAME = re.compile(r"(?i)factory|creator|callback|func|action|selector|create$") + + +def block_after(text: str, idx: int) -> str: + depth = 0 + for i in range(idx, len(text)): + if text[i] == "{": + depth += 1 + elif text[i] == "}": + if depth == 0: + return text[idx:i] + depth -= 1 + return text[idx:] + + +def classify(calls: list[str], dic: str) -> str: + if not calls: + return "NONE" + if any(c.endswith("Invoke") or DELEGATE_NAME.search(c.split(".")[-1]) for c in calls): + return "DELEGATE" + if any(c.startswith((dic + ".", "this.")) or ("." not in c and c[0].isupper()) for c in calls): + return "SELF" + return "CALL" + + +def main() -> int: + sample = sys.argv[1] if len(sys.argv) > 1 else "sample" + with open(os.path.join(sample, "index.tsv"), encoding="utf-8") as f: + index = [line.rstrip("\n").split("\t") for line in f] + rows = [] + for n, (repo, path, _sha) in enumerate(index, 1): + src = os.path.join(sample, f"{n}.cs") + if not os.path.exists(src): + continue + with open(src, encoding="utf-8", errors="replace") as f: + text = f.read() + for m in SITE.finditer(text): + var, dic = m.group(1), m.group(2) + rest = block_after(text, m.end()) + uses = [u.start() for u in re.finditer(r"\b" + re.escape(var) + r"\b", rest)] + span = rest[: uses[-1]] if uses else "" + span = span[span.find(";") + 1:] if ";" in span else span + calls = [c for c in CALL.findall(span) + if c not in KEYWORDS and c.split(".")[-1] not in KEYWORDS] + line = text[: m.start()].count("\n") + 1 + rows.append((repo, path, line, var, classify(calls, dic), sorted(set(calls))[:6])) + kinds = collections.Counter(r[4] for r in rows) + print("sites", len(rows), "repos", len({r[0] for r in rows}), dict(kinds)) + for r in rows: + if r[4] != "NONE": + print("\t".join(map(str, r))) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/evidence/next-bug-class-2026-10/repro/fetch_c.sh b/docs/evidence/next-bug-class-2026-10/repro/fetch_c.sh new file mode 100644 index 00000000..038b2c40 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/fetch_c.sh @@ -0,0 +1,5 @@ +get() { mkdir -p "c-pairs/$1/$2"; gh api "repos/$3/contents/$4?ref=$5" -H "Accept: application/vnd.github.raw" > "c-pairs/$1/$2/$(basename $4)"; echo "$1/$2 $(wc -l < c-pairs/$1/$2/$(basename $4))"; } +get meziantou base meziantou/Meziantou.Framework src/Meziantou.Framework/DictionaryExtensions.cs 7addb26cfd08a6ed6119a3d0a186486befaa923b +get meziantou fix meziantou/Meziantou.Framework src/Meziantou.Framework/DictionaryExtensions.cs cf82931e2cea86af40e67eb742f3c1331b65f91e +get maui base dotnet/maui src/Controls/src/Core/BindableObject.cs 7b051d1bbf66d25a3b46c3382ffd25911f16c892 +get maui fix dotnet/maui src/Controls/src/Core/BindableObject.cs 2db8b6af53b620f279d965f323d57f9d002c58da diff --git a/docs/evidence/next-bug-class-2026-10/repro/prs_ab.sh b/docs/evidence/next-bug-class-2026-10/repro/prs_ab.sh new file mode 100644 index 00000000..e94ed5ce --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/prs_ab.sh @@ -0,0 +1,6 @@ +v() { echo "################ $1#$2"; gh pr view "$2" -R "$1" --json title,state,baseRefOid,mergeCommit --jq '"\(.title) state=\(.state) base=\(.baseRefOid) merge=\(.mergeCommit.oid)"'; gh pr diff "$2" -R "$1" 2>&1 | grep -E "^(\+\+\+|@@|[-+])" | grep -v "^[-+]\s*//" | head -${3:-70}; } +v mono/SkiaSharp 4814 60 +v Flakroup/FEx 172 80 +v ChrisBrooksbank/Vox 41 90 +v thomhurst/ModularPipelines 6634 60 +v blxkstxllion/shopkeeper 119 60 diff --git a/docs/evidence/next-bug-class-2026-10/repro/prs_c.sh b/docs/evidence/next-bug-class-2026-10/repro/prs_c.sh new file mode 100644 index 00000000..b45b0f0b --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/prs_c.sh @@ -0,0 +1,4 @@ +v() { echo "################ $1#$2"; gh pr view "$2" -R "$1" --json title,state,mergedAt,baseRefOid,headRefOid,mergeCommit,body --jq '"\(.title)\nstate=\(.state) merged=\(.mergedAt) base=\(.baseRefOid) head=\(.headRefOid) merge=\(.mergeCommit.oid)\n\(.body[0:1800])"'; echo "------ diff"; gh pr diff "$2" -R "$1" 2>&1 | grep -v "^index " | head -${3:-120}; } +v meziantou/Meziantou.Framework 2825 160 +v dotnet/maui 37148 200 +v rtkelly-labs/Parquet.SourceGenerator 639 80 diff --git a/docs/evidence/next-bug-class-2026-10/repro/sample.sh b/docs/evidence/next-bug-class-2026-10/repro/sample.sh new file mode 100644 index 00000000..5b3f0706 --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/sample.sh @@ -0,0 +1,7 @@ +gh search code "CollectionsMarshal.GetValueRefOrAddDefault" --language "C#" --limit 60 --json repository,path,sha --jq '.[] | "\(.repository.nameWithOwner)\t\(.path)\t\(.sha)"' > sample/index.tsv +n=0 +while IFS=$'\t' read -r repo path sha; do + n=$((n+1)); out="sample/$n.cs" + gh api "repos/$repo/git/blobs/$sha" --jq .content 2>/dev/null | base64 -d > "$out" 2>/dev/null || rm -f "$out" +done < sample/index.tsv +wc -l sample/index.tsv; ls sample/*.cs | wc -l diff --git a/docs/evidence/next-bug-class-2026-10/repro/search.sh b/docs/evidence/next-bug-class-2026-10/repro/search.sh new file mode 100644 index 00000000..03f01d6a --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/search.sh @@ -0,0 +1,13 @@ +set -u +q() { echo "### $1 :: $2"; gh search "$1" $2 --limit 30 --json repository,title,url,number,state --jq '.[] | "\(.repository.nameWithOwner)\t#\(.number)\t\(.state)\t\(.title)"' 2>&1 | head -30; } +# C: CollectionsMarshal stale views +q prs '"CollectionsMarshal" stale --language C#' +q prs '"CollectionsMarshal.AsSpan" fix --language C#' +q issues '"CollectionsMarshal.AsSpan" bug --language C#' +q issues '"CollectionsMarshal" "Add" span invalid --language C#' +# B: captured disposable in local lambda +q prs 'dispose captured lambda leak --language C#' +q prs '"local function" dispose leak --language C#' +# A: leak through own helper +q prs '"not disposed" stream passed helper --language C#' +q prs 'fix "undisposed" stream leak --language C#' diff --git a/docs/evidence/next-bug-class-2026-10/repro/triage_pairs.py b/docs/evidence/next-bug-class-2026-10/repro/triage_pairs.py new file mode 100644 index 00000000..6956a1ce --- /dev/null +++ b/docs/evidence/next-bug-class-2026-10/repro/triage_pairs.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""Triage bug-fix pairs for the three candidate mechanisms (source level, no build). + +Usage: python triage_pairs.py (one directory per pair: pair.meta.json, base/, fix/) + +For every changed .cs file of every pair: locals that GAIN a release in the fix revision +(`using var x`, `using (x`, `using (var x`, `x.Dispose()`, `x?.Dispose()`), then the same local in +the BASE revision's enclosing method is classified: + B referenced lexically inside a lambda / local function / anonymous method + A passed as an argument to a method declared in the pair's own source (first-party) + O passed to some other call + - none of these +Any changed file mentioning CollectionsMarshal is flagged C. +""" + +from __future__ import annotations + +import glob +import json +import os +import re +import sys + +REL = re.compile( + r"using\s*\(\s*(?:var\s+)?(\w+)\b|using\s+var\s+(\w+)\b|\b(\w+)\s*\??\.\s*Dispose(?:Async)?\s*\(") +METHOD_DECL = re.compile( + r"\b(?:public|private|protected|internal|static)[\w\s<>\[\],?]*\s(\w+)\s*\([^;{]*\)" + r"\s*(?:where[^{]*)?\{") +LAMBDA = re.compile( + r"=>\s*\{(?:[^{}]|\{[^{}]*\})*\}|=>[^;\n]*;" + r"|delegate\s*\([^)]*\)\s*\{(?:[^{}]|\{[^{}]*\})*\}") + + +def released(text: str) -> set[str]: + return {next(g for g in m.groups() if g) for m in REL.finditer(text)} + + +def first_party_methods(root: str) -> set[str]: + names: set[str] = set() + for f in glob.glob(os.path.join(root, "**", "*.cs"), recursive=True): + try: + with open(f, encoding="utf-8", errors="replace") as fh: + names |= set(METHOD_DECL.findall(fh.read())) + except OSError: # e.g. a path past the Windows MAX_PATH limit: skipped, as originally + pass + return names + + +def enclosing_method(text: str, idx: int) -> str: + start = max((m.start() for m in METHOD_DECL.finditer(text) if m.start() < idx), default=0) + depth, opened = 0, False + i = text.find("{", start) + while 0 <= i < len(text): + if text[i] == "{": + depth += 1 + opened = True + elif text[i] == "}": + depth -= 1 + if opened and depth == 0: + return text[start:i + 1] + i += 1 + return text[start:] + + +def classify(base_text: str, name: str, fp: set[str]) -> str | None: + decl = re.search(r"(?:var|[\w<>\[\]?.]+)\s+" + re.escape(name) + r"\s*=\s*(new\b|[\w.]+\s*\()", + base_text) + if not decl: + return None + body = enclosing_method(base_text, decl.start()) + word = re.compile(r"\b" + re.escape(name) + r"\b") + kinds = {"B"} if any(word.search(m.group(0)) for m in LAMBDA.finditer(body)) else set() + arg = re.compile(r"(? int: + pairs = sys.argv[1] if len(sys.argv) > 1 else "pairs" + rows = [] + for meta_f in sorted(glob.glob(os.path.join(pairs, "*", "pair.meta.json"))): + pair = os.path.dirname(meta_f) + with open(meta_f, encoding="utf-8") as f: + meta = json.load(f) + fp: set[str] | None = None + for rel in meta.get("changed_files", []): + if not rel.endswith(".cs"): + continue + texts = [] + for side in ("base", "fix"): + p = os.path.join(pair, side, rel) + if os.path.exists(p): + with open(p, encoding="utf-8", errors="replace") as f: + texts.append(f.read()) + else: + texts.append("") + bt, ft = texts + if "CollectionsMarshal" in bt + ft: + rows.append((os.path.basename(pair), rel, "CollectionsMarshal", "C")) + for name in sorted(released(ft) - released(bt)): + if fp is None: + fp = first_party_methods(os.path.join(pair, "base")) + k = classify(bt, name, fp) + if k is not None: + rows.append((os.path.basename(pair), rel, name, k)) + for r in rows: + print("\t".join(r)) + print(f"# {len(rows)} rows", file=sys.stderr) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())