Guide audit: fix stale samples, phantom APIs, and the doc-pipeline gaps that let them rot - #1120
Conversation
Staleness audit of the recipes folder plus dev-tooling, vs-extension,
testing, performance, and diagnostics. Every bare (uncompiled) csharp
block in scope was checked symbol-by-symbol against skills/reactor.api.txt
and src/**; broken examples were promoted to compiled snippets where
self-contained, corrected in place otherwise.
Phantom APIs removed (named in docs, absent from the codebase):
- testing: ElementDescription.Of, A11yDiagnostic.Rule, "ButtonName",
UseAsync, and a NamedButton fixture that was referenced but never defined
- dev-tooling: Spacer(), Application.Current.Reload()
- diagnostics: HResults.E_FAIL
- performance: ResetDebugCounters(), ReconcileCore()
CLI surface corrected against `mur --help` and Program.cs:
- `mur devtools serve` does not exist; `mur devtools` is the launcher
+ MCP host. Rewrote the MCP section, which described a docs-inventory
server rather than the live-app inspection surface it actually is.
- added doctor / upgrade / figma / docs check-tier and the top-level
--create/--skill/--api options; fixed --tier and --skip-screenshots
- removed dead link to docs/contributing/devtools.md (file does not exist)
Internal-vs-public boundaries clarified: UseDevtools() has no Component
forwarder (the old example could not compile), and HotReloadService,
DiagnosticLog, LogCategory, HResults and ReactorEventSource are internal,
so app-facing examples no longer name them.
Recipes: 9 of 10 were already idiomatic. recipe-login hand-rolled
submitting/error UseState pairs that UseMutation now owns — rewritten to
UseMutation with IsPending/Error. The recipes index still advertised
paginated-list, multi-step-form, command-palette and drag-reorder as
"Phase 2.5 / coming soon"; all four shipped, so they are now linked.
Also: added the headless-test COMException constraint and the two-place
fixture registration gotcha to testing; added the HotReload keyword
(0x2000) to the diagnostics keyword table; corrected the supported
Visual Studio versions on vs-extension.
Verified: all three touched doc apps build clean in Release/x64,
`mur docs compile` regenerates with no new warnings, tier-lint reports
0 errors, and every relative link in scope resolves. The compiled-snippet
loop was mutation-checked (breaking a promoted symbol reddens the build).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Audit of the onboarding & setup slice (index, getting-started,
thinking-in-reactor, xaml-developers, reactor-vs-xaml, cheat-sheet,
packaging). Bare (uncompiled) code blocks and prose API references had
drifted from the real public surface.
index:
- "Minimal Project Setup" told external readers to consume Reactor via
<ProjectReference Include="..\Reactor\Reactor.csproj" />, a path that
only exists inside this repo, so the documented first run was broken.
Now uses <PackageReference Include="Microsoft.UI.Reactor"
Version="{{reactorVersion}}" /> via the pipeline token.
- Dropped the explicit Microsoft.WindowsAppSDK 2.1.* reference: the
package already brings Microsoft.WindowsAppSDK.WinUI 2.1.0
transitively (verified against the published nuspec).
- Added the RuntimeIdentifier auto-resolve condition. Without it a bare
`dotnet build` / `dotnet run` - which the very next line tells the
reader to run - fails with "WindowsAppSDKSelfContained requires a
supported Windows architecture". <Platforms> alone does not fix it.
- ai:lock markers preserved.
getting-started:
- Text(...) is not a factory; the compiled snippet on the same page
already used TextBlock(...). Fixed 3 prose references.
- "Running with devtools" was wrong on three counts: the dev menu is
not gated by #if DEBUG, there is no Ctrl+Shift+D shortcut, and a
plain `dotnet run -c Debug` starts with no dev menu because the
default launch profile passes no args. Rewritten around the real
model: Reactor.DevtoolsSupport + `--devtools app`.
reactor-vs-xaml:
- Added a doc app so its examples are compiled by CI; promoted the two
self-contained bare blocks to snippets (datatemplate-foreach,
style-as-composition).
- TextElement does not exist -> TextBlockElement (field Content, not Text).
- UsePersistedState -> UsePersisted.
- UseNamedStyle / .Named() do not exist -> named-style fluents
(.AccentButton/.SubtleButton/.TextLink).
- .WithAnimation() is not an element modifier -> .Animate()/.Transition();
AnimationScope.WithAnimation is the ambient scope.
- Fixed dead link [Observable<T>](#) and the brittle "35 token" count.
cheat-sheet:
- ReactorApp.Run has no `preview` parameter -> fullScreen?/configure?.
- ReactorWindow.Open<TComp>() does not exist -> ReactorApp.OpenWindow.
- .Bind(button) does not exist -> Button(command) / .Command(cmd).
packaging:
- Template stopped pinning Microsoft.WindowsAppSDK in #1096; it is now
added by a dotnet-new post-action at latest stable. Two claims updated.
- The default launch profile passes no arguments; named the separate
Devtools profile instead.
Element.cs: the element-record snippet's XML doc comment carried the
same Text("hi") mistake and is rendered verbatim into five guide pages;
corrected at the source, which is why four out-of-scope generated pages
carry the identical one-line change.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Audit of the core-controls guide slice (controls, forms, collections, text-and-media, status-and-info) against today's source. Catalog completeness (controls.md.dt) - The page billed itself as a catalog of "every Reactor control" but listed 46 of the 95 element families registered by ReactorApp.RegisterAllBuiltIns(), and was missing three whole categories. Added Layout & Containers, Navigation, and Shapes & Icons (with snippet-backed thumbnail groups + doc-manifest captures), and completed the existing tables: 46 -> 99 control rows. - `MarkdownTextBlock` was a phantom name in two places; the factory is `Markdown(...)` and it lives in Microsoft.UI.Reactor.Advanced. - Added ControlCatalogCompletenessTests so the claim is checked rather than asserted: deleting a single catalog row now reddens three tests. Phantom APIs corrected - forms: `TextBox(initial:)` (no such parameter), `DisabledFocusable()` -> `.IsDisabledFocusable()`, `.KeyDown` -> `.OnKeyDown`. - text-and-media: `Selectable()` -> `.IsTextSelectionEnabled()`, `RichEditBox(string text = "")` -> `Optional<string>`. - collections: `theme.IsDark` -> `UseIsDarkTheme()`, and a cache example that used `Dictionary<int, Element>` against a string-keyed selector. - status-and-info: the `ProgressBar(...)` factory does not exist at all; the page described it as merely deprecated. Wrong-overload bug - text-and-media's "Do" example for Markdown in a scrolling list used `Memo(ctx => ..., deps)`, which only skips work on a parent re-render and does nothing on a scroll recycle. Replaced with the keyed `Memo(key, factory)` cross-recycle cache and explained the distinction. Dead links - Two links pointed at github.com/microsoft/reactor1 (repo renamed). Snippet promotion - 11 uncompiled blocks moved into the doc apps behind snippet markers, so CI compiles them from now on. Across the five topics: 25 bare blocks -> 14, compiled snippets 56 -> 70. The 14 remaining are factory signature listings and "Don't" anti-patterns that cannot compile standalone; every symbol they name was verified against source. Verification: all five doc apps build Release/x64; `mur docs compile` regenerates with no collateral diff; tier lint clean on all five; Reactor.Tests 13308 passed / 0 failed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cross-session handoff from the "Guide: recipes and tooling" audit; both lines live in index.md.dt, which that session did not own. - VS Extension: src/vs-reactor/README.md line 15 requires Visual Studio 2022 (17.8+) *or* 2026 (18.x). The listing named 2022 only. The "rough experimental" framing is unchanged - still verbatim accurate against that README. - Testing: "snapshot tests" described an ElementDescription.Of golden-file harness that does not exist. Independently reconfirmed here with a positive-controlled scan of src/ + tests/ + tools/ (1887 .cs files): ElementDescription = 0 hits while Assert.IsType = 651 and "abstract record Element" = 35 in the same pass, so the zero is a real absence and not a broken probe. Now "structural assertions". Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…locks to compiled snippets Audited the 7 core-framework templates (58 uncompiled `csharp` blocks). Bare blocks are never compiled by CI, so they had drifted onto APIs that do not exist. Eight were invented outright; all are now corrected, and 39 of the 58 blocks are promoted to snippet-backed code that CI compiles from the doc apps. Phantom APIs removed (each verified against src/ with a positive control, and three caught by the compiler once promoted): - `ctx.UseTheme()` -> `ctx.UseColorScheme()` (advanced). Never implemented; existed only in design specs. The same line had been copied into `src/Reactor/Hooks/UseMemoCells.cs`, which feeds the generated reference page `docs/guide/reference/hooks/UseMemoCells.md` - fixed at the source. - `RenderContext.Current` -> `this RenderContext ctx` extension (hooks). No static `Current` exists anywhere in src/. - `Text(...)` as an element factory -> `TextBlock(...)` (commanding, hooks, components). Proven by compiler: CS0103. Only CellRenderers.Text, DragData.Text and Editors.Text exist, none on Factories. - `Theme.Stroke` -> `Theme.CardStroke`, `Theme.Error` -> `Theme.SystemCritical` (components). `static class Theme` has neither member. - `intl.Get(...)` -> `intl.Message(new MessageKey(ns, key))` (commanding). - `Component<T>().Set(...)` (advanced) - ComponentElement has no `.Set`; rewritten onto a control-backed element, which is the real hazard shape. - `.WithKey(item)` on an unconstrained generic (components) - `WithKey` requires `string` or `TKey : IReactorKeyed`; the render-props example now takes a key selector. Bare blocks 58 -> 19: 6 are `ai:lock` (only single-symbol `Text` -> `TextBlock` corrections applied, lock structure preserved) and 13 are genuine fragments/anti-examples that cannot compile standalone; every symbol they name was verified to exist. Verification: all 6 modified doc apps build clean in Release/x64; docs tier lint reports 0 errors across all 7 topics; regenerated guide diff is confined to the audited topics. The detection loop was validated non-vacuously by injecting a bad symbol and confirming the build reddens before restoring. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Line 3 advertised Visual Studio 2022 only while line 15 (the requirements line) correctly stated 2022 (17.8+) or 2026 (18.x). The guide page vs-extension.md reconciles against this README, so the inconsistency was propagating ambiguity into the docset. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
Audit of the rich-controls slice (dialogs-and-flyouts, data-system,
charting, win2d-canvas, docking). Promotes 11 uncompiled blocks to
CI-enforced snippets and corrects the symbols in the rest.
Reader-visible breakage
- win2d-canvas: the three `<!-- ref:Win2D*Canvas -->` markers never
resolved (reference generation is gated to the `hooks` category and
reads core Reactor.xml, so Advanced element types can never route).
The unresolved markers leaked into docs/guide as raw HTML comments,
rendering as three empty gaps. Replaced with same-page section links.
Clears all three REACTOR_DOC_REFMARKER_001 warnings.
Wrong API that would not compile
- charting: `using static Microsoft.UI.Reactor.Charting.D3.D3Charts` ->
`...Charting.D3Charts`; `D3Text`/`D3TextRight`/`D3TextCenter` do not
exist (real names `Text`/`TextRight`/`TextCenter`); the scale types are
in `Charting.D3`, not members of `D3Charts`.
- charting: `DashStyle.Dot` / `DashStyle.Dash` do not exist.
- charting: `.Margin(t, r, b, l)` documented CSS order; the real
signature is `Margin(left, top, right, bottom)`, defaults 40/20/20/30.
- charting D3 sample: `.Domain()`/`.Range()` are properties not methods,
`scale(v)` is not invocable (use `.Map(v)`), `D3Axes` needs six args
and returns `Element[]` (must be spread), and `.Stroke`/`.Fill` on D3
shapes take a `Brush`, not a string.
- charting live-feed sample: `await` inside a non-async `UseEffect`
lambda, plus an undeclared token.
- charting type-switch sample: `ComboBox` reports an index, not a string.
- dialogs-and-flyouts: `.WithFocusTrap(trap)` does not exist (real:
`.FocusTrap(trap)`) and `UseFocusTrap()` requires an `isActive` flag.
- dialogs-and-flyouts: `UseElementRef`/`UseFocusTrap` are extension
methods on Component, so a bare call does not resolve.
- data-system: `AutoColumns<T>` takes `overrides`, not `columnOverrides`
(that name belongs to the registry-based `DataGrid` overload);
`FilterOperator` has 13 members, not 10; `GetPageAsync` takes the
cancellation token as a separate parameter; lifted selection state must
be typed `IReadOnlySet<RowKey>` for the setter to bind.
Consistency
- charting, data-system, docking, and text-and-media told readers to add
an unversioned `<PackageReference Include="Microsoft.UI.Reactor.Advanced" />`,
which does not restore without central package management. All four now
use `Version="{{reactorVersion}}"`, matching getting-started.
Verified: all five doc apps build clean in Release/x64; tier lint reports
0 errors; every relative link and intra-page anchor in the slice resolves;
docs/guide regenerates deterministically with no collateral churn. The
win2d examples were checked against REACTOR_WIN2D_001 — the shared-device
example opts in, and the canvas-own-device example is correctly exempt.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Audit of the App architecture / interop slice of the user guide. Every bare (uncompiled) ```csharp block in scope was checked against the real API and either promoted to a CI-compiled snippet= block or corrected symbol by symbol. In-scope templates go from 30 bare / 39 compiled to 6 bare / 82 compiled; windows.md.dt goes from 16 bare / 3 compiled to 0 bare / 20 compiled. Defects found and fixed (each was uncompilable or factually wrong): - migration/050-optional-t: told authors to write `Optional.Of(value)`. There is no non-generic `Optional` helper — every real call site in the repo uses `Optional<T>.Of(...)`. Nine occurrences corrected; a note now explains the generic spelling. Verified: injecting the old spelling into a compiled snippet yields CS0305. - wpf-interop: "mount a ReactorHostControl and assign it a ComponentType". ReactorHostControl has no ComponentType — that property belongs to the WinForms XamlIslandControl. Replaced with the real ComponentFactory / Mount(...) shape, now compiled. - winforms-interop: the content-factory snippet's key lines were commented out, hiding a call to `host.SetComponent<T>()`, which does not exist. Rewritten as live code so CI enforces it. - accessibility: `var label = UseElementRef<FrameworkElement>();` does not compile inside a component — it is an extension on Component/RenderContext and needs `this.`. Corrected and promoted. - windowing-advanced: the SetWindowRgn recipe wrapped the HRGN in `using` and passed DangerousGetHandle(). SetWindowRgn transfers ownership to the system on success, so that frees GDI memory the compositor still reads. Rewritten to delete the region only when the call fails. - windows: "TaskbarItem groups all taskbar features" was wrong — JumpList is a taskbar surface that is not on TaskbarItem. JumpList / JumpListItem / LaunchKind / ReactorApp.Run(Action<ReactorAppContext>) had zero guide coverage; added a Jump list section with a compiled snippet and the argument-validation caveats. - persistence: "will eventually trigger an analyzer warning" — the analyzer shipped. Now names REACTOR_PERSIST_001 and its code fix. - windowing-advanced: pointed at a `samples/apps/hud-overlay/` that was never added; retargeted at the shipping window-styles and tool-palette samples. Structure: - docs/guide/migrations/054-windowing-evolution.md was an orphan: it lived in `migrations/` (plural) with no .md.dt template, so `mur docs compile` could neither generate nor keep it in sync. Given a template under `migration/` (singular, matching 050), its examples promoted to compiled snippets, and the duplicate directory removed. Both migration notes are now linked from windows.md. - New doc apps for windowing-advanced and wpf-interop, which previously declared an `app:` that did not exist and had never compiled a line. - Advanced Windowing was missing from the index; added under App architecture. Verification: all touched doc apps build clean in Release/x64; `mur docs compile` is idempotent (zero drift on re-run); all 71 in-scope snippet references resolve; zero broken relative links. The build loop and the link and snippet checkers were each validated with a deliberate mutation or positive control before their results were trusted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…od track Audit of the 16 Under-the-hood guide templates for drift against the current runtime. Templates are the source of truth; docs/guide/** is regenerated via `mur docs compile`. Structural fix (preferred): promote hand-written code to `snippet="source:<path>#<region>"` so the doc is pinned to real source and cannot drift again. - reconciliation, architecture-overview: the hand-written `Reconcile` body matched no live code (it showed an `Update`/`UnmountAndReturnNull` switch). Both now inline `Reconciler.cs#reconciler-entry`. - element-pool: the "why interactive controls are safe" section embedded `RenderContext.cs#ui-thread-invariant`, an unrelated snippet. Now inlines a new `Element.cs#click-trampoline` region (markers only). Corrected mechanisms (symbols existed; the explanation did not match): - Per-element state is `ReactorAttached.StateProperty` -> `ReactorState`, not `sender.Tag`, and `GetElementTag` is not generic. Tagging is gated by `NeedsTag`. (reconciliation, element-pool, control-reconciler-protocol) - `UseState` setter identity is stable because the delegate is cached on the hook cell (`ValueHookState<T>.Setter`), not because a captured index never changes; the closure holds the cell, not the index. (hooks-internals, reactivity-model, effects-scheduling) - `Component<TProps>.ShouldUpdate` already compares props structurally; "props always re-render, equality is on the roadmap" was wrong. (reactivity-model) - Echo suppression is the documented hybrid: value-diff arm (`PendingEchoMatch` / `ArmExpectedEcho`) plus the retained `ChangeEchoSuppressor` counter fallback and the non-consuming `EchoSuppressScopeDepth` scope. (threading-and-dispatch, control-reconciler-protocol) - Custom containers register through `ControlRegistry` / `RegisterHandler`; `RegisterType` takes no `ReconcileChildren` callback. (reconciliation) - MCP tool dispatch runs inline on the transport thread; the UI hop is opt-in per handler via `server.OnDispatcher<T>`. The tool registry, descriptor and `SchemaNode` are internal, so custom tool registration is a framework-contributor path, not a host-app one. (devtools-internals) - `Reactor.Analyzers` targets netstandard2.0, cannot reference `Reactor.Cli`, and every id needs an `AnalyzerReleases.Unshipped.md` row (RS2008) - previously it named Shipped.md as optional. `REACTOR_DOC_001` ships from `Reactor.Analyzers.Internal`, and analyzer tests live in `tests/Reactor.Tests/AnalyzerTests/`. (analyzer-architecture) Corrected symbols in bare (uncompiled) blocks: - No `Text(...)` element factory and no `TextElement` record exist; the DSL factory is `TextBlock(...)` -> `TextBlockElement`. The three indexed `Text(` entries are a DataGrid cell renderer, a DataGrid editor and `DragData.Text`, none of which is an element factory. (architecture-overview, hooks-internals, modifier-system, animation-pipeline, source-mapping) - The focus-trap modal used `Layer(...)`, `Dialog(...)` and a parameterless `.OnMount(...)`; none exist. Rewritten on the controlled `ContentDialog(...)` element with `IsOpen`, matching REACTOR_DIALOG_001. (focus-and-input-internals) - `Button`/`TextBox`/`ToggleSwitch` are in `ElementPool.PoolableTypes`; architecture-overview claimed interactive controls are not pooled. - 14 ETW keywords, not 7 (spec 044 + 049 added seven). (perf-instrumentation) Verification: `mur docs compile` is idempotent and touches only these topics; all relative links resolve; `Reactor.DocPipeline.Tests` 331/331; tier lint reports 0 errors (the one `winui-ref` warning is pre-existing and identical on untouched topics). The promoted snippets were mutation-checked - breaking a symbol inside `click-trampoline` fails the `src/Reactor` build at that line, then restored. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reactor has no core `Text(...)` element factory. All four `Text(` members
in src/ belong to other types — `CellRenderers.Text` and `Editors.Text`
(both `Func<object, Element>`, for DataGrid), `DragData.Text`, and
`D3Charts.Text(x, y, text, …)` (positional, canvas coordinates). None of
them is `Text(string) -> Element`. Verified with a positive control:
`public static TextBlockElement TextBlock(string content)` (Dsl.cs:60) is
found by the same probe shape, and `Factories` declares no `Text` member
across all nine of its partial files.
- docking/App.cs: the fake editor panes in the `two-pane` and `tab-group`
snippets rendered `Render() => Text("Hello")` as example source inside
string literals, so the compiler could not see them. Corrected to
`TextBlock(...)`. This is the uncompiled-rot class hiding inside a
compiled file.
- charting.md.dt: prose introduced earlier in this audit claimed `Text`
"collides with the core `Text(...)` factory". That was wrong, and wrong
in the same direction as the bug above — the three bare `Text(...)`
lines in skills/reactor.api.txt sit under `## Public types`, where
entries are grouped beneath their declaring type, not under
`## Factories`. Replaced with the verifiable distinction: `D3Charts.Text`
is positional and unrelated to `TextBlock(...)`, and no core `Text(...)`
element factory exists. The page now inoculates against the phantom
instead of propagating it.
Verified: docking doc app builds clean Release/x64; docs recompile with no
REFMARKER warnings; no bare `Text(` remains in the six generated pages in
this slice except the charting prose that names it to warn against it.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The guide audit found Text(...) used as an element factory across four
templates. It also reached product source: 12 XML doc comments cite
Text("...") in element position, plus three phantom modifiers.
None of these APIs exist:
Text(...) as an element factory. The three real Text(...) overloads
return Func<object,Element> (DataGrid cell renderer), DragData, and
a DataGrid editor builder -- none returns an Element. The correct
factory is TextBlock(...). Element.cs's own implicit string operator
calls Factories.TextBlock(text) while its doc comment two lines
above said Text("Hello").
WithOpacityTransition() -> OpacityTransition()
WithThemeTransitions(...) -> WithTransitions(...)
WithImplicitTransition -> OpacityTransition
These ship in Reactor.xml, so they surface as IntelliSense tooltips for
every consumer of the NuGet package. They do not affect docs/guide: a
full docs compile after this change produces zero diff there, because
the generated reference pages do not render these example blocks.
Element.cs:33 is deliberately left alone -- it is already fixed on
azchohfi-potential-sniffle (onboarding slice); the two combine to zero.
Verified: src/Reactor builds Release/x64 with 0 warnings and 0 errors;
docs compile succeeds with no docs/guide diff; a recursive 697-file
re-scan finds exactly 1 remaining phantom, the one intentionally
skipped. An earlier scan of this same tree reported "none" -- that was a
broken probe, since PowerShell's src\**\*.cs globs only two levels and
never reached src\Reactor\Core\.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
TeachingTipPage's SourceCode string showed:
var target = UseElementRef<FrameworkElement>();
UseElementRef is an extension method on Component, so that line does not
compile as written. The page's own working code on line 18 uses
this.UseElementRef<FrameworkElement>() -- the file contradicted its own
displayed sample.
This is the same rot class the guide audit is chasing: example code
living inside a string literal, invisible to the compiler even though
the surrounding file is compiled and shipped.
Scope checked: a scan of all 336 sample .cs files found this as the only
receiverless call of UseElementRef / UseFocusTrap / UseValidationContext
/ UseElementFocus.
Verified: ReactorGallery builds clean; tools/Reactor.SearchIndex
regenerates reactor-search-index.json byte-identical (93 controls, 0
skipped), so the CI byte-compare gate is unaffected -- this snippet is
not the page's first sample.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
…f-truth
`Optional.Of(...)` does not exist. `Of` is declared only on the generic
struct (`Optional.cs:46`, `public static Optional<T> Of(T value)`), and
there is no non-generic `Optional` type anywhere in src/ - so the bare
form cannot bind. Verified by mutation: injecting `Optional.Of("")` into
`src/Reactor/Core/Optional.cs` fails the build with
error CS0305: Using the generic type 'Optional<T>' requires 1 type arguments
The only valid spelling is `Optional<Brush>.Of(null)`.
Two fixes:
- `extending-reactor-controls.md.dt:350` - the occurrence in my slice.
Routed from the app-architecture session, which found the same defect
x9 in `migration/050-optional-t.md.dt`. Note it sits in an inline code
span inside an `ai:caveat` block, not a fenced block, so no amount of
snippet promotion would ever have caught it. Symbol corrected only;
wording and intent preserved.
- `src/Reactor/Core/Optional.cs:57` - the XML doc on the implicit
conversion operator carries the identical wrong spelling, and it is
almost certainly where the four affected templates copied it from.
`GenerateDocumentationFile=true`, so this text ships in the public
reference. Fixing the origin stops it re-propagating into the next
doc pass. Comment-only; `src/Reactor` builds clean.
Left alone deliberately: 7 further `Optional.Of(` occurrences in src/
are all `//` implementation comments or an analyzer message string, none
compiled and none public-API reference text.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…embedded APIs `Optional.Of(...)` does not exist. `Optional<T>` is a generic struct whose `Of` is a static member on the struct itself (src/Reactor/Core/Optional.cs:46), so the only valid spelling is `Optional<double>.Of(5.0)`. There is no non-generic `Optional` helper anywhere in src/ (verified exhaustively across all 732 .cs files, with a positive control). Fixed 4 occurrences in advanced.md.dt prose: - the authority paragraph (`Optional.Of(value)`) - the snap-back flow sentence (`Optional.Of(5.0)`) - the ClearValue paragraph (`Optional.Of<Brush?>(brush)` - type argument was also on the wrong side of the dot) - the null-vs-Unset sentence (`Optional.Of(null)`) Added an explicit note naming both wrong spellings as counterexamples, so the correct placement of the type argument is stated rather than implied. These were invisible to the previous audit because they live in inline code spans in prose, not in fenced blocks - nothing compiles a Markdown inline span. Swept all 7 core-framework templates for the same class of defect: 272 prose-embedded API spans (164 unique), checked by identifier existence and then by shape for `Type.Member(...)` and `.Modifier(...)` forms. Everything else verified real, including `ReactorApp.RegisterAllBuiltIns`, `ControlRegistry.Register`/`RegisterDecorator`, `IsDisabledFocusable`, `ToolTipPlacement`/`ToolTipPlacementTarget` and `WithToolTip`. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Audit of the 41 uncompiled `csharp` blocks in layout, flex-layout,
styling, animation, input-and-gestures, and theming-tokens. Bare blocks
are never compiled by CI, so they had drifted from the current API.
Promoted 10 self-contained blocks into their doc apps as `snippet=`
blocks so CI compiles them from now on (56 -> 66 compiled blocks;
41 -> 31 bare). input-and-gestures went from 5 compiled / 15 bare to
10 / 10.
Symbols that do not exist:
- `FlexPanel(...)` as a DSL factory. `FlexPanel` is the WinUI control;
the factories are `Flex` / `FlexRow` / `FlexColumn`.
- `.FlexDirection(...)` on any element. Direction is the `FlexElement`
init property (`with { Direction = ... }`) or the
`Flex(FlexDirection, ...)` factory overload. (`.Direction(...)` does
exist, but only as `ExpanderElement.Direction(ExpandDirection)` --
a different element and a different enum.)
- `Text("...")` as an element factory -> `TextBlock(...)`. The four real
`Text(...)` methods are the grid cell renderer/editor builders,
`DragData.Text(string)`, and `D3Charts.Text(x, y, text, ...)`; none
takes a lone string and returns an Element.
- `VStackElement` -> `StackElement` (what `VStack`/`HStack` return).
- `Focusable(...)` -> `.IsTabStop(true)` (ElementExtensions.cs:2764).
- `.On<Event>Handled(...)` "handled-too" overload -- no public extension
in src is named `*Handled*`; documented the
`AddHandler(..., handledEventsToo: true)` escape hatch instead.
- `A11Y_KEYBOARD_001` -- AccessibilityScanner only emits A11Y_001..008 and
has no keyboard rule; REACTOR_A11Y_004 is the only guard.
- `Command.AccessKeyModifiers` -> `Command.Accelerator`.
- `.WithImplicitTransition(...)` -> `.OpacityTransition()` and friends.
Its only occurrence in src is a stale doc comment.
- `.WithModifier(...)` (0 hits anywhere) and a headless `Mount(...)`
returning a chainable tree. `Mount` does exist -- as reconciler, host
and IElementHandler API -- but every overload either returns `void` or
a `UIElement`, which a headless xUnit test cannot construct. Rewrote
to the real `AccessibilityScanner.Scan(tree)` pattern used by
tests/Reactor.Tests.
- `Severity.Success` / `.Critical` -> `InfoBarSeverity`. The single
public `Severity` enum is Info/Warning/Error (validation).
Probe method: every verdict above was re-confirmed with a bare-name
search (never `Name\(`), because the API index renders generic members
as `T.Name<T>(...)` -- 184 of the 643 lines in `## Modifiers` use that
shape and are invisible to a paren-anchored grep. Each negative is
paired with a positive control sharing the same shape: `WithBorder`,
`IsTabStop` and `OpacityTransition` are generic extension modifiers and
all return non-zero on the identical probe.
That method also corrected one of my own findings: `Border("string")` is
*valid* -- `Element` defines an implicit `string` -> `TextBlockElement`
conversion (Element.cs:342) -- so those examples are left unchanged.
Prose sweep: fenced-block promotion cannot reach API names in headings
and inline code spans, so those were checked separately. Extracted all
375 prose-embedded API references outside fences across the six
templates (131 distinct identifiers) and verified each. One real defect:
the "Forgetting `Focusable(true)` on a clickable Border" heading named a
modifier that does not exist and contradicted its own body, which
already said `.IsTabStop(true)`. Everything else resolved: locals
(`setOffset`, `updateCount`), WinUI APIs (`AddHandler`, `Measure`), CSS
notation (`min(N, min-content)`), tuple-element names (`RequestFocus`
from `UseElementFocus`), or deliberate negatives.
Token catalog: the "full catalog" was missing `Theme.SystemSolidAttention`
and counted 35 instead of 36. The swatch-grid doc app claimed to render
"every named Theme.* token" while omitting 6 Signal tokens; both fixed.
Also documented `TryGetSafeLocalFiles` and the REACTOR_INPUT_002 analyzer,
which had shipped but were absent from the drag-and-drop section; the
`ListView<T> : IReactorKeyed` constraint that promotion surfaced; and the
`.OnDoubleTap(Action)` / `.OnDoubleTap(Action<Point>)` convenience
overloads, which the reference table omitted while the compiled snippet
used them.
All 6 doc apps build Release/x64; tier lint 0 errors; 0 broken links.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…, topic->app binding Four defects in `mur docs` generation, each of which failed silently or produced a false signal. 1. Reference pages dropped all but one overload (REFGEN_002, 18 sites). Pages are keyed by short name, so every overload of a method routed to the same file and the first writer won; the rest never appeared anywhere in the docset. `UseEffect` documented 1 of its 8 overloads -- the `Func<Action>` cleanup form, the only way to register a self-tearing-down effect, was absent entirely. A page now represents every member routed to it, one `##` section each (signature, summary, parameters, returns, discussion), ordered from the cref rather than from XML declaration order so output is stable. One page per short name is preserved, so inbound links and `<!-- ref:Name -->` markers keep working. Signatures come from a new `CrefSignature` that renders a cref as readable C# and slugs it to a GitHub-compatible anchor. REFGEN_002 is retained but narrowed to its real meaning: a page name claimed by two unrelated declaring scopes. Those members are now rendered too, and still reported. 2. Member crefs failed to resolve even when the declaring type had a page (REFGEN_001, 12 sites). A positional record's properties are compiler-generated and never appear in the XML doc file, so `MutationOptions<,>.OnSuccess` and friends had nothing to resolve against. `CrefResolver` now falls back to the declaring type's page, keyed on the full type cref so a same-named type in another namespace cannot capture it. 3. `ExpandMarkers` emitted an unresolvable `<!-- ref:X -->` verbatim into published Markdown. The marker is an HTML comment, so the reader saw an empty gap mid-sentence with nothing indicating a cross-reference had gone missing -- three of them in win2d-canvas.md. It now degrades to inline code, matching what `CrefResolver.Rewrite` already does for an unresolvable cref, and still reports REFMARKER_001. 4. `docs check-tier --topic` reported fabricated TIER_003 errors. App discovery filtered by directory name against the topic id, but the binding is the template's `app:` field, and the two differ for 13 shipping topics (`async-resources` -> `async-resources-cookbook`, every `recipes/<x>` -> `recipe-<x>`). Those topics discovered no app at all, so every `snippet=` failed to resolve and the documented fast inner loop fired on correct pages. Discovery is now driven by `app:` plus the app ids named in `snippet=` refs; `compile --topic` had the same bug. Regenerated docs/guide/reference/** accordingly; two consecutive compiles produce byte-identical output across all 190 generated pages. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The "Minimal Project Setup" block on the docset landing page is the first
thing a new user copies, and nothing validated it. It is a fenced xml
block, so no compiler sees it, and it sits inside ai:lock markers, which
authoring passes skip by design. It shipped telling readers to consume
Reactor via <ProjectReference Include="..\Reactor\Reactor.csproj" /> -- a
path that does not exist outside this repo -- so the documented first-run
was broken for every external reader.
Adds MinimalCsprojDocTests, which extracts the block from index.md.dt and
asserts it: parses as XML; references the Microsoft.UI.Reactor package and
carries no ProjectReference; pins the {{reactorVersion}} token rather than
a literal that goes stale at the next release; keeps the RuntimeIdentifier
auto-resolve line; declares the required WinUI properties; agrees with the
scaffolded dotnet-new template on the package id; and produces a generated
index.md with a real version and no leaked token.
Non-vacuous by natural mutation, not a synthetic one. Run against the
pre-fix template the suite reports 4 failed / 5 passed, and the failure
names the historical defect exactly:
'Minimal Project Setup' must not use ProjectReference -- that path does
not exist for a reader outside this repo. Found: ..\Reactor\Reactor.csproj
With the fix merged: 9/9 pass, and the full project is 340/340.
The 5 that pass in both states (extraction, well-formedness, required
properties, scaffold cross-check) are what show the harness itself works,
so the 4 failures are a measurement rather than a broken probe.
A build fixture was considered and rejected: restoring Microsoft.UI.Reactor
at the version in Directory.Build.props couples CI to a package that is not
yet published at release time. This runs offline with no feed.
RuntimeIdentifier is asserted because it is load-bearing and non-obvious --
adding <Platforms> instead does not fix the architecture error; that was
measured against a real build before the fix shipped.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
AGENTS.md is the file that orients every future contributor and agent session, so a stale claim here misdirects work before it starts. Two were wrong; verified against source with positive controls. 1. "Register it in RegisterV1BuiltInHandlers" -- that bootstrap no longer exists. No such method is defined anywhere in src/ or tests/; the only surviving mention is a stale comment. Reconciler.cs:264 records why: "Spec 048 SS3.4 -- no more bootstrap. Built-in handlers register themselves lazily on first factory call (per-control Reg<>/RegDecorator<> cctor latch in Dsl.cs), or callers register explicitly via ControlRegistry.Register<,>." Also notes the spec-058 [GenerateReactorWrapper] static-cctor path, and that `new MyElement(...)` alone registers nothing (Reconciler.Mount.cs:268). 2. The Element pooling paragraph was wrong twice over. PoolableWireFlags has zero hits across src/ and tests/. And the CWT in ElementPool is not event-wiring bookkeeping: ElementPool.cs:20 declares ConditionalWeakTable<UIElement, object> _compositorTainted, tracking elements that had GetElementVisual() called so they are EXCLUDED from pooling (they permanently lose the XAML implicit-transition APIs). ElementPool.cs:10 also states V1 pools only non-interactive controls, so there is no double-subscribe problem to solve. Probe note: the zeros above are measurements, not silent misses. The same scan over the same 1847-file set found ConditionalWeakTable< in 30 files, and re-verified 14 other AGENTS.md symbols (PendingEchoMatch, ArmExpectedEcho, ShouldSuppressEcho, ChangeEchoSuppressor, WriteSuppressed, ReactorAttached, StateProperty, ModifierEventHandlerState, ControlEventStateBox, ReactorState, ControlDescriptor, IElementHandler, RuleRegistry, valueDiffEcho) as present. The rest of the file checks out. Found by the under-the-hood guide-audit session; both claims had already been propagated into this audit's own session briefs before being caught. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
…ments Optional.Of(...) does not exist. Of is declared only on the generic struct (src/Reactor/Core/Optional.cs:46: `public static Optional<T> Of(T value)`), so the sole valid spelling is Optional<T>.Of(...). Injecting the bare form yields CS0305 "Using the generic type 'Optional<T>' requires 1 type arguments". This is the fourth phantom traced to shipped XML doc comments during the guide audit, after Text(...), UseTheme(), and the With*Transition family. The templates were quoting the source: extending-reactor-controls.md.dt tracks the Optional.cs doc comment almost word for word. Fixing the copies without the original just lets it re-seed. Fixed 15 of the 16 shipped (///) occurrences: src/Reactor/Core/Element.cs x11 Optional<int>.Of(-1) src/Reactor.Analyzers/OptionalSentinelAnalyzer.cs x3 src/Reactor.Analyzers/NoOpModifierAnalyzer.cs x1 Also corrected OptionalSentinelAnalyzer's DiagnosticDescriptor Description, which is shown to users in the IDE and already used the correct Optional<T>.Of / Optional<T>.Unset spelling two lines either side of the wrong one. Left alone by design: 6 `//` implementation comments (never shipped) and one `//` comment in NoOpModifierAnalyzerTests. Optional.cs:57 is the 16th and is fixed on azchohfi-guide-under-the-hood-audit; the two branches combine to zero, as with the Element.cs Text( split. Types verified, not assumed: every Element.cs site is a `Optional<int> SelectedIndex`/`SelectedPageIndex` property, and the CalendarDatePicker site is Optional<DateTimeOffset?>. Verified: src/Reactor and src/Reactor.Analyzers both build Release with 0 warnings, 0 errors; docs compile produces no docs/guide diff (these comments do not feed generated pages -- they ship via GenerateDocumentationFile in Reactor.xml as IntelliSense); the 70 OptionalSentinel + NoOpModifier tests pass, and the filter was confirmed to match non-zero so the pass is a measurement. Residual scan: 0 shipped occurrences remain on this branch besides Optional.cs:57, with a positive control showing 22 correct generic forms now present. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
Reactor validates executable code (the compiler plus REACTOR_DYM_003 /
REACTOR_DYM_005) and validates snippet-backed doc blocks (they compile as
part of the doc apps). It validates nothing else. Four documentation
surfaces are exempt from every existing check, and all four have shipped a
phantom API: bare ```csharp template blocks, example source carried in
string literals inside doc apps, gallery SourceCode strings, and /// doc
comments in src/ — the last of which ship in Reactor.xml as IntelliSense
and have been observed re-seeding the guide templates that quote them.
Mechanism note — this is NOT a `mur check` rule
-----------------------------------------------
`mur check` is a build-diagnostic post-processor: CheckCommand runs a
build, parses MSBuild diagnostics, and only invokes IRulePattern.TryMatch
per parsed diagnostic (CheckCommand.cs:161). On a clean build it prints
"ok" and no rule runs at all (CheckCommand.cs:198). IRulePattern requires a
Diagnostic, a SemanticModel and a CSharpCompilation.
Every target surface compiles clean, by construction:
- docs/_pipeline/apps/docking/App.cs carried `Text("Hello")` inside a
string literal and built green in Release/x64 for months;
- .md.dt templates are not C# and are in no compilation;
- /// comments are comments.
A rule under Check/Rules/ would therefore be structurally incapable of
firing on any of them. It is implemented as a docs-pipeline lint instead,
alongside the existing REACTOR_DOC_XLINK_001 / _TIER_W001 / _REFMARKER_001
family, which is why there is no AnalyzerReleases row (not a Roslyn
analyzer) and no CLI/analyzer parity mirror (one matcher, two consumers).
Design
------
PhantomSymbolLint is a pure matcher over (path, text, surface); the docs
compile and the test gate both call it, so they cannot drift. Phantoms live
in one table — add an entry and every consumer picks it up. Seeded with the
six needed to cover the audit's real defects: Text, UseTheme,
WithOpacityTransition, WithThemeTransitions, WithImplicitTransition, and
Optional.Of.
Surface gates keep it off executable code: Markdown lints only inside
fences, C# lints only /// lines, and <see cref="..."/> is masked because
crefs are compiler-validated (CS1574) and so are not an unvalidated
surface. A scoped `<!-- phantom:skip "Name" -->` marker — same spelling
convention as the pipeline's existing `<!-- xlink:skip -->`, and valid in
both Markdown and XML doc comments — covers the case where a doc must name
a phantom in order to warn against it.
False-positive classes found by the spike, each now a test
---------------------------------------------------------
- Case-insensitive matching flags the English phrase "…text (for…"
(AccessibilityScanner.cs:94). Patterns are case-sensitive; C# is.
- A lookbehind excluding '>' silently misses `<c>Optional.Of(`, the form
that phantom almost always takes — a self-inflicted zero.
- Receiver-qualified members named Text are all real (CellRenderers.Text,
Editors.Text, DragData.Text, D3Charts.Text) and must not fire.
- D3Charts.Text is positional, so even unqualified under `using static` its
first argument is numeric; the string-literal-first-arg rule excludes it.
- Example code inside a C# string literal escapes its quotes, so the
opening quote must be matched as `"`, `\"` and `""`. The first
implementation matched only the plain form and was blind to the exact
surface the rule was written for; the docking fixture caught it.
Two-direction evidence
----------------------
Fires on real historical defects, not synthetic fixtures: the pre-6e80de46
docking string literals, layout.md.dt's ai:lock'd fences (×3), and the
shipped doc comments in Element.cs, ElementExtensions.cs, UseMemoCells.cs,
Animation.cs and Optional.cs.
Silent on the current tree where it should be: zero findings across all six
pages of this branch's slice, including charting.md.dt, whose prose names
the phantom in order to inoculate the reader.
Current triage: 25 template findings across 15 templates (layout ×3 is the
cited defect; the other 22 are new finds owned by other slices) and 35 ///
findings across 11 files in src/. Severity is Warning on roll-out, mirroring
CrossLinkLint's staging, so the docset does not break while those clear.
SrcDocComments_ContainOnlyTheKnownPhantomBacklog pins the src/ backlog as an
explicit per-file allow-list that fails in both directions: new rot fails,
and clearing a known occurrence also fails, so the list shrinks deliberately
rather than drifting.
Verified: dotnet test tests/Reactor.Tests --filter
"FullyQualifiedName~PhantomSymbolLintTests" → 26/26 passed;
Reactor.DocPipeline.Tests → 331/331 passed; `mur docs compile` produces no
docs/guide churn (the lint is read-only).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…-HINT Follow-on to 974e5f5 (AGENTS.md). The under-the-hood audit session corrected its own "PoolableWireFlags has 0 hits repo-wide" claim -- its scan was src/**/*.cs, so it could not see .md files. A full-corpus sweep (3061 files, all types) found the remaining live instances. Fixed: - .github/skills/pr-review/dimensions/correctness.md -- the Element pooling bullet was built entirely on the non-existent PoolableWireFlags type and told reviewers to flag unguarded event wiring on poolable controls. V1 pools only non-interactive controls, so that hazard does not exist. Rewritten around the real one: ElementPool's CWT is ConditionalWeakTable<UIElement, object> _compositorTainted, which EXCLUDES elements that had GetElementVisual() called (they permanently lose the XAML implicit-transition APIs). Kept the two sub-points that were correct. - .github/skills/pr-review/dimensions/alternative-solution.md -- said a new ControlDescriptor is "registered in RegisterV1BuiltInHandlers". That bootstrap was deleted by spec-048 SS3.4 for trimming; registration is lazy via the factory's Reg<>.Done touch, explicit via ControlRegistry.Register, bulk via ReactorApp.RegisterAllBuiltIns(). - src/Reactor/Core/Reconciler.Mount.cs AI-HINT -- described "Mount() is a big switch over all Element subtypes -> MountXxx() methods", the exact legacy model spec 048 removed. No MountXxx() methods remain. This comment sits at the top of the file anyone greps for the extension point. Deliberately NOT changed, having checked each rather than pattern-matching: - src/Reactor/Hosting/ReactorApp.BuiltIns.cs:5 and two test files name RegisterV1BuiltInHandlers *correctly* -- they say it "was removed"/"was deleted", explaining why today's design exists. Editing them would have broken accurate text. - docs/specs/** (~48 mentions) and docs/specs/tasks/039-...:304 (PoolableWireFlags) are historical spec and audit records, not guidance. Verified: src/Reactor builds Release/x64 with 0 warnings, 0 errors. The corpus sweep used a positive control (22 files mention ReactorAttached) so the counts are measurements, not silent misses -- the failure that produced the original "0 repo-wide" claim. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
The gate landed in 8ab509c asserted exact equality against the known src/ backlog. That fails in both directions by design — but ~32 of the 36 occurrences are already fixed on branches queued for the same PR, so the moment they merge the backlog shrinks and the gate goes red on whoever merges, with no context. Anti-drift bookkeeping is not worth a red build landing on an innocent party. Ceiling semantics instead: a new file, a new phantom in a known file, or an increased count fails; a decrease passes and is reported as a stale entry to trim. The property that matters — the count can never silently grow — is preserved, and the gate now survives the merge without coordination. Exact-match pre-shrunk to the predicted post-integration state was the other option offered. Rejected: those commits are not visible from this branch, so the expected values would be transcribed from a description rather than measured. A wrong transcription reintroduces exactly the red-at-merge failure, except for a reason nobody can diagnose — and the test would be red on this branch today, which means committing a knowingly-failing test. Also keyed per (repo-relative path, phantom) rather than per file. A per-file total would let five fixed Text occurrences mask five newly introduced Optional.Of ones in the same file and still pass — a blind spot in the original that only showed up on re-reading. Each phantom class is now bounded independently, and the budget carries the owning branch per entry. Mutation-verified in all four directions, with a guard asserting the mutation actually applied — the first attempt silently changed nothing (dictionary entries end in ',' not ';') and all four cases passed, including the two that must fail: budget raised above actual -> PASS (simulates the merge landing) budget lowered below actual -> FAIL "GREW … = 6 (budget 3)" budget entry removed -> FAIL "NEW … (no budget entry)" current tree -> PASS Verified: PhantomSymbolLintTests 26/26; Reactor.DocPipeline.Tests 331/331. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follow-up to a38a6b4. That sweep used a probe requiring a quote after the paren (`///.*[^A-Za-z]Text\("`), so it could not see a call passing a variable or an ellipsis. Two shipped doc comments survived it: ElementExtensions.cs:2941 <example>Text(statusMessage).LiveRegion(...)</example> Reactor.Analyzers/UnusedGridTrackAnalyzer.cs:46 "a known Microsoft.UI.Reactor.Factories factory (Text(...), Button(...))" The second is the more pointed one: an analyzer that reasons about which calls are Factories factories was itself naming a non-factory as the example. Its behaviour is correct -- IsReactorFactory resolves semantically via ContainingType == "Microsoft.UI.Reactor.Factories", not a name list -- so this is prose only, verified before editing rather than assumed. Found by the REACTOR_DOC_PHANTOM_001 lint authored on the rich-controls branch, whose matcher does not assume a string-literal argument. Exactly the argument for a standing rule over a one-off grep: my probe was shape-blind in a way I could not see from inside it. Occurrence counts reconciled while here: main carries 16 Text( occurrences in /// comments, not the 12 previously reported -- Element.cs:340 holds two on a single line (`VStack(Text("Hello"), Text("World"))`), so counting edits undercounts occurrences. Residual sweep on this branch, broadened patterns: Text( = 0, UseTheme( = 0, With*Transition = 0, Optional.Of( = 1 (Optional.cs:57, deliberately left to azchohfi-guide-under-the-hood-audit). Positive control: 35 /// comments cite TextBlock(, so the zeros are measurements. Verified: src/Reactor and src/Reactor.Analyzers both build Release with 0 warnings, 0 errors; docs compile produces no docs/guide diff. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 11263efd-3cf9-4dc2-b8ed-6b6380193e9a
Round 24: two suppressed findings, both right. The inline-span parser only understood one backtick CommonMark delimits a span with a RUN of backticks and closes it on a run of equal length. Toggling one tick at a time mis-parses ``Optional.Of(`x`)``: the span is read as the empty string between the first two ticks, and the phantom inside is masked away as prose. Now matches runs of arbitrary length, with the multi-backtick case and an unterminated run pinned by tests. A doc taught a test that cannot fail theming-tokens.md.dt offered a [Theory] over Light and Dark whose only assertion was Assert.Empty(AccessibilityScanner.Scan(tree)) -- and the scanner never resolves a brush (0 occurrences of RequestedTheme in AccessibilityScanner.cs, against 28 of AutomationName). Both InlineData cases return identical findings, so a banner hardcoded to a light-only colour passes exactly like a correct one. The theme parameter reaches nothing the assertion reads. That is the worst shape a documented test can take: it was presented as the remedy for the dark-mode regression it structurally cannot detect, in the section named after that regression. The paragraph above it was also wrong -- it said the testing page covers a fixture that mounts the component under both themes and snapshots both. testing.md.dt contains zero occurrences of theme, Theme, Dark or RequestedTheme, so there is no such pattern to point at. Rewritten: the block is now labelled as the trap, with a comment explaining precisely why it is vacuous, and the guidance says a colour regression is only observable once the tree is materialized and the resource lookup runs -- so it belongs in a selftest, which is also the only tier that can construct WinUI controls at all. Verified: docs compile --ci exit 0, Reactor.Tests and DocPipeline.Tests green, Release build of src/Reactor.Cli clean. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 50199c9b-2da9-46a5-9afd-857958642219
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 256 out of 294 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/Reactor.Cli/Docs/PhantomSymbolLint.cs:124
- The new
Textguard still misses valid single-argument phantom calls whose argument starts with an unlisted expression shape, such asText(enabled ? "On" : "Off"),Text(prefix + value), orText((string)value). Those examples remain uncompiled and pass this lint, so the durability gate can silently regress. Detect an unqualifiedTextinvocation with exactly one top-level argument (using balanced-token scanning) instead of enumerating selected argument prefixes, and add regression cases for these shapes.
Round 25 was right that the prefix enumeration could not converge: each round added an arm and the next found another shape. Ternary, concatenation and cast were still missing. Balanced scanning asks the structural question instead: an unqualified Text invoked with exactly one top-level argument. Two exclusions carry it. Multi-argument calls are not this phantom -- that keeps the positional D3Charts.Text(x, y, text) shape out even spelled unqualified. English prose is not an expression. A C# argument never contains two identifier tokens separated by nothing but whitespace: "prefix + value" has an operator between them, "the element" does not. That single test admits every expression shape without enumerating any, and it is what keeps sentences like "Text (the element) is set separately" silent. Two things the scan surfaced that the regex had hidden Text(\"Hello\") stopped matching, because I had SkipLiteral honour backslash as an escape. On these surfaces a quote is spelled three ways -- ", \" (code embedded in a C# string literal, the original docking defect) and "" (the same verbatim) -- so a backslash before a quote is part of the quote's spelling, not an escape of it. Honouring it swallowed the closing quote and the call scanned as unterminated. This is the exact case the historical-defect theory exists to pin, and it caught it. Text(...) began firing across the docset. The ellipsis placeholder is how prose names a signature, not a call -- including in charting.md.dt's own sentence warning that there is no core Text(...) factory. Excluded structurally rather than papered over with a skip marker, since otherwise every signature mention would need one. PhantomSymbol gains an optional Scanner; the other six phantoms are unambiguous names and stay on their regexes. Verified: docs compile --ci exit 0 (0 findings), Reactor.Tests and DocPipeline.Tests green, Release build of src/Reactor.Cli clean. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 50199c9b-2da9-46a5-9afd-857958642219
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 255 out of 294 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
src/Reactor.Cli/Docs/PhantomSymbolLint.cs:517
- This prose discriminator also rejects valid C# expressions that use word operators. For example, both
Text(value is null ? "" : value)andText(value as string)contain adjacent identifier tokens, soHasAdjacentIdentifiersreturns true and the phantom call silently passes. The matcher needs to recognize C# operator/token shapes rather than treating every adjacent identifier pair as prose.
Round 26: one suppressed finding, one real inline finding, and three CodeQL
style notes.
Word-shaped operators were being read as English
The prose discriminator asked whether two identifier tokens sit adjacent with
only whitespace between them. C# spells several operators with letters, so
`Text(value is null ? "" : value)` and `Text(value as string)` looked exactly
like prose and were dismissed.
Adjacency now only counts when BOTH sides are ordinary identifiers. "the
element" has no operator between the words; an expression always does, even
when that operator is spelled with letters.
A wrapped call was never reported at all
The scanner received one physical line, so it could only close a paren on that
line. A doc example routinely writes
Text(
BuildLabel(item))
and the whole invocation went unseen -- in fenced blocks and XML examples
alike, the surfaces the gate exists for.
It now gets a bounded lookahead window (8 lines), built only where every line
is genuinely code: inside a fence, an all-code surface, or a contiguous ///
block. Joining prose would let an unbalanced "Text (" in one sentence close
against a ')' in the next and manufacture a false positive.
The window creates a misattribution trap worth naming: an earlier line's
window also reaches the call, so it would report it too. The scanner therefore
only accepts invocations that START within the first line. The regression test
asserts Single(findings) at the opening line, not merely that something fired
-- an Any() assertion would have passed with the duplicate bug present.
Also applied the three CodeQL notes (Where on the phantom loop, Select on the
opener projection, Any in IsElidedArgument).
Verified: docs compile --ci exit 0 (0 findings), Reactor.Tests and
DocPipeline.Tests green, Release build of src/Reactor.Cli clean.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 50199c9b-2da9-46a5-9afd-857958642219
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 255 out of 294 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/Reactor.Cli/Docs/ReferenceGen/CrefSignature.cs:151
- A complete
<typeparam>set is not necessarily in declaration order—XML documentation preserves the tags in authoring order. For a valid method declared<TKey, TValue>but documented with theTValuetag first, this branch emitsMethod<TValue, TKey>and substitutes those names into the wrong parameter slots and anchors. Either obtain ordinals from assembly metadata, or use placeholders for multi-parameter methods when order cannot be proven.
Round 27 reported no new comments on b8ec120, with one suppressed finding and three CodeQL notes. A complete <typeparam> set is not an ordered one My own comment said these tags are "in XML comment order" and then concluded that a complete set could be indexed positionally. Those two statements contradict each other. XML preserves authoring order, so a method declared <TKey, TValue> whose TValue tag is written first renders Method<TValue, TKey> -- a complete set of real names in the wrong slots, which then propagates into the parameter types and the anchor. Nothing in a cref carries declaration ordinals, so the order cannot be recovered here. Names are now used only at arity 1, where there is nothing to order; placeholders stand in beyond that. A placeholder is less informative, a swapped name is wrong and wrong silently. The new order-insensitivity test asserts the in-order and swapped inputs render identically, which fails if order ever leaks back in. The old test asserted the unsafe behaviour and was replaced rather than relaxed. Empty catch blocks A comment does not satisfy the rule -- it wants a statement -- so the three catches that end their lambda now `return;`. ScreenshotCapture is deliberately not among them: its catch is followed by the `port >= 0 && token is not null` handshake check, so returning early would skip it and change behaviour on a partially-completed handshake. Left as a commented empty block; the note there explains why cancellation is ignored. Also applied the two remaining CodeQL notes (Where in HasAdjacentIdentifiers, a named projected sequence for the opener indices). Verified: docs compile --ci exit 0, Reactor.Tests and DocPipeline.Tests green, Release builds of src/Reactor.Cli plus the hooks, regedit and demo-script-tool apps clean. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 50199c9b-2da9-46a5-9afd-857958642219
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 255 out of 294 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
samples/apps/demo-script-tool/App/DemoScriptShell.cs:176
- This explanation no longer matches the implementation:
cts.Tokenis captured synchronously beforeTask.Run, so the previous worker cannot still be reading that property. The remaining hazard is disposing the source whileTask.Delayor the file write registers the captured token; likewise, the worker still indirectly uses the source throughCancellationToken. Update the comment to describe that registration race.
Round 28 found the sibling of the bug fixed last round: I corrected method type
parameters in CrefSignature but left declaring-type parameters, which
ReferenceGenerator builds the same way and indexes the same way.
typeParamsByType projected <typeparam> tags straight into a positional list,
then FormatType resolved `0/`1 against it. A type documented <TResult> before
<TInput> renames every parameter into the wrong slot, and the signature it
produces feeds the anchor, so the damage outlives the heading.
Same rule as the method side: a single documented name has no order to get
wrong and is used; from two upward, placeholders. Arity comes from the cref,
not from the tag count, so an under-documented type still gets the right
number of slots.
No generated-docs drift, so this is a guard against a latent defect rather
than a correction of shipped output.
The regression test needed two attempts, which is the point
The first version generated one member and compared headings. It passed --
and still passed when I reverted the fix, because a page with a single member
renders only "# Swap": the per-member signature heading, the only place
declaring type parameters appear, is never emitted. Both sides were empty and
the assertion compared nothing.
The test now declares two overloads so the collision page renders signatures,
and asserts a non-vacuity floor (a "Swap(" heading exists) before comparing.
It fails when the fix is reverted, which the first version did not.
Also corrected the DemoScriptShell comment, which described a hazard my own fix
had already removed: the token is captured synchronously before Task.Run, so
the worker never reads cts.Token late. The real remaining hazard is disposing
the source while Task.Delay or the file write holds a registration on it.
Plus the CodeQL Select note on the opener projection.
Verified: docs compile --ci exit 0, Reactor.Tests and DocPipeline.Tests green,
Release builds of src/Reactor.Cli and demo-script-tool clean.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 50199c9b-2da9-46a5-9afd-857958642219
The last open code-quality thread. LooksLikeSingleArgTextCall now reads as one chain -- project each opener to its argument, drop the empty and elided ones, and return whether any survivor is not prose -- instead of a foreach with three continue guards. Behaviour is unchanged and the phantom-lint theories (historical defects, expression shapes, word operators, multiline, prose, elided placeholders) all still pass. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 50199c9b-2da9-46a5-9afd-857958642219
Audit of the entire user guide for staleness, run as nine parallel sessions over disjoint slices, then integrated. Every slice's commits are preserved.
Why this was needed
The docs pipeline compiles code blocks written as
```csharp snippet="topic/id", but not bare```csharpblocks,```xmlblocks, prose inside backticks, code inside string literals, or///XML doc comments. At the start, 239 of 654 code blocks (37%) had no compile gate at all.That is where the rot was. All 50 doc apps built clean on
mainthe whole time — the snippet-backed code was fine. The damage was concentrated exactly where nothing looked.Headline fixes
.csprojdid not work for anyone outside this repo.index.md.dttold readers to use<ProjectReference Include="..\Reactor\Reactor.csproj" />. Now aPackageReferencepinned to the{{reactorVersion}}token. Verified by actually building it: the block was extracted from the template by regex, scaffolded outside the repo with all package sources cleared, and compiled. Six variants were tested to establish that<Platforms>is neither necessary nor sufficient — the load-bearing property is theRuntimeIdentifierauto-resolve line, without which a baredotnet run(which the next sentence tells the reader to do) fails with the Windows App SDK architecture error.controls.mdclaimed to catalogue "every control" and listed 46 of ~93. Three whole categories were missing. Now 99 rows, plus aControlCatalogCompletenessTestsgate that parses the registration bootstrap and fails the build when a registered control has no catalog row.SetWindowRgnrecipe was a double-free.using var region = …thenSetWindowRgn(hwnd, region.DangerousGetHandle(), true)— the system takes ownership on success, so theusingfreed GDI memory the compositor still read.chartingdocumented.Margin(t, r, b, l)in CSS order; the real signature isMargin(left, top, right, bottom). Silently wrong margins, no error.UseEffect's reference page documented 1 of its 8 overloads. The generator keyed pages by short name, so overloads collided and all but one were silently dropped — including theFunc<Action>cleanup form.<!-- ref:X -->markers were emitted verbatim into published Markdown, rendering as three invisible gaps mid-sentence inwin2d-canvas.md.docs check-tier --topicfabricated errors for any topic whose app directory name differs from its template id (11 topics), reportingfound 0 snippet refsfor pages with five.UseTheme(),RenderContext.Current,ElementDescription.Of,mur devtools serve,ProgressBar(...),VStackElement,A11Y_KEYBOARD_001(a fabricated analyzer ID promising runtime coverage that doesn't exist),Optional.Of(...),Text(...)as an element factory, and more.The rot ran backwards, into shipped source
Text(...),UseTheme(),Optional.Of(...)and threeWith*Transitionmodifiers were not only in the guide — they were in///doc comments insrc/, which ship inReactor.xmlas the IntelliSense every NuGet consumer sees. In several cases the templates were quoting the source comment;extending-reactor-controls.md.dttracksOptional.cs's wording almost verbatim.Element.cs's implicit string operator callsFactories.TextBlock(text)while its own doc comment two lines above saidText("Hello").Fixing only the guide would have let the source re-seed it.
The same phantoms had also reached contributor guidance:
AGENTS.mdand twopr-reviewskill dimensions documentedRegisterV1BuiltInHandlers(deleted by spec 048) and aPoolableWireFlagstype that never existed. Those misdirect every future contributor and agent session before work starts.Durability, not just cleanup
Cleanup alone would rot again, so this adds gates:
REACTOR_DOC_PHANTOM_001— a docs-pipeline lint over templates, string literals and///comments. Deliberately not amur checkrule:CheckCommandonly invokes rules per parsed MSBuild diagnostic and printsokwhen there are none, so a rule over surfaces that compile clean could never fire. Ships at Error severity, somur docs compile --cifails the Docs build on new rot; Warning was tried first and did not gate CI. The pre-existing backlog still passes via a per-(file, phantom) ceiling budget rather than a blanket suppression (new rot fails; fixing rot passes). The lint reads fenced blocks and inline code spans, since code formatting in prose reads as an endorsement, and matches phantom receivers (UI.Text()) as well as phantom members.AgentKitMarkdown_NamesNoPhantomApis— extends the same matcher over theplugins/andskills/markdown packed into the NuGet underagentkit/, which is what an AI assistant reads when writing Reactor code. It asserts it found files before reporting clean, so a broken sweep reddens instead of looking green.ControlCatalogCompletenessTests— makes the "every control" claim checkable.MinimalCsprojDocTests— asserts the landing-page csproj parses, references the package not a project path, uses the version token not a literal, and keeps theRuntimeIdentifierline.ComponentElementhaving no.Set, aWithKeyexample that never compiled (CS0314), and akeys.FirstOrDefault()that compiled fine but silently yieldeddefault(RowKey)instead of null.Verification
Reactor.Tests: 13,334 passed, 0 failed.Reactor.DocPipeline.Tests: 379/379.docs/guideis idempotent — 190 generated files byte-identical on recompile.Text("Hello")instead of copied from the real pre-fix line — which uses escaped quotes inside a string literal — it would have passed and shipped blind.Not verified locally: the full Release solution build cannot complete on an offline machine — three
src/vs-reactor/**projects fail restore with NU1301. Confirmed environmental by running the identical command on unmodifiedmain, which fails identically; this branch touches onlysrc/vs-reactor/README.md. CI has network and will run that gate properly.Known issues, deliberately not fixed
#ctor, so positional records lose their<param>docs entirely.MutationOptions.mdhas no Parameters section becauseOnError/OnSuccessdocumentation lives on the skipped constructor. Separate bug, larger than this change.REACTOR_DOC_REFGEN_001rises 54 → 64, and that is correct. −12 from the cref fix, +22 newly surfaced: 18 previously-dropped overloads are now rendered and their prose cites out-of-category types, which degrade to inline code by design. Those crefs were "absent" only because the prose containing them was being deleted. A per-cref diff confirms no in-category cref regressed.<!-- ref: -->markers are removed, not bypassed. If Advanced element types become routable later, that capability would ship with nothing pointing at it; restoring the cross-references needs a deliberate edit towin2d-canvas.md.dt.app:directory that doesn't exist. Benign — 17 are 100%source:snippets, which need no doc app — but the metadata is inaccurate.Reviewing this
The nine slices are independent and each merge commit is one slice, so reviewing per-merge works well. Two files have two authors (
index.md.dt,text-and-media.md.dt) and both contributions were verified to survive.One caveat worth knowing:
docs/guide/**is generated.docs/guide/win2d-canvas.mdconflicted at integration and was resolved by regenerating, not by picking hunks — merging generated output textually can produce a file matching no valid regeneration.UseFocusTrap.mdalso needed regeneration to pick up a compound effect no single branch produced: a doc-comment fix on one branch only becomes visible once the generator on another branch renders<example>blocks it previously dropped.