Skip to content

oracle: fix dotted-test-dir filter + dispose-optional StringWriter/StringReader (+ field notes)#147

Merged
PhysShell merged 10 commits into
mainfrom
claude/own-net-tasks-3k6d07
Jun 27, 2026
Merged

oracle: fix dotted-test-dir filter + dispose-optional StringWriter/StringReader (+ field notes)#147
PhysShell merged 10 commits into
mainfrom
claude/own-net-tasks-3k6d07

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Что и зачем

Прогон кросс-инструментального оракула по 4 OSS-целям (Newtonsoft.Json, Serilog, StackExchange.Redis, RestSharp) вскрыл два дефекта точности, которые тут и чинятся: (1) компаратор не отфильтровывал .NET тест-дир с точками (Newtonsoft.Json.Tests), из-за чего ~485 тестовых находок попадали в product-дифф (own-only 489 вместо реальных 4); (2) экстрактор флагал owning-поля StringWriter/StringReader как утечку, хотя у них нет unmanaged-ресурса. Плюс зафиксированы два own-only идиома в field-notes (требование oracle.md).

Тип изменения

  • feat — новая возможность
  • fix — исправление бага
  • docs — документация
  • refactor / chore / test / ci — без изменения поведения

Как проверено

  • python scripts/oracle_compare.py --selftest25/25 (новые кейсы на дотовый тест-дир Newtonsoft.Json.Tests и его продуктового соседа Newtonsoft.Json)
  • python tests/test_ownir.py194/194 (не затронуто, sanity)
  • ruff check scripts/oracle_compare.py → clean
  • Сквозная проверка фикса компаратора на записанном выводе оракула Newtonsoft: own-only 489 → 4, oracle_other 903 → 235; подтверждено чистым ре-раном в CI.
  • Экстракторная часть (IsDisposeOptional + sample HolderWithStringWriter + CI-ассерт) — C#/dotnet, валидируется в CI (локально dotnet недоступен): новый контроль должен остаться SILENT, control MemoryStream/CTS — по-прежнему WARN.

