Skip to content

oracle: timer leak (а) + golden runtime-correlation fixture (б) - #21

Merged
PhysShell merged 6 commits into
mainfrom
claude/sts-runtime-analysis-2mo4z9
Jun 26, 2026
Merged

oracle: timer leak (а) + golden runtime-correlation fixture (б)#21
PhysShell merged 6 commits into
mainfrom
claude/sts-runtime-analysis-2mo4z9

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Two complementary in-scope additions that dovetail: (а) gives the oracle a second heap-provable leak, and (б) correlates both leaks through correlate.py.

(а) Timer leak in the oracle

TickerViewModel starts a recurring System.Threading.Timer in its ctor and never disposes it → it stays rooted by the runtime's TimerQueue (own-check OWN-TIMER; coverage-matrix "Timers"). FixedTickerViewModel disposes it — the control case.

LeakScenario now proves both leaks (subscription + timer) and both fixes through the same WeakReference harness:

subscription leaky alive (GC):  50   subscription fixed alive (GC):   0
timer        leaky alive (GC):  50   timer        fixed alive (GC):   0
verdict: BOTH leaks confirmed, each isolated to its un-released resource

Stable across repeated runs; builds clean on net8.0, zero warnings.

(б) Runtime correlation golden fixture — phase 5 on real oracle-shaped data

  • findings.json — the static suspects own-check would emit (OWN001 subscription on WatchlistViewModel, OWN-TIMER on TickerViewModel). resource is a description, so correlation keys on the source-file stem.
  • runtime.json — heap evidence faithful to the leak proof: 50 of each leaky VM retained (expected 0), plus the 250 000 QuoteRow they transitively retain.
  • test_oracle_runtime.py asserts the three-way split:
bucket result
confirmed WatchlistViewModel ×50 + TickerViewModel ×50 — both high, each naming the leaked CLR type + the static rule it corroborates
static-only (none) — both suspects retained
runtime-only QuoteRow ×250 000 — a static blind spot (rows retained through the leaked VMs)

