Skip to content

spec(047): Phase 3 prereqs — HandCoded* builders + TextBox descriptor proof#424

Merged
codemonkeychris merged 1 commit into
mainfrom
spec/047-phase3-prereqs
May 27, 2026
Merged

spec(047): Phase 3 prereqs — HandCoded* builders + TextBox descriptor proof#424
codemonkeychris merged 1 commit into
mainfrom
spec/047-phase3-prereqs

Conversation

@codemonkeychris
Copy link
Copy Markdown
Collaborator

@codemonkeychris codemonkeychris commented May 27, 2026

Summary

  • 3.0.1 ships HandCodedControlled / HandCodedEvent escape-hatch builders on ControlDescriptor. Method-level TPayload + native TDelegate generics let multi-event controls (TextBox / NumberBox / Image / etc.) reuse the existing per-control event payload types from ControlEventPayloads.cs without disturbing the single-event fast-path DescriptorControlledPayload. Trampolines stay static; subscription is callback-gated and fires once per control lifetime.
  • 3.0.2 ports TextBox as the 2-event proof point — Text via HandCodedControlled (TextChanged), SelectionChanged via HandCodedEvent, both sharing TextBoxEventPayload's existing slots. Adds 3 Desc_TextBox_* self-test fixtures (16 TAP checks). Bench's DescriptorVariantFactory now registers TextBoxDescriptor alongside the Q1 trio.
  • 3.0.3 captures an x64 advisory perf run under docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/. All Q1 gating benches land ≤5% vs ReactorV2; M2 -2.2% / M10 +1.1% — within the §9.2.1 ±3% thesis. The README is explicit that this is NOT authoritative — a stable-AC ARM64 re-capture on LAPTOP-4MEP83VI mirroring the Phase 2 methodology should land before §14 Phase 3 is closed.

3.0.4 (author guide) deferred to a later session per the resume doc's guidance.

Documented gaps

  • TextBoxDescriptor does not request rerender from the TextChanged trampoline. Controlled-mode snap-back (when a user callback returns the same Value after filtering input) therefore differs from the hand-coded TextBoxHandler. Acceptable for the proof point; the descriptor's Update enforces the controlled value when Update DOES run.

Test plan

Builds (x64 local)

  • src/Reactor, tests/Reactor.AppTests.Host, tests/perf_bench/PerfBench.ControlModel — 0 errors
  • Solution-wide build — failed locally on StressPerf.{Direct,DirectX} due to Cloud PC disk-space (415 MB free) blocking native runtime DLL copies. Environment, not code; CI will exercise.

Self-test (956 fixtures, x64 local; REACTOR_USE_V1_PROTOCOL flipped on the same exe)

Run Fixtures TAP ok TAP not ok Failing fixtures
V1 ON 956 3574 2 NativeDocking_CompositionDrivenDocumentsRespectKeyedReconciliation, FloatCov_BuildFloatingRoot_ProducesTabbedChrome
V1 OFF 956 3573 3 NativeDocking_Composition_SiblingMutation_PreservesActivePaneIdentity (3 assertions)
Failing fixtures re-run in isolation × 3 0 All pass
  • My new Desc_TextBox_* and the pre-existing V1_ / Desc_ fixtures: all 60 checks pass identically under V1 ON and V1 OFF.**
  • The two/three failing fixtures above are all in NativeDocking_* / FloatCov_* — zero overlap with code this PR touches. The failure set DIFFERS between V1 ON and V1 OFF, which rules out a V1-flag regression. All pass cleanly when re-run in isolation, indicating test-ordering / state-bleed flakes in the docking suite, not caused by this PR.

Other gates

  • Reactor.Tests xUnit: 9086 passed / 0 failed / 62 skipped
  • Vulnerable-package gate via dotnet list ... --vulnerable --include-transitive: 0 vulnerable packages
  • PerfBench 3×5 advisory capture committed; M2 / M10 within ±3% on this x64 capture

Outstanding for §14 Phase 3 close

  • ARM64 stable-AC re-capture on LAPTOP-4MEP83VI to ratify the §9.2.1 thesis for the multi-event hand-coded descriptor shape (capture protocol documented in the Phase 3 results README).

🤖 Generated with Claude Code

… proof

Ships the §14 Phase 3 prerequisites that unblock bulk control migration:

3.0.1 — HandCodedControlled / HandCodedEvent escape-hatch builders on
ControlDescriptor. Method-level TPayload generic threads a user-supplied
per-control event payload (e.g. TextBoxEventPayload) through subscription
without disturbing the existing single-event fast-path
DescriptorControlledPayload. Native TDelegate generic lets authors pass
the control's natural trampoline type (TextChangedEventHandler,
RoutedEventHandler) directly — no EventHandler<TArgs> bridge closure.

3.0.2 — TextBoxDescriptor as the 2-event proof point. Text via
HandCodedControlled (TextChanged), SelectionChanged via HandCodedEvent —
both sharing TextBoxEventPayload's existing slots. Trampolines stay
static (zero per-mount closures); subscription is gated on the live
element's callback presence and fires once per control lifetime.
Documented gap: the descriptor does not request rerender from
TextChanged, so controlled-mode snap-back differs from the hand-coded
handler on filtered-input-returns-same-state scenarios. Acceptable for
the proof point; matches the §14 thesis that the hand-coded escape
hatch retains nuances the declarative path can re-engage if needed.

3.0.3 — x64 advisory perf capture under
docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/. ALL Q1
gating benches land in the ≤5% band on this advisory capture
(M2 -2.2%, M10 +1.1% vs ReactorV2 — within the §9.2.1 ±3% thesis).
The README is explicit that this is NOT authoritative — a stable-AC
ARM64 re-capture on LAPTOP-4MEP83VI mirroring the Phase 2 methodology
should land before §14 Phase 3 is closed. The Phase 2 ARM64 verdict
(judgment-call band, descriptors as primary) stands until then.