Что в диффе

  • scripts/oracle_compare.py_is_test_path матчит каждый дот-компонент сегмента (Newtonsoft.Json.Tests ловится по tests), продуктовые SnippetEngine/Documentation/Newtonsoft.Json не трогаются. +selftest-кейсы. Ловит проблему на любом репо с *.Tests/*.Benchmarks-неймингом.
  • frontend/roslyn/OwnSharp.Extractor/Program.csSystem.IO.StringWriter/StringReaderIsDisposeOptional (тот же рычаг, что Task/DataTable). MemoryStream намеренно НЕ исключён.
  • frontend/roslyn/samples/ResolvedDisposableSample.cs + .github/workflows/ci.yml — контроль HolderWithStringWriter + ассерт молчания.
  • docs/notes/field-notes-patterns.md — записи CI SHA-pinning #7 (подписка на returned publisher) и Quality gate, lifetimes module (WPF leaks), spec + proposals, and a real C# leak pipeline #8 (owning-field без unmanaged-ресурса); первые own-only-идиомы блокнота (где слишком строги мы, а оракул молчит).

Связанные issue

Refs #146 — подписочный фикс (#7) требует межпроцедурного escape паблишера (дуал D5), не безопасный микрофикс; заведён отдельным трекером.

Чеклист

  • изменение покрыто тестом/селфтестом (компаратор — selftest; экстрактор — CI sample + ассерт)
  • README/docs обновлены при необходимости (field-notes)
  • коммиты в conventional-commit стиле (fix:/docs:/oracle:)

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Extended leak-check handling to treat in-memory reader/writer scenarios (such as StringReader/StringWriter) as dispose-optional, reducing false positives.
  • Bug Fixes
    • CI now fails if the known “no warning” string-backed scenario is reported as a leak.
    • Improved path classification for test/benchmark/sample/docs detection in dotted folder names (e.g., *.Tests, *.Benchmarks).
  • Documentation
    • Added more “resource & lifetime” idiom examples for bounded lifetimes and subtle over-reporting cases.
  • Tests
    • Added a new StringWriter/StringReader sample and updated oracle self-checks for the dotted-directory matching behavior.

claude added 8 commits June 27, 2026 03:53
The integration token can't workflow_dispatch (403), so use the existing
push-sentinel fallback to kick the cross-tool oracle. Adds this dev branch to
the push allowlist and points corpus/oracle-target.txt at JamesNK/Newtonsoft.Json
for a blind precision run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
…oduct diff

_is_test_path matched a path segment only as a whole word, so the .NET
<Project>.Tests / Foo.Benchmarks convention slipped through: the dir
'Newtonsoft.Json.Tests' is one segment, never equal to 'tests', so ~485 test
findings leaked into the product diff (own-only 489 vs the true 4). Match each
dot-component of a segment instead — catching the 'tests' tail while leaving
single-component product dirs (SnippetEngine, Documentation, Newtonsoft.Json)
untouched. Adds selftest cases for the dotted dir and its product sibling.

Verified on the recorded Newtonsoft oracle output: own-only 489 -> 4,
oracle_other 903 -> 235.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
…wtonsoft run

First entries where Own.NET (not the oracle) over-reports, triaged from the
4 product own-only findings on JamesNK/Newtonsoft.Json:

- #7 event '+=' on a freshly-created, RETURNED publisher (JsonSerializer:717):
  the dual of ownership transfer; bounded by the returned object's lifetime, so
  no '-=' needed. Already tiered as an advisory warning ('injected'), not a hard
  error — surfaces only under --severity warning. Proper fix is interprocedural
  publisher provenance.
- #8 owning field whose IDisposable holds no unmanaged resource (TraceJsonReader/
  Writer StringWriter/JsonTextWriter): a StringWriter is StringBuilder-backed, not
  a handle. Fixable by adding System.IO.StringWriter/StringReader to the existing
  IsDisposeOptional allowlist (mirrors Task/DataTable); the JsonTextWriter wrapper
  stays a low-value residual.

Both verdicts: not real leaks, criticality ~0. CodeQL/Infer# correctly silent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
A StringWriter is StringBuilder-backed and a StringReader reads a string — neither
holds an OS handle or unmanaged resource, so Dispose() frees nothing real and an
undisposed owning field of these is not a leak. Add System.IO StringWriter/StringReader
to IsDisposeOptional (the same lever already used for Task/ValueTask/DataTable/DataSet/
DataView), so the owned-field detector stays silent on them. MemoryStream is deliberately
NOT exempted — it can own a real buffer and still warns.

Surfaced by the oracle on Newtonsoft.Json (field-notes #8): TraceJsonReader/TraceJsonWriter
own undisposed StringWriter/JsonTextWriter fields; CodeQL/Infer# correctly stay silent on
the StringWriter (not a real resource), Own.NET over-reported. Adds a HolderWithStringWriter
control to ResolvedDisposableSample + a CI assertion it stays silent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
Removes this dev branch from the oracle workflow push allowlist and restores
corpus/oracle-target.txt to the committed fixture. The wiring was a dev-loop
mechanism (the automation token can't workflow_dispatch); the workflow comment
flags it 'remove before merging to main'. Net diff of both files vs main is now zero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f067082-45e8-40a7-aad3-c987e0534750

📥 Commits

Reviewing files that changed from the base of the PR and between 19471e3 and f799ca0.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • docs/notes/field-notes-patterns.md
  • frontend/roslyn/OwnSharp.Extractor/Program.cs
✅ Files skipped from review due to trivial changes (1)
  • docs/notes/field-notes-patterns.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/roslyn/OwnSharp.Extractor/Program.cs

📝 Walkthrough

Walkthrough

Adds StringWriter/StringReader dispose-optional handling, a matching sample and CI check, lifetime-pattern notes, and refined oracle test-path classification for dotted project segments.

Changes

Disposal heuristics and validation

Layer / File(s) Summary
String-backed dispose-optional handling
frontend/roslyn/OwnSharp.Extractor/Program.cs, frontend/roslyn/samples/ResolvedDisposableSample.cs, .github/workflows/ci.yml
IsDisposeOptional includes StringWriter and StringReader; HolderWithStringWriter is added as a sample control; CI fails if that sample appears in the extractor output.
Lifetime-pattern notebook entries
docs/notes/field-notes-patterns.md
The notes add entries for event subscription tied to publisher lifetime and for owning IDisposable fields backed by string-based readers/writers, then expand the through-line summary for those cases.
Dotted segment test-path matching
scripts/oracle_compare.py
_is_test_path now checks dot-separated components within each path segment, and the self-test assertions and check count are updated for the revised exclusion behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PhysShell/Own.NET#21: Both PRs change scripts/oracle_compare.py’s _is_test_path behavior and the corresponding exclude-tests expectations.
  • PhysShell/Own.NET#79: Both PRs adjust disposal-related extractor behavior and validate it with sample/CI coverage for types that should stay silent.
  • PhysShell/Own.NET#110: Both PRs touch the oracle triage plumbing and the shared notes that track resource/lifetime idioms.

Poem

🐇 I hopped through strings and dotted paths today,
And silent readers kept the leaks away.
The notebook rustled with a lifetime tune,
While CI kept watch by the light of the moon.
Crisp carrots to the code, and tidy night!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main fixes and the added field-notes documentation.
Description check ✅ Passed The description covers the required sections, explains the changes, lists verification, related issue, and checklist items.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/own-net-tasks-3k6d07

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e661573746

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/oracle_compare.py
claude added 2 commits June 27, 2026 05:21
…he filename

The dotted-component match in _is_test_path also examined the FILE name (the last
path segment), so a product finding in a file literally named 'Test.cs' / 'Doc.cs' /
'Foo.Tests.cs' was dropped from the product diff (under-counting real findings).
Iterate directory segments only (segs[:-1]); the file's own name no longer classifies
it as non-product. Test-*directories* (Newtonsoft.Json.Tests, src/Test/) still drop.
Adds selftest negatives for the marker-named product files. (Codex P2 on #147.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMngnax1jsy378fSAZb9r
…3k6d07

# Conflicts:
#	docs/notes/field-notes-patterns.md
@PhysShell PhysShell merged commit 467cb27 into main Jun 27, 2026
30 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.

2 participants