spec(047): Phase 3 prereqs — HandCoded* builders + TextBox descriptor proof#424
Merged
Conversation
… 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>
There was a problem hiding this comment.
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
HandCodedControlledandHandCodedEventdescriptor entries/builders. - Adds
TextBoxDescriptorand 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
HandCodedControlled/HandCodedEventescape-hatch builders onControlDescriptor. Method-levelTPayload+ nativeTDelegategenerics let multi-event controls (TextBox / NumberBox / Image / etc.) reuse the existing per-control event payload types fromControlEventPayloads.cswithout disturbing the single-event fast-pathDescriptorControlledPayload. Trampolines stay static; subscription is callback-gated and fires once per control lifetime.TextviaHandCodedControlled(TextChanged),SelectionChangedviaHandCodedEvent, both sharingTextBoxEventPayload's existing slots. Adds 3Desc_TextBox_*self-test fixtures (16 TAP checks). Bench'sDescriptorVariantFactorynow registersTextBoxDescriptoralongside the Q1 trio.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
Valueafter 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 errorsStressPerf.{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_PROTOCOLflipped on the same exe)NativeDocking_CompositionDrivenDocumentsRespectKeyedReconciliation,FloatCov_BuildFloatingRoot_ProducesTabbedChromeNativeDocking_Composition_SiblingMutation_PreservesActivePaneIdentity(3 assertions)Desc_TextBox_*and the pre-existing V1_ / Desc_ fixtures: all 60 checks pass identically under V1 ON and V1 OFF.**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.TestsxUnit: 9086 passed / 0 failed / 62 skippeddotnet list ... --vulnerable --include-transitive: 0 vulnerable packagesOutstanding for §14 Phase 3 close
🤖 Generated with Claude Code