Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ to land under these conventions; subsequent specs follow this shape.

## [Unreleased]

### Changed (breaking)

- **`.Margin(double, double)` and `.Padding(double, double)` parameter order
swapped to match CSS shorthand convention.** Was `(horizontal, vertical)`;
now `(vertical, horizontal)`. This aligns with CSS — `padding: 16px 14px;`
means top/bottom = 16, left/right = 14, vertical first. Any existing
positional 2-arg call site in the repo has been migrated to the named-arg
form (`.Margin(horizontal: 16, vertical: 8)`) which preserves layout
regardless of parameter order; recommend the same for external callers.
Pre-1.0 breaking change is intentional — the original ordering was a
layout-rotation footgun for agents and humans with CSS muscle memory.
(spec 038 §3 — feedback from 525-run corpus / WPF-vs-CSS mental model)

### Added

- `mur check --trace <path>` — append one JSONL row per parsed diagnostic
Expand Down Expand Up @@ -78,6 +91,135 @@ to land under these conventions; subsequent specs follow this shape.
values; gate threshold (3) empirically defensible at 28.7% emit rate.
No code change in this entry — calibration + docs only. (spec 038 §1.8,
Data Checkpoint C)
- `mur check` Phase 2 — MSBuild passthrough + deterministic pre-emit ranker.
`mur check [<path>] [mur-flags] [-- <msbuild-args>]` — anything after a
bare `--` is forwarded verbatim to `dotnet build`. `mur` injects `--nologo`,
`-v:m`, and `-p:Platform={host arch}` only if the same flag is not named
in the passthrough section (detection by flag name, not value). When
`--trace` is on, the trace records the effective `dotnet build` argv as
a `kind: "command"` header row so replays are bit-faithful. New mode
flags: `--strict` (promote warnings to errors), `--final` (emit every
diagnostic — pre-merge sweep), `--quiet` (errors only). `--emit-threshold
<float>` overrides the per-mode ranker default (0.6 iteration / 0.0 final).
Pre-emit ranker (`src/Reactor.Cli/Check/Ranker/PolicyTable.cs`) suppresses
noise mid-iteration (CS1591, CS0168, IDE0xxx, NU1701/NU1605,
MSB3245/MSB3270/MSB3277, CS8600–CS8625 nullable warnings) while always
emitting errors. (spec 038 §8, Phase 2.1–2.3)
- `tools/Reactor.MurCheckGuardrail` — offline guardrail that audits a pair
of `--trace` files (one iteration, one `--final`) against PolicyTable's
universal-error floor invariant. Fails CI if a future policy-table edit
would let a real build error get suppressed mid-iteration. The "universal
floor" rule (Error severity always scores 1.0 regardless of code family)
makes the invariant hold by construction today; the guardrail is the
regression test that catches accidental violations. (spec 038 §8 Phase 2.4)
- `plugins/reactor/skills/reactor-build-and-check/SKILL.md` updated for
the iteration / `--final` workflow. EC2 measured 0/10 production value
on the strong "explicit done gate" framing across 6 variant runs, so
the framing was softened post-batch: `--final` is now documented as an
optional pre-merge sweep (for human review / CI ship-readiness gates),
explicitly NOT a task-completion requirement. SKILL anchor wording:
"When `mur check` exits 0, you are done." Same wording in the legacy
root `SKILL.md`. (spec 038 §8 Phase 2.5)
- Phase-2.x — gate-input regression fix in `CheckCommand.ShouldEmitSuggestions`.
The initial Phase-2 implementation counted the post-ranker `emittable`
list when deciding whether to run the Tier-2 suggester. EC2 (3-round
preview) measured Tier-2 firing collapse from EC1's 80% to 0% on
kanban-mur because nullable warnings (CS8602/etc) were filtered out
of the emittable list before the gate-count, closing the gate on
builds EC1 had left open. Fixed by counting the full parsed
`diagnostics` list — the gate measures build complexity, not stdout
visibility. Regression test
`RankerTests.Suggest_gate_counts_full_parsed_list_not_post_ranker_emittable`
locks the behavior; fails the build if the bug is reintroduced.
(spec 038 §14 #8)
- Phase-2.x — EC2 5×N PASS by median (2026-05-11). `reactor-calc-mur-check`
beats base on every metric (cost −5.1%, tokens −5.8%, turns −5.1%,
wall −7.9%; variance 1.9× tighter). `reactor-kanban-mur-check` at cost
median parity ($3.30 = $3.30); mean dragged to +5.7% by R2 outlier
(n=5, R2-excluded mean is −3.3%). First-build OK 5/5 on both variant
arms. `--final` invocation 0/10 across both projects (SKILL framing
doing its job). Tier-2 firing 0/10 — gate correctly inhibits on
small-batch iteration patterns; closing the kanban token gap is
Phase-3's scope (rules > fuzzy match). Criterion-2 guardrail audit
deferred to a harness retrofit (post-run `mur check --final` against
the final workspace state to generate the iter+final trace pair the
guardrail tool audits). Phase 2 cleared to merge to `main`.
(spec 038 §1.8 EC2 acceptance, §8, §11)
- Phase 3.1 / 3.1a — Tier-3 rule infrastructure scaffolded. New surface
under `src/Reactor.Cli/Check/Rules/`: `IRulePattern` contract (`Name`,
`Provenance`, `DiagnosticCodes`, `DeclaredTargets`, `TryMatch`),
`RuleContext` + `RuleSuggestion` records, `RuleRegistry` (reflection
discovery of `IRulePattern` implementations in `Reactor.Cli`, `Default`
singleton, dedup on Name collisions, `BestMatch` with disable list and
self-disable-on-unresolved-target reporting, `Statuses` for `--list-rules`),
and `RuleSymbolResolver` (per-`CSharpCompilation` cached symbol lookup
via `ConditionalWeakTable` — spec §3.1a's contract that rules never
string-match `MemberAccess.Name.ValueText`). New CLI flags
`--disable-rule <Name>` (repeatable, warns on unknown names) and
`--list-rules` (short-circuits `dotnet build`, prints the
name/provenance/status table, exits 0). `SuggesterOrchestrator` runs
rules alongside Tier-2; spec §6 "rule wins over Tier-2 fuzzy match"
preserved; rules can match diagnostic codes outside Tier-2's
`SupportedCodes` so CS1955 / Theme-lookup rules are unblocked.
`tests/Reactor.Tests/CheckCommandTests/Rules/RuleTargetResolutionTests.cs`
is the §3.1a CI gate — instantiates every registered rule against a
live Reactor `Compilation` (full assembly references, the inverse of
`TestCompilation.Create`) and asserts every declared target resolves.
Passes vacuously today; becomes load-bearing the moment the first
rule lands. 35 new unit tests covering contract shape, registry
discovery and edge cases (duplicates, throwing rules, self-disable),
resolver cache identity, orchestrator rule-vs-Tier-2 precedence, and
ArgsParser round-trip. Phase-3 rule PRs themselves remain blocked on
the second-agent corpus drop (cross-agent reproducibility bar #2 of
the Validation Gate). (spec 038 §3.1 + §3.1a)
- Spec 038 Phase-3 vocab table at `docs/specs/tasks/038-vocab-table.csv`
(§3.0 prerequisite for any Class-B rule PR). 20 rows covering WPF /
Silverlight / WinUI 2 / WinUI 3 → Reactor vocabulary translations,
seeded from the 525-run report's Phase-3 priority targets plus desk
research against `skills/reactor.api.txt`. (spec 038 §3.0)
- First three Class-B vocabulary-translation rules: `ThemeBackgroundSuffixRule`
(CS0117 on `Theme` with member ending in `Background` → `Theme.SolidBackground`,
cluster C0019, 16 events); `AlignmentShortcutRule` (CS1061 on Reactor
`*Element` receivers for `HorizontalAlignment` / `VerticalAlignment` →
`.HAlign(...)` / `.VAlign(...)`, cluster C0017 + adjacent ≈ 22 events); and
`ButtonOnClickFactoryMoveRule` (CS1061 on `ButtonElement.OnClick` →
`Button(..., onClick: ...)` factory named-arg, explicitly naming `.OnTapped`
as the wrong sibling to keep agents from reaching for the gesture event).
Both bind target types via `RuleSymbolResolver` (no string matching);
the rule-target resolution CI gate is now load-bearing. 10 new unit tests
(positive fixtures cite their source `run_id`s from the 525-run corpus;
hand-authored extensions are tagged `[Trait("Origin", "VocabHandAuthored")]`).
PRs remain blocked on Validation Gate bar #5 (independent reviewer
signoff) — the artifacts are "ready for review", not "ready to merge".
(spec 038 §3.2, §6 Class B)
- `.Margin(...)` and `.Padding(...)` per-side overloads now default unspecified
sides to `0.0`. Enables agent-intuitive call shapes like `.Margin(top: 12)`,
`.Padding(left: 8, right: 8)` that previously failed to compile (CS7036:
no matching overload). 525-run corpus shows **198 build failures** from
agents writing this exact shape against the prior all-required signature —
far and away the highest-frequency failure-driver in the drop. Eliminating
it is a single-line code edit per overload but a large agent-productivity
unlock. `Reactor.Tests` adds CSS-ordering + per-side + positional-overload
regression tests. (spec 038 §3 follow-up — surfaced during Phase-3 rule
authoring)
- Cheatsheet in `plugins/reactor/skills/reactor-getting-started/SKILL.md` now
shows the named-arg `Button("Save", onClick: handler)` form alongside the
positional one, with an explicit anti-pattern comment naming `.OnClick(...)`
and `.OnTapped(...)` as the wrong fixes for click intent. The cheatsheet's
`.OnTapped((s, e) => ...)` example is now anchored to non-Button surfaces
(Border / Image / ScrollView) with a back-reference to the Controls section
— the prior parenthetical Button carve-out was easy to miss mid-build.
(spec 038 §3 — agent-facing skill updates)
- Spec 038 §3.1a residual: trace-channel structured warning hook for
self-disabled rules. `TraceWriter.WriteRuleSelfDisabled(rule, target)`
emits `{kind: "rule_self_disabled", rule, unresolved_target, mode}`.
`SuggesterOrchestrator` threads an optional `onRuleSelfDisabled`
callback through to `RuleRegistry.BestMatch`; `CheckCommand.Run` wires
it to the active trace writer when `--trace <path>` is set, dedup'd
per-invocation per-rule. Stdout stays clean — agents don't read trace
files, but maintainers see "rule X disabled because target Y didn't
resolve" the moment a Reactor minor release breaks something.
(spec 038 §3.1a)
- EC1 re-run with the diagnostic-count gate (2026-05-11): both arms PASS.
`reactor-calc-mur-check` cost −4% mean (was +21% in the prior batch);
`reactor-kanban-mur-check` cost −33% mean / −39% median (was −24% mean
Expand Down
3 changes: 3 additions & 0 deletions Reactor.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,9 @@
<Project Path="tests/stress_perf/StressPerf.Wpf/StressPerf.Wpf.csproj" />
</Folder>
<Folder Name="/tools/" />
<Folder Name="/tools/Reactor.MurCheckGuardrail/">
<Project Path="tools/Reactor.MurCheckGuardrail/Reactor.MurCheckGuardrail.csproj" />
</Folder>
<Folder Name="/tools/Reactor.SignaturesGen/">
<Project Path="tools/Reactor.SignaturesGen/Reactor.SignaturesGen.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
Expand Down
41 changes: 37 additions & 4 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ The `mur` CLI ships these embedded — works from any directory:
| `mur --skill` | This SKILL.md | embedded in `mur` |
| `mur --api` | The signatures index (≈12K tokens, every factory/modifier/hook/Theme token/enum) | embedded in `mur` |
| `mur --regen-api` | Rebuilds `skills/reactor.api.txt` from a freshly-built `Reactor.dll` (selfhost only) | rebuilds `tools/Reactor.SignaturesGen` |
| `mur check <path>` | Runs `dotnet build` and emits one-line diagnostics with skill-file pointers for known REACTOR_* IDs | wraps MSBuild |
| `mur check <path>` | **Is** the build (same exit code as `dotnet build`); adds one-line diagnostics with skill pointers for known REACTOR_* IDs and `→ try:` did-you-mean suggestions | wraps MSBuild |

A consumer who doesn't have `mur` can read the same files directly from the
NuGet cache:
Expand Down Expand Up @@ -113,17 +113,50 @@ form-with-validation, async-fetch-list, themed-card.

## `mur check` — fast feedback with skill pointers

`mur check <path>` builds the target and emits one-line diagnostics with
pointers into the skill files for known Reactor analyzer IDs:
**`mur check` is the build, not a separate check step.** It runs `dotnet build`
under the hood and returns the same exit code. When `mur check` exits 0, the
build is green — **do not re-run `dotnet build` to confirm**. They're the same
compilation; a redundant `dotnet build` afterwards is wasted work.

Two enrichments over raw `dotnet build`:

1. **Skill pointers** for known `REACTOR_*` IDs.
2. **Did-you-mean `→ try:` suggestions** for unknown identifiers, computed against
the live Reactor surface for the exact diagnostic.

```
C:\path\Program.cs:15:23 W REACTOR_DSL_001 Element produced by Select(...)… → SKILL.md gotcha #6 (.WithKey on dynamic list items)
C:\path\Program.cs:34:16 E CS1061 'ButtonElement' does not contain a definition for 'OnClick' → try: Button(label, onClick: ...) // [factory has Action onClick parameter]
```

`<path>` defaults to `.` and accepts a `.csproj` or directory. Single-file
`.cs` builds work but **don't load analyzers** — for analyzer coverage,
use a `.csproj`.

**Trust `→ try:` suggestions directly.** Use the suggested name verbatim in your
next edit; don't grep adjacent or sibling names to second-guess it. The
suggestion has been computed against the actual Reactor surface for this exact
diagnostic. If wrong, the next `mur check` will tell you — that self-correcting
loop is cheaper than manual verification.

**Don't introspect via `[System.Reflection]`** to enumerate Reactor types or
members — the skill files plus `mur check`'s did-you-mean suggestions plus
`skills/reactor.api.txt` cover the surface.

Workflow modes (Phase-2 ranker):

- `mur check` — iteration mode (default). A ranker suppresses cosmetic noise
(CS1591 XML doc, CS0168 unused-var, IDE0xxx style, NuGet restore chatter)
mid-iteration so the real blocker doesn't scroll off attention. **When this
exits 0, you are done** — the build is green.
- `mur check --final` — optional pre-merge sweep. Emits the cosmetic
diagnostics suppressed during iteration (XML doc, unused locals, style
hints, nullable warnings, transient restore noise). For human code review
or a CI ship-readiness gate; **not** a task-completion requirement and
skipping it is fine.
- `mur check -- <msbuild args>` — anything after a bare `--` is forwarded
verbatim to `dotnet build` (override platform, config, restore, verbosity).

## Sub-skills — load when the task calls for them

| Skill | When to load |
Expand Down Expand Up @@ -404,7 +437,7 @@ class App : Component
Heading("My App").VAlign(VerticalAlignment.Center),
NavBtn("Home", page, setPage),
NavBtn("Settings", page, setPage))
).Background("#f0f0f0").Padding(24, 12).Grid(row: 0),
).Background("#f0f0f0").Padding(horizontal: 24, vertical: 12).Grid(row: 0),

Border(page switch
{
Expand Down
4 changes: 2 additions & 2 deletions docs/_pipeline/apps/animation/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public override Element Render()
l.Count > 0 ? l.Take(l.Count - 1).ToList() : l))
),
VStack(4, items.Select(item =>
TextBlock(item).Padding(8, 12).Background("#f0f0f0")
TextBlock(item).Padding(horizontal: 8, vertical: 12).Background("#f0f0f0")
.CornerRadius(4).LayoutAnimation()
.WithKey($"item-{item}")
).ToArray())
Expand Down Expand Up @@ -286,7 +286,7 @@ public override Element Render()
SubHeading("Staggered Animation"),
Button("Shuffle", () => setItems(items.OrderBy(_ => Random.Shared.Next()).ToArray())),
VStack(4, items.Select(item =>
TextBlock(item).Padding(8, 12).Background("#f0f0f0")
TextBlock(item).Padding(horizontal: 8, vertical: 12).Background("#f0f0f0")
.CornerRadius(4).LayoutAnimation()
.WithKey(item)
).ToArray()).Stagger(TimeSpan.FromMilliseconds(40))
Expand Down
2 changes: 1 addition & 1 deletion docs/_pipeline/apps/calculator/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Element OpButton(string label, string opCode) =>
TextBlock(display)
.FontSize(32).Bold()
.HAlign(HorizontalAlignment.Right)
.Padding(12, 8),
.Padding(horizontal: 12, vertical: 8),

