fix(quickfiler): itemviewer breadcrumb lifecycle defects D1-D5 and #475 - #672
Merged
drmoisan merged 14 commits intoAug 28, 2026
Conversation
Records the policy reads, feature-document reads, repo state, resolved toolchain paths, SDK/NuGet/tool restore, and the four baseline gates: csharpier check (0 unformatted of 1553), analyzer Rebuild (0 errors, 5 pre-existing System.Reactive advisories, 0 CoreCompile skips), nullable Rebuild (0 errors), QuickFiler.Test (1192/1192, empty failure set), and repository-wide coverage (line-rate 0.852607, branch-rate 0.791925 over 9 assemblies, 6812 tests). Also records the baseline per-file coverage of the three measured owned production files, the eight file-size rows (all matching constraint C2), the seven-file ExcludeFromCodeCoverage counts, the eight CaptureCurrentOrTests occurrence lines, and the capacity budget. [P0-T1] through [P0-T18]. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…acement (#488 D1) ConfigureBreadcrumbDropDown(environment, initializer) now disposes the outgoing host between the same-environment early return and the construction of the replacement, so the ordering is guaranteed by statement order rather than by dispatcher behaviour. The type test names the concrete BreadcrumbDropDownHost rather than IBreadcrumbDropDownHost, so a mock host installed by the injected 3-arg overload is not disposed here and ItemViewerDisposal_OwnsHostAndDetachesBothSurfaces keeps its Times.Once() assertion. A fresh pattern variable is used because the one bound in the same-environment guard is definitely assigned only on the returning branch. Adds QuickFiler.Test/Viewers/ItemViewerBreadcrumbLifecycleRegressionTests.cs with its own viewer scope, host accessor, context assigner, uninitialized-environment factory, and drainable synchronization context, plus the D1 regression test. One Compile Include line is added to QuickFiler.Test.csproj adjacent to the existing ItemViewerBreadcrumbDropDownContractTests entry. Fail-before evidence records the test Failed on its discriminating SetTheme disposal-guard assertion; pass-after records all three named tests Passed with both constraining test files byte-identical to BASE_SHA. [P1-T1] through [P1-T15]. Six spec criteria checked off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d host (#488 D2) BreadcrumbItemViewerLifecycleCoordinator now retains the last requested theme and replays it onto the newly adopted host inside ConfigureHost's posted lambda, so a theme set while that post is still queued is no longer lost. SetTheme assigns the retained value before its two forwarding calls, so the retained value can never lag the caller's most recent request. The replay is confined to the newly-adopted branch. The UpdateRequestProviders branch performs no theme call, which is what keeps ResetAndPooledReuse and the mock-host replacement tests unaffected. The guard captures the field into a local and tests `retained != null` alongside IsNullOrWhiteSpace. The bare IsNullOrWhiteSpace form raised CS8604 because the net481 reference assemblies carry no NotNullWhen post-condition, and that file has `#nullable enable`, so TreatWarningsAsErrors would have promoted it to an error. No null-forgiving operator and no suppression was used. RecordingHost gains a ThemesApplied recorder; its Dispose body stays empty so HostReplacement_SubscriptionsAndMessengerReplacementPreserveOrder is unaffected. Fail-before evidence records the applied-theme sequence as empty. Pass-after records the D2 test plus both coordinator constraining tests green, and the interaction evidence records why the pooled-reuse "no stale pooled theme" assertion survives. The constrained file lands at 497 lines, a +16 delta within its 19-line headroom, and is already CSharpier-clean. [P2-T1] through [P2-T12]. Five more spec criteria checked off (11 of 54). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…#488 D3) InitializeBreadcrumbPipeline now retains the provider it was initialized with and throws InvalidOperationException when a later call supplies one that is not reference-equal, instead of discarding it through a blanket already-initialized early return. A repeat call with the same provider reference still returns without effect. DisposeBreadcrumbResources clears the retained reference so a pipeline re-created after disposal is not blocked by a stale one. A retained field is required because BreadcrumbBridgeCoordinator does not expose its provider. The comparison is reference equality, matching what BreadcrumbItemViewerLifecycleCoordinator.SetBridgeCoordinator already does. No re-initialization branch is added, per constraint C7: keeping this fail-fast is what holds the out-of-scope SetBridgeCoordinator replace-without-dispose defect dormant, since InitializeBreadcrumbPipeline then never constructs a second bridge coordinator. This changes no production behaviour. The single production call site, QfcItemController.ViewerSetup.cs:150 inside EnsureBreadcrumbPipeline, is guarded upstream on viewer.BreadcrumbCoordinator == null in a 484-owned file, so the new throw is unreachable from production. Both D3 regression tests exclude ObjectDisposedException from the negative assertion, so a D5 disposal throw cannot satisfy a D3 test by inheritance. [P3-T1] through [P3-T12]. Five more spec criteria checked off (16 of 54). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
D4) Adds a private ThrowIfOffUiBoundary(string) helper to ItemViewer.Breadcrumb.cs and invokes it as the first statement of InitializeBreadcrumbPipeline(provider, operations), both ConfigureBreadcrumbDropDown overloads, and EnsureBreadcrumbResourceOwnership. The comparison is reference equality against UiSyncContext, not managed thread identity: a continuation resumed without the captured context can land on a recycled pool thread whose id matches, so a thread id is not a boundary proof. The null-context escape keeps a viewer constructed without an ambient context from throwing. No Interlocked, lock, Monitor, Volatile, or Mutex construct is introduced. Atomic initialization was rejected because it would legitimise off-thread access to WinForms control state that is not thread-safe at all. Both regression tests call the two-argument overload with injected operations, per decision D-8: the one-argument overload evaluates CaptureCurrent() eagerly and that method already throws under a null ambient context, so a test against it would have passed before the guard existed. Each asserts the message names the operation and excludes ObjectDisposedException. The second case installs a different non-null context, proving the comparison is reference equality rather than a null check. Both doc comments state that the proxy proves the guard fires and does not prove the race is absent. The full QuickFiler.Test assembly runs 1198/1198 green under the guard, with an empty failing set matching the empty Phase 0 baseline. That settles the recorded C6 enumeration discrepancy: the re-derived construction-site set holds 19 executable sites, not 13, and one of the five additions installs no synchronization context at all. No site was edited and the guard was not weakened. [P4-T1] through [P4-T14]. Five more spec criteria checked off (21 of 54). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…488 D5) EnsureBreadcrumbResourceOwnership now throws ObjectDisposedException when the viewer reports IsDisposed or Disposing, before any Container is created and before any BreadcrumbResourceOwner is added, so no pipeline is built against a dead viewer. Statement order follows decision D-15 and is stated explicitly in the delivered source: ThrowIfOffUiBoundary is the first statement but is a precondition check that returns without effect, so the disposal throw immediately following it is the first statement that acts. Both the "first statement" and "first action" criteria are satisfied under that reading. Control.IsDisposed and Control.Disposing are public WinForms properties, so no new state was needed and ItemViewer.Designer.cs (6223 lines, 489-owned) is untouched and byte-identical to BASE_SHA. Discharges research section 3.5: a faulted QfcItemController.InitializeWebViewAsync task is NOT observed. Three of its four production call sites discard it (Initialization.cs:192, :288, :324); only :256 awaits. The D5 guard is not weakened in response, and a potential entry is filed against QfcItemController.ViewerSetup.cs. TWO TASKS LEFT UNCHECKED. [P5-T6] and [P5-T11] both require a GitHub issue number for that follow-up. enforce-promotion-mcp-only.ps1 forbids `gh issue create` and requires the drm-copilot MCP promotion tools, which are not in this executor's tool set. The forbidden path was not used and no wording was changed to evade the hook. The caller holds those tools. [P5-T1] through [P5-T5], [P5-T7] through [P5-T10]. Three more spec criteria checked off (24 of 54); the research-section-3.5 criterion is left unchecked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… all three parts) Part 1: deletes BreadcrumbPopupUiOperations.CaptureCurrentOrTests(). A repository-wide search of tracked .cs files now returns zero hits. CreateForCurrentThreadTests survives unchanged on both BreadcrumbPopupUiOperations and BreadcrumbUiDispatcher: only the ambient-probing selector is removed, so every injected test seam is preserved. Part 2: both BreadcrumbDropDownHost seven-parameter constructor chains now supply CaptureCurrent(). No constructor argument is reordered, which is what keeps Constructor_NullLegacySurfaceFactory_ThrowsForSurfaceFactory passing without an ambient context: the surfaceFactory null guard is evaluated before the operations argument. Part 3: EnsureBreadcrumbLifecycle takes a Func<BreadcrumbPopupUiOperations>, invoked exactly once and only after the already-initialized early return, with all three call sites updated. This is required rather than opportunistic: the member discards its operations argument whenever the coordinator already exists, so an eager argument would make a pure no-op call throw on any thread without a context. Replaces the deleted boundary test with CaptureCurrent_NullAndControlledContexts_FailFastAndCapture, retaining the controlled-context half. Adds the constructor fail-fast and seam-preservation regression tests, bringing the new file to 8 test methods. The new test file first measured 521 lines. Per capacity rule 3 the doc comments were compacted to 480 rather than dropping a method or creating a second file; every acceptance-required clause was preserved and re-verified. BreadcrumbPopupUiOperations.cs is 489, strictly below its 494 baseline, by a pure deletion with zero additions. Fail-before evidence for the constructor test was taken by temporarily restoring a local ambient-probing selector, rebuilding (exit 0), observing the failure, then restoring to a byte-identical file (SHA verified) and rebuilding again (exit 0). [P6-T1] through [P6-T17]. Seven more spec criteria checked off (31 of 54). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Re-runs the nine constraining tests: all nine Passed, each matching its Phase 0 baseline of Passed, zero failures. Verifies the changed-file set: 78 paths, zero outside the permitted set of four owned production files, three owned test files, QuickFiler.Test.csproj, the feature folder, and docs/features/potential/. All eleven forbidden files produce an empty git diff --name-only against BASE_SHA. Public surface: three added members all private (ThrowIfOffUiBoundary, _breadcrumbProvider, the retained-theme field), one re-signed member the private EnsureBreadcrumbLifecycle, one removed member the internal static CaptureCurrentOrTests. Zero public members added, removed, or re-signed; both seven-parameter BreadcrumbDropDownHost constructor signatures unchanged. Re-checks all six 489 dependencies against current source; all confirmed. D489-4's two collision searches return zero matches across every other ItemViewer partial. Records a correction to constraint C6's reachability argument: the delivered ItemViewer constructor calls Dispatcher.CurrentDispatcher, not TaskScheduler.FromCurrentSynchronizationContext, so a viewer with a null UiSyncContext is constructible. No delivered design changes. csproj diff confirmed exactly 1 added / 0 deleted, at line 88 immediately below the ItemViewerBreadcrumbDropDownContractTests entry, BOM and CRLF preserved. Creates the three out-of-scope follow-up potential entries (D1c; SetBridgeCoordinator replace-without-dispose; Reset synchrony mismatch, naming both its files) and confirms no fix for any appears in the diff. Writes the change description quoting the spec and both D4 test doc comments verbatim. [P7-T1] through [P7-T18]. Nine more spec criteria checked off (40 of 54). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Format: csharpier format over the seven owned paths, exit 0. Per-file SHA-256 shows exactly one file changed, ItemViewer.Breadcrumb.cs, by a cosmetic lambda reflow. csharpier check . then reports zero unformatted of 1554 files, matching the empty Phase 0 baseline set. Analyzers: msbuild /t:Rebuild with EnableNETAnalyzers, exit 0, 0 errors, 5 warnings (all the pre-existing System.Reactive packages.config advisory). Per-file analyzer warning count for each owned production file is 0, equal to baseline. Zero "Skipping target CoreCompile" against 83 CoreCompile executions. Nullable: msbuild /t:Rebuild with TreatWarningsAsErrors, exit 0, 0 errors, zero CS86xx. Nullable=enable not supplied; target was Rebuild. Zero CoreCompile skips against 76 executions. Tests: 1201/1201 passed, 0 failed, exactly BASELINE_PASSED 1192 + 9. All ten added tests enumerated and Passed; the deleted test absent from the TRX. Coverage: repository-wide 6821/6821 passed over 9 assemblies. Four figures recorded -- baseline raw 0.852607 and testable 0.850562; post-change raw 0.852830 and testable 0.850799. Both denominators rose. All 14 measured added lines are covered. File sizes: all eight at most 500; the two constrained files at 497 and 489; BreadcrumbDropDownIntegrationTests.cs exactly 500 and byte-identical to BASE_SHA. Exemptions: per-file ExcludeFromCodeCoverage counts equal baseline exactly across all seven owned files; none added, none removed. Also removes host identity from evidence: /EnableCodeCoverage deposited attachments named after the account and machine, and the TRX format records computerName on every result. The attachments were deleted and all 19 evidence TRX files scrubbed, with zero residual references and all counters preserved. Test policy audit records one honest exception: the replacement boundary test retains a Task.Run from the deleted test's controlled-context half, exactly as [P6-T3] instructs. It is deterministic and does not affect the criterion, which bans sleeps, delays, wall-clock waits, and temporary files -- all four absent. [P8-T1] through [P8-T11]. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Writes the fail-before index covering all six defect units, each citing an artifact recording a non-zero EXIT_CODE with ExpectedExitCode: 1 (verified by direct search). #475 carries two, because the constructor observation required a temporary revert with a rebuild on each side. Checks off 53 of the 54 spec criteria. No criterion text was modified: the spec.md diff is 53 added / 53 deleted, every changed line is a checkbox line, and stripping the checkbox prefix from both sides of the diff yields identical text sets. ONE CRITERION REMAINS UNCHECKED and the reconciliation is recorded as remediation-required, not a pass. The research section 3.5 criterion has two of its three clauses delivered -- the open item is discharged (the faulted InitializeWebViewAsync task is NOT observed; three of four call sites discard it) and the D5 guard is not weakened -- but the GitHub issue it specifically requires could not be opened. enforce-promotion-mcp-only.ps1 forbids `gh issue create` and requires the drm-copilot MCP promotion tools, which are not in this executor's tool set. The forbidden path was not used and no wording was altered to evade the hook. The follow-up is prepared as a potential entry and needs only promotion. The authorized pre-existing-unformatted-file exception was not used and does not apply: both the Phase 0 baseline and the final csharpier check reported empty sets. [P9-T1] through [P9-T16]. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
git status --porcelain produces no output lines; nothing under .claude/agent-memory is dirty. git diff --name-only against BASE_SHA lists 107 paths, all of which are the four owned production files, the three owned test files, QuickFiler.Test.csproj, paths under the feature folder, or paths under docs/features/potential/; a mechanical filter for anything outside that set returns 0. Ten commits on the branch, one per phase. Nothing pushed. [P9-T17]. 143 of 145 plan tasks checked off; [P5-T6] and [P5-T11] remain blocked on MCP-only issue promotion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The executor delivered 143 of 145 plan tasks and 53 of 54 acceptance criteria. The single gap was the research 3.5 criterion, which requires a GitHub issue to be opened against QfcItemController.ViewerSetup.cs and referenced from the spec. The executor prepared the follow-up entry but could not promote it: the approved MCP promotion tools are absent from its tool set, and it correctly declined to use the hook-forbidden alternative or to reword anything to get around the gate. The orchestrator holds those tools and ran the approved promotion path, producing issue 670 (qfc-initializewebviewasync-fault-is-unobserved), verified OPEN. The faulted task is confirmed unobserved at three of its four production call sites, and the D5 guard is delivered unweakened, as the criterion requires. Records the receipt in the D5 evidence artifact, adds the issue reference to the spec D5 design section, and checks off the criterion plus plan tasks P5-T6 and P5-T11. The executor's blocked-state record is retained rather than deleted, since it documents a tool-set boundary a future executor will meet again. Result: 145 of 145 plan tasks, 54 of 54 acceptance criteria. No criterion text was modified and the criterion count is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Feature review returned PASS with zero Blocking findings and six Non-blocking ones. Adds the three audit artifacts and remediates the one finding that was actionable before fan-in. PA-1 asked for a mechanical sanitization pass over the TRX evidence. All 19 files now carry REDACTED-WORKTREE-ROOT in place of the absolute worktree path and REDACTED-DOMAIN-USER in place of the runUser domain token. A case-insensitive sweep of the feature folder for the account and machine names returns zero files. The only remaining occurrences in the repository are in four previously merged promoted potential entries and in untracked build outputs, none of them in this branch's diff. The same pass uncovered a defect the audit had not detected. The original scrub substituted the placeholder into XML attribute values using angle brackets, which are not legal there, so all 19 committed TRX files were not well-formed and would not parse. Verified against the committed blobs, so the defect predates this change. Replacing the 7306 occurrences with a bracket-free token restores well-formedness: all 19 parse, zero failures. Only path and identity attributes changed; every counter is preserved. Re-parsing them independently corroborates the delivered result: baseline 1192 passed, final 1201 passed, the D4 full-suite run 1198 of 1198, and genuine red fail-before runs for all six defect units. The evidence is strictly more useful than before, having gone from unparseable to machine-readable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The feature review's PA-1 recommended a repo-wide cleanup entry for the host tokens already present in merged sibling evidence, and PA-2 left the raw-Cobertura size question owed to a maintainer decision. Both are real and neither is curable by this branch alone. Files them as issue 671, covering the pre-existing tokens in features 501, 608 and 439, the angle-bracket placeholder trap that silently breaks TRX well-formedness, and the coverage-artifact size convention. Filed rather than left as prose, because a finding recorded only in a feature folder stops being visible once the feature merges. Issue 671 is a follow-up and is deliberately excluded from this branch's pull-request closing list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
drmoisan
merged commit Aug 28, 2026
4b6d38d
into
epic/quickfiler-bug-family-integration
5 checks passed
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
Fixes six lifecycle defects in the QuickFiler breadcrumb pipeline: five filed under #488 (D1 through D5) and one under #475. All six alter observable behaviour on a construction, replacement, or teardown path, so each carries its own deterministic regression test with recorded fail-before evidence.
This is the twelfth and final child of the
quickfiler-bug-familyepic. It targetsepic/quickfiler-bug-family-integration, notmain.Closes #488
Closes #475
SetThemereads a host that the deferredConfigureHostpost has not yet adopted, so the null-conditional swallows the call and the popup keeps the old theme.IFolderHierarchyProvideris silently discarded, making the wrapper laxer than the collaborator it wraps.InvalidOperationException; return without effect when the provider is reference-equal.SynchronizationContext.CurrentagainstUiSyncContext.Containercreated afterDispose(bool)has run is never disposed, leaking the hub, the lifecycle coordinator, and the bridge coordinator.ObjectDisposedException.CaptureCurrentOrTests()selects a test-only dispatcher by probing ambient state, inverting a fail-fast guard into a silent degradation: the popup never opens, with no exception and one log line.CaptureCurrent()at both constructor chains.Two results a reviewer should not misread
D3 changes no production behaviour, by design.
QfcItemController.ViewerSetup.csguards both production callers onviewer.BreadcrumbCoordinator == null, so on a pooled reuse the second controller never callsInitializeBreadcrumbPipelineat all. The stale-provider symptom is real but originates upstream in a file owned by feature 484. Fixing the wrapper is correct defensive work; no user-visible repair should be expected from it.D4's regression test proves the guard fires. It does not prove the race is absent. A true two-thread data race cannot be reproduced deterministically under this repository's ban on sleeps and wall-clock waits. No acceptance criterion in this change claims the race is eliminated, and the spec, the change description, and both test methods'
<remarks>each say so explicitly.Toolchain
Run in the mandated order with
/t:Rebuild, never/t:Build, and verified non-vacuous.dotnet tool run csharpier check .msbuild ... /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=trueSkipping target "CoreCompile"across 83 compilesmsbuild ... /p:TreatWarningsAsErrors=trueSkipping target "CoreCompile"/p:Nullable=enableis deliberately not supplied; it is absent fromci.ymlfor the same reason.The
Skipping target "CoreCompile"count is asserted at zero because a warm/t:Buildreturns EXIT 0 having compiled nothing, which would make the analyzer gate unable to fail.Coverage
Both denominators rose. Floors are 85% line and 75% branch. Each of the three measured owned files exceeds the 90% new-code floor (0.909091, 0.991342, 0.992883), and all 14 measured added lines are covered.
Four of the six fixes land in
ItemViewer.Breadcrumb.cs, whose partial type carries[ExcludeFromCodeCoverage]. Their regression tests are required by the Bugfix Workflow but move no coverage number. Flat coverage on those units is not a testing gap and must not be "fixed" by removing the exemption, which is a 489-owned file. No[ExcludeFromCodeCoverage]attribute is added or removed anywhere by this change.Scope
Four owned production files, two owned test files, one new test file, and one project file.
QuickFiler/QuickFiler.csprojis absent from the diff entirely;QuickFiler.Test.csprojgains exactly one line (+1/-0), placed beside the existingItemViewerBreadcrumbDropDownContractTests.csentry with no reordering. That item group is not alphabetically ordered, and was deliberately not "corrected".Verified byte-identical to the base: all 489-owned
ItemViewerpartials, all 501-owned breadcrumb coordinators, the 484-ownedQfcItemControllerpartials,ItemViewer.Designer.cs, andBreadcrumbDropDownIntegrationTests.csat exactly 500 lines.The 141 pre-existing
<Compile Include>entries carried by eleven merged sibling features are all preserved.Content-loss check
git diff --numstat <base>..HEADfiltered to rows with 0 added and more than 0 deleted returns exactly one file:QuickFiler/Viewers/BreadcrumbPopupUiOperations.cs(0 added, 5 deleted).That is not content loss. The invariant is that no file may lose content the base gained — not that the query prints nothing, which is unsatisfiable for a change that legitimately removes code. Two proofs: there are zero base-only commits anywhere, since
merge-baseequals the base tip, so the base gained nothing after this branch's fork point; and scoped to that file, zero base-only commits touched it. The five deleted lines are exactly theCaptureCurrentOrTestsmethod, which is the whole chartered purpose of #475. A repository-wide search for the identifier now returns zero hits, whileCreateForCurrentThreadTestssurvives, as its own criterion requires.Review
policy-audit,code-review, andfeature-auditat2026-08-28T06-44: PASS, zero Blocking findings, six Non-blocking.The one Non-blocking finding actionable before fan-in was host identity tokens in the committed TRX evidence. Remediating it uncovered a second defect the audit had not caught: the original redaction wrote an angle-bracket placeholder into XML attribute values, where
<is illegal, so all 19 committed TRX files were not well-formed and would not parse. This was verified against the committed blobs, so it predated the remediation. Replacing 7,306 occurrences with a bracket-free token restored well-formedness; all 19 now parse. Only path and identity attributes changed, every counter is preserved, and re-parsing them independently corroborates the delivered result — baseline 1192 passed, final 1201 passed, the D4 full-suite run 1198 of 1198, and genuine red fail-before runs for all six defect units.Acceptance criteria
spec.mdis the sole acceptance-criteria source for thisfull-bugfeature: 54 of 54 checked, no criterion text modified. The plan is 145 of 145 tasks complete.One criterion required a GitHub issue to be opened for a discharged research item. The executor prepared it but could not promote it, correctly declining the hook-forbidden path rather than rewording to evade the gate; the orchestrator completed the approved promotion path. That interim 53-of-54 state was recorded honestly as remediation-required and superseded in place rather than overwritten.
Follow-up issues opened, not closed by this PR
QfcItemController.InitializeWebViewAsyncis unobserved at three of its four production call sites. Required by D5's acceptance criterion; filed against a 484-owned file this feature must not edit. The D5 guard is delivered unweakened in response, as the criterion demands.Three further out-of-scope candidates are recorded as potential entries: the
ConfigureHostgeneration guard dropping the incoming host,SetBridgeCoordinatorreplacing without disposing, and theReset()surface-detach synchrony mismatch.Note on merge semantics
This PR targets the epic integration branch, so merging it will not close #488 or #475. GitHub registers closing references only for pull requests targeting the default branch; the closing keywords above carry forward to the integration-to-main pull request, which the epic parent opens.