The high gate fails on the two confirmed leaks. A drift guard ties every retained type to a real oracle/LeakyOracle/ViewModels/*.cs.

Scope

Both fixtures pin the contract the слой-2 ClrMD/gcdump collectors (Own.NET) must emit; when those land, these same arch/ + runtime/ passes run on extracted data. Wired into CI (normal + -O); all suites green. The .NET oracle build is deliberately not on CI (build-free Python contract) — oracle/tools/leak-scenario.sh is the reproducer.

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added end-to-end coverage for a second leak scenario, including a recurring timer case, alongside the existing subscription leak checks.
    • Introduced new golden runtime fixtures and correlation expectations covering both scenarios.
  • Bug Fixes
    • Added a dedicated “fixed” control path to confirm timer cleanup prevents unintended retention.
  • Tests
    • Added a runtime correlation test runner and strengthened architecture validation to guard against contract drift.
  • Documentation
    • Updated Oracle and fixture docs to describe the expanded scenarios and expected outputs.
  • CI
    • Extended CI to run the new runtime validation during both test passes.

claude added 4 commits June 26, 2026 11:13
The in-scope half of слой 2: validate the architecture pass on a real, faithful
graph of the LeakyOracle app (8 internal types + the framework types they touch)
instead of synthetic fixtures, and pin the graph.json contract the Own.NET-side
Roslyn extractor must emit for the oracle.

 - oracle/fixtures/graph.json — faithful arch-graph of the oracle (ownAudit/arch-graph/v1).
 - oracle/fixtures/rules.json — an MVVM layering profile (Views -> ViewModels ->
   Services; reverse forbidden), showing the rules engine is configurable per
   codebase, not STS-hardcoded.
 - test_oracle_arch.py: clean faithful graph -> 0 findings (arch/ doesn't cry wolf
   on well-layered MVVM; the oracle's smells are lifetime/heap, not architecture).
   One planted MVVM inversion (WatchlistViewModel -> MainWindow) -> exactly
   ARCH-MVVM-VM-VIEW + ARCH-CYCLE-TYPE + ARCH-CYCLE-NS. Also guards the graph
   against drift (every internal node must point at a real file under oracle/).

Wired into CI (normal + -O). All suites green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
… P2, CodeRabbit)

Codex: the documented extractor contract (docs/arch-graph.md) counts param/return
types and generic args as dependencies, so a faithful extractor emits System.String
edges for EventHandler<string>/Tick(string)/QuoteRow(string)/string[] — which the
golden omitted. Add System.String (an external node) + its edges from the six types
that reference it. String earns its place: string duplication is one of the oracle's
own smells.

Reframe to match reality: hand-authoring can't be byte-exact on BCL leaves, so the
INTERNAL type graph is the contract the extractor must reproduce; external nodes list
the meaningful framework types, and pure-infrastructure leaves (Int32, Console, GC,
attributes, arrays) are elided — a faithful extractor emits a superset. The test
already pins only the internal subgraph, not external completeness.

CodeRabbit: the _comment's stale reference to a non-existent graph.degraded.json is
removed by the same reframe (the regression variant is in-memory in the test).

Still 0 findings on the clean graph; test 4/4 normal + -O.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
(а) A second intentional, heap-provable lifetime leak in the oracle: TickerViewModel
starts a recurring System.Threading.Timer in its ctor and never disposes it, so it
stays rooted by the runtime's TimerQueue (own-check OWN-TIMER; coverage matrix
"Timers"). FixedTickerViewModel disposes it — the control case. LeakScenario now
proves BOTH leaks (subscription + timer) and BOTH fixes through the same
WeakReference harness: 50/50 leaky survive, 0/50 fixed collected, stable across runs.
Builds clean on net8.0, zero warnings.

(б) Runtime correlation golden fixture — phase 5 on real, oracle-shaped data:
 - findings.json: the static suspects own-check would emit (OWN001 subscription on
   WatchlistViewModel, OWN-TIMER on TickerViewModel); resource is a description, so
   correlate keys on the source-file stem.
 - runtime.json: heap evidence faithful to the leak proof — 50 of each leaky VM
   retained (expected 0), plus the 250k QuoteRow they transitively retain.
 - test_oracle_runtime.py: asserts the three-way split — both leaks CONFIRMED high
   (each naming the leaked CLR type + the static rule it corroborates), nothing
   static-only, QuoteRow surfaces RUNTIME-ONLY (a static blind spot), the high gate
   fails on the two confirmed leaks. Drift guard ties every retained type to a real
   oracle source file.

Both fixtures pin the contract the слой-2 collectors (ClrMD heap walk) must emit;
when they land in Own.NET, these same passes run on extracted data. Wired into CI
(normal + -O); all suites green.

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

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dd7c1d1c-fd72-4011-ba4e-9f2f8a18c8e7

📥 Commits

Reviewing files that changed from the base of the PR and between 777f82b and 155648d.

📒 Files selected for processing (2)
  • oracle/fixtures/findings.json
  • oracle/fixtures/test_oracle_runtime.py

📝 Walkthrough

Walkthrough

The PR adds a timer leak/control pair, expands the leak scenario to cover subscription and timer cases, adds runtime correlation fixtures and tests, and wires the new runtime test into CI.

Changes

Oracle timer leak correlation

Layer / File(s) Summary
Timer view-models
oracle/LeakyOracle/ViewModels/TickerViewModel.cs, oracle/LeakyOracle/ViewModels/FixedTickerViewModel.cs
Adds a leaky recurring-timer view-model and a disposable fixed control that disposes its timer in Dispose().
Dual leak scenario
oracle/LeakyOracle/LeakScenario.cs, oracle/README.md
LeakScenario.Run now checks subscription and timer leak/fixed pairs, and the Oracle README output and smell notes report both cases.
Runtime fixture contract
oracle/fixtures/findings.json, oracle/fixtures/runtime.json, oracle/fixtures/README.md
Adds the static findings fixture, the runtime fixture, and fixture documentation for correlation behavior and run instructions.
Correlation test and wiring
oracle/fixtures/test_oracle_runtime.py, oracle/fixtures/graph.json, oracle/fixtures/test_oracle_arch.py, .github/workflows/ci.yml
Adds the runtime-correlation test runner, updates the graph and architecture drift test for the new timer types, and runs the new test from CI in normal and -O steps.

Sequence Diagram(s)

sequenceDiagram
  participant TestScript as test_oracle_runtime.py
  participant RuntimeCorrelate as runtime.correlate
  participant Gate as C.gate
  TestScript->>RuntimeCorrelate: correlate findings.json and runtime.json
  RuntimeCorrelate-->>TestScript: confirmed, static_only, runtime_only
  TestScript->>Gate: gate(res, "high")
  Gate-->>TestScript: blocked items for high confidence
Loading

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PhysShell/OwnAudit#9: Modifies the same CI test step to add oracle/fixtures/test_oracle_runtime.py.
  • PhysShell/OwnAudit#20: Extends the oracle graph fixture and the associated oracle test coverage around the same leak-scenario model.

Poem

I thumped through timers, tick by tick,
then hopped past leaks both fast and slick.
The fixed ones tucked their clocks away,
while oracle clues came out to play.
Now both the burrows sing “hoppity!” 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main additions: a timer leak and a golden runtime-correlation fixture.
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/sts-runtime-analysis-2mo4z9

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: 9a87eb60a4

ℹ️ 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".

/// The `_timer` field is deliberate — without it the public Timer wrapper would be finalized and stop
/// the timer; holding it keeps the timer alive (and the leak real), exactly as leaky code does.
/// </summary>
public sealed class TickerViewModel

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update the oracle graph for ticker view models

I checked oracle/fixtures/graph.json and oracle/fixtures/test_oracle_arch.py: the golden architecture contract still describes 8 internal types and has no TickerViewModel/FixedTickerViewModel nodes or LeakScenario edges to them. With this new view-model in the oracle, a real Roslyn extraction over oracle/LeakyOracle would now emit those two types, so the pinned graph is no longer faithful and the drift guard keeps passing the stale fixture instead of catching the mismatch.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Correct on both counts — fixed in 777f82b. Adding the timer VMs in (а) left the arch golden at 8 internal types, and the old drift guard ("every node points at a real file") structurally couldn't catch a missing type. Fix: added TickerViewModel/FixedTickerViewModel nodes + their edges (LeakScenario → Ticker VMs, Ticker → System.Threading.Timer, FixedTicker → IDisposable), and replaced the guard with a both-directions check — the graph's internal node set must equal the set of oracle source types (glob of LeakyOracle/**/*.cs), so a new view-model with no node (or a node for a deleted type) now fails the test. Still 0 findings on the clean graph; 4/4 normal + -O.


Generated by Claude Code

…n drift guard (Codex P2)

(а) added TickerViewModel/FixedTickerViewModel to the oracle but the arch golden
graph still described 8 internal types — a faithful extraction would now emit 10,
so the pinned graph was stale, and the old drift guard ("every node points at a
real file") couldn't catch a MISSING type.

Fix both: add the two nodes + their edges (LeakScenario -> Ticker VMs; Ticker VMs
-> System.Threading.Timer; FixedTicker -> IDisposable) and a System.Threading.Timer
external node. Replace the guard with a both-directions check: the graph's internal
node set must EQUAL the set of oracle source types (glob of LeakyOracle/**/*.cs),
so a new view-model without a node — or a node for a deleted type — fails the test.

Still 0 findings on the clean graph; planted-inversion test unchanged; 4/4 normal + -O.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
oracle/fixtures/test_oracle_runtime.py (1)

36-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin the oracle fixture's correlation config.

_correlate() currently pulls the shared runtime config, so unrelated tuning in runtime/config.json can change this fixture's confirmed/static-only split or downgrade the expected high confidence without any oracle fixture drift. For a golden fixture, pass an explicit local config here (or load one from oracle/fixtures) so the contract only changes when these fixtures do.

Proposed change
+ORACLE_RUNTIME_CONFIG = {
+    "leak_categories": ["subscription-leak", "idisposable-leak", "region-escape"],
+    "default_expected": 1,
+    "min_count": 2,
+    "high_count": 10,
+}
+
 def _correlate():
-    findings = json.load(open(FINDINGS, encoding="utf-8"))["findings"]
-    dump = json.load(open(RUNTIME, encoding="utf-8"))
-    return C.correlate(findings, dump, C.load_config()), dump
+    findings = json.load(open(FINDINGS, encoding="utf-8"))["findings"]
+    dump = json.load(open(RUNTIME, encoding="utf-8"))
+    return C.correlate(findings, dump, ORACLE_RUNTIME_CONFIG), dump
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@oracle/fixtures/test_oracle_runtime.py` around lines 36 - 39, The
_correlate() fixture is using the shared C.load_config() runtime configuration,
which makes the oracle output sensitive to unrelated config changes. Update
_correlate() in test_oracle_runtime.py to load an explicit fixture-local
correlation config from oracle/fixtures instead of the shared runtime config,
while still passing it into C.correlate(...) alongside findings and dump. Keep
the change scoped to the _correlate() helper so the golden oracle contract only
changes when the fixture data changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@oracle/LeakyOracle/ViewModels/TickerViewModel.cs`:
- Around line 23-28: The OWN-TIMER fixture is pointing at the wrong location for
TickerViewModel; update the finding in oracle/fixtures/findings.json so it
references the timer allocation inside TickerViewModel instead of the
constructor header. Use the _timer assignment in TickerViewModel() as the anchor
and move the fixture to the line where new Timer(...) is created.

---

Nitpick comments:
In `@oracle/fixtures/test_oracle_runtime.py`:
- Around line 36-39: The _correlate() fixture is using the shared
C.load_config() runtime configuration, which makes the oracle output sensitive
to unrelated config changes. Update _correlate() in test_oracle_runtime.py to
load an explicit fixture-local correlation config from oracle/fixtures instead
of the shared runtime config, while still passing it into C.correlate(...)
alongside findings and dump. Keep the change scoped to the _correlate() helper
so the golden oracle contract only changes when the fixture data changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d1d4541a-ef3d-4fe7-8ee6-721566728140

📥 Commits

Reviewing files that changed from the base of the PR and between 247bda0 and 9a87eb6.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • oracle/LeakyOracle/LeakScenario.cs
  • oracle/LeakyOracle/ViewModels/FixedTickerViewModel.cs
  • oracle/LeakyOracle/ViewModels/TickerViewModel.cs
  • oracle/README.md
  • oracle/fixtures/README.md
  • oracle/fixtures/findings.json
  • oracle/fixtures/runtime.json
  • oracle/fixtures/test_oracle_runtime.py

Comment thread oracle/LeakyOracle/ViewModels/TickerViewModel.cs
…Rabbit)

 - findings.json: anchor each suspect at its real fix site — OWN-TIMER at the
   `new Timer(...)` allocation (TickerViewModel.cs:24 -> :27), and (for the same
   honesty) OWN001 at the `+=` line (WatchlistViewModel.cs:35 -> :32).
 - test_oracle_runtime.py: pin a local ORACLE_CFG instead of C.load_config(), so
   this golden depends only on its own fixtures — unrelated tuning in the shared
   runtime/config.json can no longer shift the confirmed/runtime-only split or
   downgrade the expected `high`. Mirrors the default; same convention as
   runtime/tests/test_runtime.py.

Correlation keys on the file stem, so the line fixes don't change the split; 6/6
normal + -O.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
@PhysShell
PhysShell merged commit da65d1c into main Jun 26, 2026
2 of 3 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