Skip to content

fix(guard): the observable→Task bridge guard enforced a SPELLING — make it detect the thing - #2762

Merged
rbuergi merged 7 commits into
mainfrom
fix/zero-tolerance-bridge-guard
Aug 30, 2026
Merged

rbuergi merged 7 commits into
mainfrom
fix/zero-tolerance-bridge-guard

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

What was wrong

ObservableToTaskBridgeGuard matched the literal text .ToTask( and its own remarks claimed src/ was at zero "with no escape hatch". Measured today, that claim was false as enforced, in two independent ways:

Site Why the marker missed it
src/MeshWeaver.Reactive.Assertions/ReactiveWait.cs Hand-rolls the bridge from a TaskCompletionSource + Subscribe. Not one character of .ToTask( in it.
src/MeshWeaver.Mesh.Contract/Services/MeshServiceExtensions.cs Declares a method literally named ToTask and calls it as ToTask<bool>(service.DeleteNode(path), ct). The marker's leading dot — documented as deliberate ("any .ToTask( in this repo is Rx's") — walked straight past a static call to a hand-rolled bridge of exactly the banned name.

The second was also the unsafe form (a bare new TaskCompletionSource<T>()), i.e. a live inline-resumption hazard whose remaining callers are 58 in-mesh MeshWeaver.Reinsurance layout-area sites — hub-reachable code, exactly where an inline resumption wedges a turn.

What this changes

Detection is now structural and name-independent. Rename ToTask to First and it still fires:

  1. A TaskCompletionSource settled from inside a Subscribe callback.
  2. A Task-returning method over an IObservable<> that builds its own completion source.
  3. An IObserver<> implementation that settles a completion source (the same bridge with its callbacks extracted into a class).

Two tiers, deliberately different in strength:

  • Unsafe form (no RunContinuationsAsynchronously) → ZERO. No register, no allow file, no exemption. That flag is the LiveQueryHandoffDropTest fails post-warmup-fix (e2080c4fa) — new information per #2338's own ask #2377 defect.
  • Safe form → only the entries in SanctionedBridges, each machine-verified to still exist, still contain a bridge, and still be the safe form — so an entry cannot outlive its subject. Four entries: ReactiveCompletion (the one real implementation), ReactiveWait, MeshServiceExtensions (retained debt, already governed by MeshServiceHasNoTaskShimGuard) and MessageHubGrain (grain Task is contractual, and it must map an activation fault to a successful classified DeliveryFailure, which ObserveCompletion cannot express).

The one real defect found is fixed: MeshServiceExtensions.ToTask now constructs with RunContinuationsAsynchronously. No signature change, no caller impact.

🚨 Where the line is — this bans BRIDGING, never the Task type

Stated in the guard's remarks and pinned by its tests, because getting it wrong would make the rule unshippable:

  • IIoPool is the sanctioned async/IO boundary (measured: it constructs no TaskCompletionSource at all).
  • Orleans grain signatures are Task by contract.
  • A TaskCompletionSource used as a plain lifecycle signal is not a bridge — MessageHub.hasStarted is settled from Start()/FailStartup() and is correctly invisible.
  • A Task-returning method that merely consumes the sanctioned bridge is not hand-rolling one — HubDisposalJoin.JoinDisposalAsync passes, and the negative control asserts it passes.

What I deliberately excluded, and why

  • .Result — overwhelmingly a domain property here (ToolCall.Result, PatchResult.Result). A marker matching it would flag a hundred innocent reads to catch one bridge, and a rule that cries wolf gets suppressed, which is worse than not having it. Narrowing it to ".Result on a task-returning expression" was considered and rejected: the narrowing needs a call graph to be sound, and a marker that is right most of the time is the same "spelling, not the thing" failure this PR exists to end. .GetAwaiter().GetResult() is unambiguous and is matched.
  • .Wait(timeout) — matched only with empty parens. The bounded form is legitimate in the two places it appears: IoPool's _gate.Wait(DrainTimeout) (the SemaphoreSlim AGENTS.md sanctions inside IoPool) and HubDisposalJoin's deliberate Task.Wait(TimeSpan). The empty-paren spelling is the unbounded park.
  • A bare TaskCompletionSource ban — would red on MessageHub.hasStarted and on ReactiveCompletion itself, i.e. on the very fix every failure message tells authors to use.

Deviation from the brief (flagging explicitly)

The brief asked for .GetAwaiter().GetResult() and .Wait( at zero, no allow file. Production roots do not start clean: 9 sites across 5 files (compiler, Orleans test-base disposal, plugin tester, a sample). Holding them at zero today would red on merge. They get a seeded ratchet (test/ProductionBlockingBridgeSites.allow) that may only shrink. This still closes a real gap: BlockingBridgeInTestRatchetGuard scans test/ only, on the stated grounds that src/ is covered "by the reviews that enforce it" — reviews are not a gate, and that remark is corrected in this change.

Proof it can actually fail

Non-vacuity is proved, not asserted. The guard builds a real temp tree with a real production root, drops a hand-rolled bridge into it containing no .ToTask( at all, and drives the same scanner over it — asserting the unsafe fixture is caught and classified unsafe, the safe fixture is caught and classified safe, and a mere consumer is not caught.

Mutation A — restore the bare new TaskCompletionSource<T>() in MeshServiceExtensions:

NoProductionBridgeUsesTheUnsafeForm                [FAIL]
  src/MeshWeaver.Mesh.Contract/Services/MeshServiceExtensions.cs
      line 60: 'ToTask' returns a Task over an IObservable<> and builds its own TaskCompletionSource
      line 69: an IObserver<> implementation that settles a TaskCompletionSource
TheSanctionedBridgeRegisterIsStillAccurate         [FAIL]  (not the safe form)
NoProductionCodeBridgesAnObservableToATask         [PASS]  ← the OLD rule, blind to it

Mutation B — revert detection to the .ToTask( spelling alone:

NoProductionBridgeUsesTheUnsafeForm                [PASS]  ← green on a tree with 4 bridges
EveryProductionBridgeIsASanctionedImplementation   [PASS]  ← the false pass that stood before
TheScannerCatchesAHandRolledBridgeInAProductionRoot [FAIL] ← only the control catches it

Deferred, by coordinator decision

Moving MeshWeaver.Reactive.Assertions to test/ is NOT in this PR. It must land after #2750, which is rewriting test/MeshWeaver.Reactive.Assertions.Test; a rename underneath it turns a clean merge into a path-rename conflict across 343 files. A peer worktree is also editing ReactiveWait.cs right now. The register entry carries that follow-up in its reason text, and goes stale — failing loudly — the moment the move lands.

Framing note: ReactiveWait is exonerated as a design. It introduced no defect. NodeTypeCompileParkTest carried a latent ordering assumption that Rx's bridge was masking by resuming the test inline; ReactiveWait correctly queues that continuation, so the latent race became visible. Removing a bridge surfaces latent ordering bugs rather than creating them — that is now a section in the doc, and it is the strongest argument for the zero-tolerance rule. The pending move is layering, not a defect fix.

Conserved

Doc/Architecture/RemovingObservableToTaskBridges gains the spelling-vs-thing lesson, the where-the-line-is boundary, the new rule table, and the "removing a bridge surfaces latent bugs" section.

No What's New entry — deliberate. This is agent/maintainer-facing tooling with no user-visible behaviour change.

Verification

  • dotnet build src/MeshWeaver.Mesh.Contract -c Release -warnaserror → 0 Warning(s), 0 Error(s) (fresh assembly, not a no-op)
  • dotnet build test/MeshWeaver.Documentation.Test -c Release -warnaserror → 0 Warning(s), 0 Error(s)
  • dotnet test test/MeshWeaver.Documentation.Test → 109/109 passed, including DocumentationLinkIntegrityTest (the MeshWeaver.Documentation project rebuilt in the same run, so the edited embedded markdown was re-embedded first)

🤖 Generated with Claude Code

… thing

ObservableToTaskBridgeGuard matched the literal text `.ToTask(` and claimed
src/ was at zero "with no escape hatch". Measured 2026-08-30, that claim was
FALSE as enforced, in two independent ways:

  * src/MeshWeaver.Reactive.Assertions/ReactiveWait.cs hand-rolls the bridge
    from a TaskCompletionSource + Subscribe — no `.ToTask(` anywhere in it.
  * src/MeshWeaver.Mesh.Contract/Services/MeshServiceExtensions.cs declares a
    method LITERALLY NAMED ToTask and calls it as
    `ToTask<bool>(service.DeleteNode(path), ct)`. The marker's leading `.` —
    documented as deliberate — walked straight past it. It was also the UNSAFE
    form (bare `new TaskCompletionSource<T>()`), i.e. a live inline-resumption
    hazard on hub-reachable in-mesh callers.

Detection is now STRUCTURAL and name-independent: a TCS settled from inside a
Subscribe callback; a Task-returning method over an IObservable<> that builds
its own completion source; an IObserver<> implementation that settles one.

Two tiers, deliberately different in strength:
  * UNSAFE form (no RunContinuationsAsynchronously) — ZERO, no register, no
    allow file. That flag IS the #2377 defect.
  * SAFE form — only the entries in SanctionedBridges, each machine-verified to
    still exist, still contain a bridge, and still be the safe form, so an entry
    cannot outlive its subject.

Where the line is, stated in the guard and pinned by its tests: this bans
BRIDGING, never the Task type. IIoPool, Orleans grain signatures, a TCS used as
a plain lifecycle signal (MessageHub.hasStarted) and a mere CONSUMER of the
sanctioned bridge (HubDisposalJoin.JoinDisposalAsync) all pass.

Also fixes the one real defect found: MeshServiceExtensions.ToTask now
constructs with RunContinuationsAsynchronously.

Blocking bridges (.Wait() / .GetAwaiter().GetResult()) in production roots get a
seeded ratchet rather than a zero — they did not start clean (9 sites, 5 files),
and BlockingBridgeInTestRatchetGuard had never scanned production, on the stated
grounds that src/ is covered "by the reviews that enforce it". Reviews are not a
gate; that remark is corrected in the same change.

Non-vacuity is proved, not asserted: the guard builds a real temp tree with a
real production root, drops a hand-rolled bridge into it containing no
`.ToTask(` at all, and drives the same scanner over it — asserting the unsafe
fixture is caught AND classified unsafe, the safe one caught AND classified
safe, and a consumer NOT caught.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 30, 2026 11:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR strengthens ObservableToTaskBridgeGuard so it detects observable→Task bridges by structure (e.g., TaskCompletionSource settled from inside Subscribe) rather than by the .ToTask( spelling, and adds a separate ratchet for blocking bridges (.Wait() / .GetAwaiter().GetResult()) in production roots.

Changes:

  • Reworked ObservableToTaskBridgeGuard to structurally detect hand-rolled observable→Task bridges, distinguish safe vs unsafe forms, and verify a sanctioned-bridge register.
  • Added a seeded ratchet allow-list for blocking bridges in production roots (test/ProductionBlockingBridgeSites.allow) and corrected the related test-only guard remarks.
  • Updated documentation (RemovingObservableToTaskBridges.md) to capture the “spelling vs thing” lesson, the boundary conditions, and the ratchet model.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/ProductionBlockingBridgeSites.allow Seeded allow-list for blocking-bridge sites in production roots with a “may only shrink” budget.
test/MeshWeaver.Documentation.Test/ObservableToTaskBridgeGuard.cs Implements structural bridge detection + safe/unsafe classification + sanctioned register + production blocking ratchet.
test/MeshWeaver.Documentation.Test/BlockingBridgeInTestRatchetGuard.cs Updates remarks to reflect production blocking-bridge scanning is now gated elsewhere.
src/MeshWeaver.Mesh.Contract/Services/MeshServiceExtensions.cs Fixes the previously-unsafe TaskCompletionSource construction by using RunContinuationsAsynchronously.
src/MeshWeaver.Documentation/Data/Architecture/RemovingObservableToTaskBridges.md Documents the updated guard approach and rationale (structural detection, boundaries, seeded ratchets).
Suppressed comments (1)

src/MeshWeaver.Mesh.Contract/Services/MeshServiceExtensions.cs:66

  • ToTask claims to complete with the first emitted value, but the subscription is never disposed after TrySetResult/TrySetException. That can keep the observable pipeline alive (and keep consuming values/errors) after the returned Task is already completed, leaking resources and potentially hiding late faults. Dispose the subscription on OnNext/OnError/cancellation using a SingleAssignmentDisposable so synchronous-on-subscribe sources are handled safely.
        var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
        var sub = observable.Subscribe(new SingleObserver<T>(tcs));
        if (ct.CanBeCanceled)
            ct.Register(() => { tcs.TrySetCanceled(); sub.Dispose(); });
        return tcs.Task;

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread test/MeshWeaver.Documentation.Test/ObservableToTaskBridgeGuard.cs
@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

726 tests  +5   534 ✅ +5   4m 57s ⏱️ ±0s
  5 suites ±0   192 💤 ±0 
  5 files   ±0     0 ❌ ±0 

Results for commit 8fb1465. ± Comparison against base commit d1a8259.

♻️ This comment has been updated with latest results.

Review finding on #2762, and it is this PR's own defect class one level down:
`new\s+TaskCompletionSource` missed the fully-qualified
`new System.Threading.Tasks.TaskCompletionSource<T>(…)` (and `global::`) and
the target-typed `TaskCompletionSource<T> x = new(…)`.

Missing a CONSTRUCTION is worse than missing a call site. IsSafeForm asks "do
ALL constructions carry RunContinuationsAsynchronously?", so a construction the
regex cannot see makes the answer vacuously YES — the unsafe-form zero rule then
passes having checked nothing.

Closed by test, not by a claimed regex: the negative control now carries a
fully-qualified fixture and a target-typed fixture, and asserts each is BOTH
detected AND classified unsafe. Mutation-checked — narrowing the regex back
makes the control fail with "the safe-form classifier called
QualifiedUnsafeBridge.cs SAFE".

No occurrence of either spelling exists in the tree today, so this is pure
hardening with no behaviour change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

917 tests  ±0   917 ✅ ±0   5m 28s ⏱️ -1s
  6 suites ±0     0 💤 ±0 
  6 files   ±0     0 ❌ ±0 

Results for commit 8fb1465. ± Comparison against base commit d1a8259.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

801 tests  ±0   801 ✅ ±0   6m 46s ⏱️ -5s
  6 suites ±0     0 💤 ±0 
  6 files   ±0     0 ❌ ±0 

Results for commit 8fb1465. ± Comparison against base commit d1a8259.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 547 tests  ±0   1 547 ✅ ±0   6m 22s ⏱️ -4s
    8 suites ±0       0 💤 ±0 
    8 files   ±0       0 ❌ ±0 

Results for commit 8fb1465. ± Comparison against base commit d1a8259.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

   10 files  ±0     10 suites  ±0   5m 59s ⏱️ +9s
3 481 tests ±0  3 481 ✅ ±0  0 💤 ±0  0 ❌ ±0 
4 139 runs  ±0  4 139 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 8fb1465. ± Comparison against base commit d1a8259.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

966 tests  ±0   962 ✅ ±0   6m 1s ⏱️ +2s
  6 suites ±0     4 💤 ±0 
  6 files   ±0     0 ❌ ±0 

Results for commit 8fb1465. ± Comparison against base commit d1a8259.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   41 files  ±0     41 suites  ±0   35m 35s ⏱️ -1s
8 438 tests +5  8 242 ✅ +5  196 💤 ±0  0 ❌ ±0 
9 096 runs  +5  8 900 ✅ +5  196 💤 ±0  0 ❌ ±0 

Results for commit 8fb1465. ± Comparison against base commit d1a8259.

♻️ This comment has been updated with latest results.

rbuergi and others added 2 commits August 30, 2026 14:16
CI red on the merge ref, and the guard was RIGHT. #2750 landed
src/MeshWeaver.Fixture/ObservableAwait.cs on main while this PR was open: the
test tree's one wait, introduced when 1,538 `.ToTask(` call sites were swept.

It is a hand-rolled observable→Task bridge in a production root, and it contains
NOT ONE CHARACTER of `.ToTask(` — so the spelling-only rule this PR replaces
would have waved it through. Only EveryProductionBridgeIsASanctionedImplementation
fired; the unsafe-form rule stayed green because ObservableAwait is correctly
built with RunContinuationsAsynchronously. That is exactly the two-tier split
behaving as designed.

Registered with its reason (same category as ReactiveWait: infrastructure whose
purpose is to hand a test a Task, shipping from a production root because
MeshWeaver.Fixture is where the test bases live).

Merged main to pick it up — CI evaluates the merge ref, so the branch could not
see it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi

rbuergi commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

CI status: the guard is green; the one red is the known MeshNodeLanguageServiceTest intermittent, which a peer owns. Not re-run, per house rule.

Run 1 (fcd9cc78e) — red, and the guard was RIGHT

ObservableToTaskBridgeGuard.EveryProductionBridgeIsASanctionedImplementation failed on the merge ref. Cause: #2750 landed src/MeshWeaver.Fixture/ObservableAwait.cs on main while this PR was open — the test tree's one wait, introduced when 1,538 .ToTask( call sites were swept.

That is this guard's first live catch, and the best evidence for the whole change: ObservableAwait is a hand-rolled observable→Task bridge in a production root containing not one character of .ToTask(. The spelling-only rule this PR replaces would have waved it straight through.

The two-tier split also behaved exactly as designed — only the register rule fired; NoProductionBridgeUsesTheUnsafeForm stayed green, because ObservableAwait is correctly built with RunContinuationsAsynchronously. Registered with its reason in 2f5ffae2f, and main merged so the branch can see it.

Run 2 (2f5ffae2f) — one red, not mine

MeshWeaver.Hosting.Monolith.Test.MeshNodeLanguageServiceTest.ScriptCompletions_FilterByTypedPrefix_NotJustTheAlphabet (shard 2). Attribution:

  • My diff does not touch MeshWeaver.Hosting.Monolith.Test.
  • The failing test does not reference the one production symbol I changed (MeshServiceExtensions.ToTask).
  • Same production change, two runs, different outcome: fcd9cc78e already carried the RunContinuationsAsynchronously fix and this test passed there — only my guard failed. Intermittent, not deterministic.
  • Main is independently intermittently red over the same window (2 failures in the last 8 runs, a different flaky test each time — e.g. DataContextInitFaultedTest on 0c584a2d5's predecessor).

ObservableToTaskBridgeGuard and the full MeshWeaver.Documentation.Test suite (109/109) passed in run 2.

🚫 Not merging — an active merge hold is in force on core's CD lane. Holding for an explicit go.

rbuergi and others added 3 commits August 30, 2026 14:33
Conserving the unverified items where the next person will trip on them, rather
than leaving them in a session report:

1. A `using` alias (`using Tcs = …TaskCompletionSource<int>;` then `new Tcs()`)
   still evades the construction detectors — and the consequence is the bad one:
   IsSafeForm asks "do ALL constructions carry RunContinuationsAsynchronously?",
   so a construction it cannot see makes the answer vacuously YES.
2. MeshServiceExtensions' SingleObserver.OnCompleted is an empty body, so a
   source that completes WITHOUT emitting leaves the task pending forever.
   Deliberately unchanged (behaviour change; belongs with the port), but the
   shim is now the odd one out — ObservableAwait and ReactiveCompletion both
   settle on completion.
3. The shim's only real callers are 58 in-mesh Reinsurance sites that compile at
   RUNTIME, so the RunContinuationsAsynchronously fix was never exercised
   against a single real caller by any build or test in this repo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… budgets

#2764 fixed what the guard is SEEDED with (1,548 allowances against 2 actual,
19 stale memex/ entries, so it could not fail). This PR fixes what the guard can
SEE. Both are needed; they are orthogonal.

Resolution, agreed with the peer: this branch's ObservableToTaskBridgeGuard.cs
wins wholesale, then theirs is re-applied for the seeding facts only —
ProductionRoots now includes "memex", TotalBudget 1548 -> 2, and the re-seeded
test/ObservableToTaskBridgeSites.allow (single entry, DisposalWaitBridgeTest 2).
git auto-merged to exactly that; verified rather than trusted.

🚨 memex/ moved to ZERO TOLERANCE with no allow file on the strength of a TEXT
marker. Checked it here with all three STRUCTURAL detectors before merging,
because a tree emptied of `.ToTask(` is not the same as a tree free of
hand-rolled bridges — that distinction is this whole PR. Result: memex/ is clean
on every detector — 0 structural bridges, 0 blocking sites, 0 .ToTask(. The
zero-tolerance promotion is safe, and now means what it says.

Also corrects the root prose the merge made stale, in the guard and the doc:
RatchetedRoots no longer claims to hold memex/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi merged commit 3199350 into main Aug 30, 2026
25 checks passed
@rbuergi
rbuergi deleted the fix/zero-tolerance-bridge-guard branch August 30, 2026 12:54
rbuergi added a commit that referenced this pull request Aug 30, 2026
…ion and ratcheted in tests (#2782)

* feat(guard): hand-woven concurrency gates are held at zero in production and ratcheted in tests

A SemaphoreSlim / ManualResetEventSlim / AutoResetEvent / CountdownEvent /
Monitor.Wait parks a thread. On a hub action block or a grain turn that is the
deadlock the mesh cannot recover from — the class /async names alongside await
on a hub. AGENTS.md states the rule; nothing enforced it.

Two tiers, the shape #2762 established:

- ProductionRoots (src, tools, samples, clients, memex) — ZERO, no allow file.
  The only permitted gates are SanctionedGates entries, VERIFIED not listed:
  each must still exist and still contain a gate, so an entry cannot outlive
  its subject.
- RatchetedRoots (test) — a seeded inventory that may only shrink. Measured on
  main's tip: 79 sites across 24 files, every one a ManualResetEventSlim, 30 of
  them in IoPoolTest.cs. TotalBudget 79.

🚨 The guard found a production violation on its first run that a src/-only
grep had missed: tools/MeshWeaver.ThumbnailGenerator. Registered rather than
converted, with the premise VERIFIED rather than asserted in prose — its csproj
is an Exe referencing Microsoft.Playwright, SixLabors.ImageSharp and
System.CommandLine and NOT ONE MeshWeaver assembly, so there is no hub, no
grain turn and no IIoPool in that process and the defect cannot occur. The
register re-reads that csproj on every run and FAILS if a MeshWeaver
ProjectReference ever appears: an exemption whose PREMISE silently stops
holding is worse than one whose subject moved, because the subject's absence at
least fails loudly. `tools` stays in scope rather than being dropped, so a gate
added to a tool that DOES reference the mesh still fails.

The self-test plants a real tree and runs the REAL scan over it — .cs, .csx,
prose, a string literal and bin/ — rather than matching the regex against
strings. That distinction is the defect this repo found in three separate
guards today.

Verified by MUTATION, each arm proven able to fail and then restored:
  A new gate in src/                        -> RED (production zero)
  A new file in test/ not in the allow list -> RED (NEW SITE)
  A listed test file's count grows          -> RED (may only shrink)
  restored                                  -> GREEN 4/4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* re-seed against main's tip: #2770 added two gates while this PR was open

The ratchet caught real drift on its first exposure to a moving main —
IoPoolTest.cs 30 -> 32, from #2770 (`a pooled-I/O residual NAMES the leaf that
would not unwind`) landing after this branch took its seed.

Re-seeded rather than argued with: the allow file's own header says to seed
against main's TIP, not the branch point, precisely because CI checks out the
merge ref. TotalBudget 79 -> 81, with the reason recorded in the guard's
remarks so the next reader does not read 81 as the original measurement.

🚨 This is the ratchet working, not a concession. Those two sites come back out
in the IoPool drain-budget change, which lowers the budget to 79 again — the
mechanism the file documents (delete a line, lower the budget by exactly that
amount, in the same change).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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