// Button grid
HStack(4, Button("C", PressClear).Width(60).Height(48),
Expand Down
2 changes: 1 addition & 1 deletion docs/_pipeline/apps/collections/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public override Element Render()
HStack(8,
ForEach(colors, ((string Name, string Hex) color) =>
TextBlock(color.Name)
.Padding(8, 16)
.Padding(horizontal: 8, vertical: 16)
.Background(color.Hex)
.CornerRadius(4)
.WithKey(color.Name)
Expand Down
2 changes: 1 addition & 1 deletion docs/_pipeline/apps/flex-layout/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public override Element Render()
FlexRow(
tags.Select(tag =>
TextBlock(tag)
.Padding(6, 12)
.Padding(horizontal: 6, vertical: 12)
.Background("#e8e8e8")
.CornerRadius(12)
).ToArray()
Expand Down
4 changes: 2 additions & 2 deletions docs/_pipeline/apps/styling/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public override Element Render()
TextBlock("Accent Text").Foreground(Theme.AccentText).SemiBold(),
TextBlock("On Accent Background")
.Foreground("#FFFFFF")
.Padding(8, 4)
.Padding(horizontal: 8, vertical: 4)
.Background(Theme.Accent)
.CornerRadius(4)
).Padding(24);
Expand Down Expand Up @@ -88,7 +88,7 @@ static Element Badge(string label, ThemeRef color) =>
TextBlock(label)
.FontSize(12).SemiBold()
.Foreground(color)
.Padding(8, 4)
.Padding(horizontal: 8, vertical: 4)
.Background(Theme.SubtleFill)
.CornerRadius(4);
}
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/animation.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class LayoutAnimationDemo : Component
l.Count > 0 ? l.Take(l.Count - 1).ToList() : l))
),
VStack(4, items.Select(item =>
TextBlock(item).Padding(8, 12).Background("#f0f0f0")
TextBlock(item).Padding(horizontal: 8, vertical: 12).Background("#f0f0f0")
.CornerRadius(4).LayoutAnimation()
.WithKey($"item-{item}")
).ToArray())
Comment thread
codemonkeychris marked this conversation as resolved.
Expand Down Expand Up @@ -429,7 +429,7 @@ class StaggerDemo : Component
SubHeading("Staggered Animation"),
Button("Shuffle", () => setItems(items.OrderBy(_ => Random.Shared.Next()).ToArray())),
VStack(4, items.Select(item =>
TextBlock(item).Padding(8, 12).Background("#f0f0f0")
TextBlock(item).Padding(horizontal: 8, vertical: 12).Background("#f0f0f0")
.CornerRadius(4).LayoutAnimation()
.WithKey(item)
).ToArray()).Stagger(TimeSpan.FromMilliseconds(40))
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ class ForEachDemo : Component
HStack(8,
ForEach(colors, ((string Name, string Hex) color) =>
TextBlock(color.Name)
.Padding(8, 16)
.Padding(horizontal: 8, vertical: 16)
.Background(color.Hex)
.CornerRadius(4)
.WithKey(color.Name)
Comment thread
codemonkeychris marked this conversation as resolved.
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/flex-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class WrapGapDemo : Component
FlexRow(
tags.Select(tag =>
TextBlock(tag)
.Padding(6, 12)
.Padding(horizontal: 6, vertical: 12)
.Background("#e8e8e8")
.CornerRadius(12)
Comment thread
codemonkeychris marked this conversation as resolved.
).ToArray()
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ class CalculatorApp : Component
TextBlock(display)
.FontSize(32).Bold()
.HAlign(HorizontalAlignment.Right)
.Padding(12, 8),
.Padding(horizontal: 12, vertical: 8),

Comment thread
codemonkeychris marked this conversation as resolved.
// Button grid
HStack(4, Button("C", PressClear).Width(60).Height(48),
Expand Down
Loading
Loading