Test infrastructure:
- 3 new Desc_TextBox_* self-test fixtures (16 TAP checks) covering
  mount/update, 2-event subscription wiring, and the callback-gate.
- DescriptorVariantFactory now registers TextBoxDescriptor for the
  ReactorDescriptors bench variant alongside the Q1 trio.

Validation on this branch (x64 Cloud PC):
- src/Reactor + Reactor.AppTests.Host + PerfBench.ControlModel: 0 errors.
- All 60 V1_* + Desc_* checks pass identically under V1 ON and V1 OFF
  (REACTOR_USE_V1_PROTOCOL env var flipped both ways on the same exe).
- Reactor.Tests xUnit: 9086 passed / 0 failed / 62 skipped.
- Vulnerable-package gate: 0 vulnerable packages.
- Solution-wide build could not complete locally due to a Cloud PC disk
  space constraint (415 MB free, copy step failed on native runtime
  DLLs for StressPerf.{Direct,DirectX}). Reactor-scope projects all
  built clean; the failures are environmental, not code, and CI will
  exercise the full solution.

Deferred to a later session:
- 3.0.4 — author onboarding doc (docs/guide/descriptor-authoring.md),
  per resume guidance.
- ARM64 stable-AC re-capture on LAPTOP-4MEP83VI to ratify the §9.2.1
  thesis (see Phase 3 results README for capture protocol).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codemonkeychris codemonkeychris requested a review from Copilot May 27, 2026 13:12
@codemonkeychris codemonkeychris merged commit 5a8bf93 into main May 27, 2026
16 checks passed
@codemonkeychris codemonkeychris deleted the spec/047-phase3-prereqs branch May 27, 2026 13:18
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR advances Spec 047 Phase 3 by adding descriptor escape-hatch builders for hand-coded event wiring, introducing TextBox as the first multi-event descriptor proof point, and committing advisory x64 benchmark artifacts.

Changes:

  • Adds HandCodedControlled and HandCodedEvent descriptor entries/builders.
  • Adds TextBoxDescriptor and self-test fixtures for TextBox mount/update and event subscription behavior.
  • Updates descriptor perf registration and documents an advisory Phase 3 x64 benchmark capture.
Show a summary per file
File Description
src/Reactor/Core/V1Protocol/Descriptor/ControlDescriptor.cs Adds public fluent builders for hand-coded controlled/event descriptor entries.
src/Reactor/Core/V1Protocol/Descriptor/PropEntry.cs Implements the new hand-coded controlled and event prop entry types.
src/Reactor/Core/V1Protocol/Descriptor/Descriptors/TextBoxDescriptor.cs Adds the TextBox descriptor proof point using shared TextBox event payload slots.
tests/Reactor.AppTests.Host/SelfTest/Fixtures/Spec047V1ProtocolDescriptorFixtures.cs Adds TextBox descriptor self-test fixtures.
tests/Reactor.AppTests.Host/SelfTest/SelfTestFixtureRegistry.cs Registers the new TextBox descriptor fixtures.
tests/perf_bench/PerfBench.ControlModel/Variants/DescriptorVariantFactory.cs Registers TextBoxDescriptor in the descriptor perf variant.
docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-27-textbox-proof-3x5/README.md Documents the advisory benchmark capture and caveats.
docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-27-textbox-proof-3x5/summary.md Adds aggregated benchmark results.
docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-27-textbox-proof-3x5/aggregate.py Adds the aggregation script used for the benchmark summary.
docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-27-textbox-proof-3x5/launch-1.jsonl Adds raw benchmark run output.
docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-27-textbox-proof-3x5/launch-2.jsonl Adds raw benchmark run output.
docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-27-textbox-proof-3x5/launch-3.jsonl Adds raw benchmark run output.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 12/12 changed files
  • Comments generated: 7

.OneWayConditional(
get: static e => e.IsReadOnly,
set: static (c, v) => c.IsReadOnly = v!.Value,
shouldWrite: static e => e.IsReadOnly == true)
Comment on lines +116 to +121
get: static e => e.SelectionStart,
set: static (c, v) => c.SelectionStart = v!.Value,
shouldWrite: static e => e.SelectionStart.HasValue)
.OneWayConditional(
get: static e => e.SelectionLength,
set: static (c, v) => c.SelectionLength = v!.Value,
Comment on lines +123 to +127
.OneWayConditional(
get: static e => e.MaxLength,
set: static (c, v) => c.MaxLength = v,
shouldWrite: static e => e.MaxLength != 0)
.OneWayConditional(
Comment on lines +131 to +135
.OneWayConditional(
get: static e => e.CharacterCasing,
set: static (c, v) => c.CharacterCasing = v,
shouldWrite: static e => e.CharacterCasing != WinUI.CharacterCasing.Normal)
.OneWayConditional(
Comment on lines +135 to +139
.OneWayConditional(
get: static e => e.TextAlignment,
set: static (c, v) => c.TextAlignment = v,
shouldWrite: static e => e.TextAlignment != TextAlignment.Left)
.OneWayConditional(
@@ -0,0 +1,34 @@
# Per-(bench, variant) means

| Bench | Variant | n | Mean ns | 95% CI �ns | Mean alloc B | 95% CI �B |
| M10 | ReactorDescriptors | 15 | 150,409 | 6,903 | 35,380,849 | 1,220,536 |
| | | | | | | |

# Q1 head-to-head � ReactorDescriptors deltas
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants