Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c6723e9
docs(468): phase 0 baseline and toolchain bootstrap
drmoisan Aug 26, 2026
63eebd4
fix(468): remove unreachable load paths and the dead _templateTlp field
drmoisan Aug 26, 2026
122dcd8
fix(474): retype _parent to IQfcFormController and drop the runtime d…
drmoisan Aug 26, 2026
fbe5b3a
fix(286): restore the reentrancy counter on the exceptional exit path
drmoisan Aug 26, 2026
d512fcf
fix(469): replace the unordered move collection with an ordered snapshot
drmoisan Aug 26, 2026
8637aaa
fix(473): stop swallowing cancellation and double-logging one root fa…
drmoisan Aug 26, 2026
137ee30
fix(469): correct the diagnostics array length and guard before deref…
drmoisan Aug 26, 2026
6232243
fix(470): derive the conversation insertion count from a single sourc…
drmoisan Aug 26, 2026
4038113
fix(470): handle a missing conversation original explicitly instead o…
drmoisan Aug 26, 2026
ffc10ff
fix(470): skip groups with no controller or viewer in SetVisualDigits
drmoisan Aug 26, 2026
6cac5a8
refactor(471): extract the shared panel-height arithmetic behind Shri…
drmoisan Aug 26, 2026
f733506
fix(471): shrink the item panel on conversation collapse
drmoisan Aug 26, 2026
9760406
refactor(473): extract DrainBackgroundLoadingTasksAsync from the dupl…
drmoisan Aug 26, 2026
505cab9
fix(473): drain background loading tasks through an atomic bag swap
drmoisan Aug 26, 2026
613e88c
docs(469): document the retained stackMovedItems contract and consume…
drmoisan Aug 26, 2026
4938779
refactor(474): split the move-readiness evaluation from its notification
drmoisan Aug 26, 2026
48c9ad8
docs(468): commit phase 11-13 QA-gate and regression evidence
drmoisan Aug 26, 2026
7f0e7a2
Merge remote-tracking branch 'origin/epic/quickfiler-bug-family-integ…
drmoisan Aug 26, 2026
ef90790
Merge origin/epic/quickfiler-bug-family-integration into bug/qfc-coll…
drmoisan Aug 26, 2026
5f8026a
fix(474): make move readiness inspectable without presenting a dialog
drmoisan Aug 26, 2026
71713df
docs(468): dossier, audits, downstream handoff, and follow-up entries
drmoisan Aug 26, 2026
e265a26
docs(468): final QA loop evidence and coverage comparison
drmoisan Aug 26, 2026
fa0446b
docs(468): check off AC-1 through AC-26 and record orchestrator defer…
drmoisan Aug 26, 2026
f077696
docs(468): close out phase 16 acceptance-criteria check-off
drmoisan Aug 26, 2026
68826ef
docs(468): record final clean-tree verification
drmoisan Aug 26, 2026
9194305
docs(468): record follow-up issue numbers 629-635 and check off AC-29
drmoisan Aug 26, 2026
7c8d843
docs(468): add feature, policy and code-review audit artifacts
drmoisan Aug 26, 2026
3540556
docs(468): verify PR body against the five accuracy constraints and c…
drmoisan Aug 26, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,23 @@ A receipt counts **only when all three hold together**:
hard-coded `'evidence'` member regardless of the array. Truthy-but-not-`$true` flags
(`1`, `"true"`) deliberately do not count.

`delegation_receipts[]` is separate and needs `agent_name`, `agent_id`, `step`, `phase`,
`skill_source`, `result_signal`, `artifact_paths` (list). It is a LIST, not an object.
`delegation_receipts` is separate. Two forms are accepted (`OrchestratorStateReceipts.psm1`): a bare
LIST, or an OBJECT namespace whose only permitted keys are `agents` (a list) and `promotion` (an
object with only `potential_entry` / `issue` / `feature_folder`). The object form is what lets a run
carry BOTH the promotion receipts the orchestrator agent spec demands and the routing-contract
`agent_name` list — use it.

Each entry in the list (or in `agents`) needs **exactly these eight keys**, confirmed 2026-08-26 by
reading the MCP validator's own error output on epic child #468:
`agent_name`, `step`, `agent_id`, `skill_source`, `started_at`, `completed_at`, `result_signal`,
`artifact_paths`. There is **no `phase` key** (an earlier note here claimed one; it is wrong).

**Only record agents THIS run delegated.** Naming an upstream-prepared agent (`task-researcher`,
`prd-feature`, `atomic-planner` from an epic-planner preparation run) in `agents` immediately forces
`model_routing_receipts is missing a receipt for delegated agent: <name>` under
`require_model_routing` — and you would have to invent a model choice you never made. Leave
`agents: []` until your first delegation returns, and record the upstream work in `notes` and
`delegation_receipts.promotion` instead.

### Bug-promotion tool-name swap, and the divergence it creates

Expand Down
154 changes: 154 additions & 0 deletions QuickFiler.Test/Controllers/QfcCollectionController.TestSupport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using System;
using System.Reflection;
using System.Runtime.Serialization;
using FluentAssertions;
using QuickFiler.Controllers;

namespace QuickFiler.Controllers.Tests
{
/// <summary>
/// Shared reflection helpers and builders for the issue #468 defect-family test files.
/// <para>
/// <see cref="QfcCollectionController"/>'s only constructor requires WinForms UI components, so
/// instances are allocated with <see cref="FormatterServices.GetUninitializedObject(Type)"/> to
/// bypass it and the required private fields are then injected by reflection. This mirrors the
/// technique already established in <c>QfcCollectionControllerTests.cs</c>.
/// </para>
/// <para>
/// The helpers below follow the <em>asserting</em> form used by
/// <c>QfcItemControllerTestSupport</c> (assert the member was found before touching it) rather
/// than the silently-no-op <c>?.SetValue(...)</c> form used by the older
/// <c>QfcCollectionControllerTests.SetControllerField</c>. A typo in a member name must fail the
/// test loudly instead of leaving the field at its default and producing a misleading result.
/// </para>
/// </summary>
internal static class QfcCollectionControllerTestSupport
{
private const BindingFlags NonPublicInstance =
BindingFlags.NonPublic | BindingFlags.Instance;

private const BindingFlags NonPublicStatic = BindingFlags.NonPublic | BindingFlags.Static;

/// <summary>
/// Sets a non-public instance field on <paramref name="controller"/>, asserting first that
/// the field exists.
/// </summary>
internal static void SetField(QfcCollectionController controller, string name, object value)
{
FieldInfo field = typeof(QfcCollectionController).GetField(name, NonPublicInstance);
field
.Should()
.NotBeNull(because: "field '" + name + "' must exist on QfcCollectionController");
field.SetValue(controller, value);
}

/// <summary>
/// Reads a non-public instance field from <paramref name="controller"/>, asserting first
/// that the field exists.
/// </summary>
internal static object GetField(QfcCollectionController controller, string name)
{
FieldInfo field = typeof(QfcCollectionController).GetField(name, NonPublicInstance);
field
.Should()
.NotBeNull(because: "field '" + name + "' must exist on QfcCollectionController");
return field.GetValue(controller);
}

/// <summary>
/// Returns the <see cref="FieldInfo"/> for a non-public instance field, asserting first that
/// it exists. Used by structural tests that assert on a field's declared
/// <see cref="FieldInfo.FieldType"/> rather than on its value.
/// </summary>
internal static FieldInfo GetFieldInfo(string name)
{
FieldInfo field = typeof(QfcCollectionController).GetField(name, NonPublicInstance);
field
.Should()
.NotBeNull(because: "field '" + name + "' must exist on QfcCollectionController");
return field;
}

/// <summary>
/// Sets a non-public <em>static</em> field on <see cref="QfcCollectionController"/>,
/// asserting first that the field exists. Static state is process-wide, so every test that
/// touches it must reset it in <c>[TestInitialize]</c> and <c>[TestCleanup]</c> to keep the
/// suite order-independent.
/// </summary>
internal static void SetStaticField(string name, object value)
{
FieldInfo field = typeof(QfcCollectionController).GetField(name, NonPublicStatic);
field
.Should()
.NotBeNull(
because: "static field '" + name + "' must exist on QfcCollectionController"
);
field.SetValue(null, value);
}

/// <summary>
/// Reads a non-public <em>static</em> field from <see cref="QfcCollectionController"/>,
/// asserting first that the field exists.
/// </summary>
internal static object GetStaticField(string name)
{
FieldInfo field = typeof(QfcCollectionController).GetField(name, NonPublicStatic);
field
.Should()
.NotBeNull(
because: "static field '" + name + "' must exist on QfcCollectionController"
);
return field.GetValue(null);
}

/// <summary>
/// Invokes a non-public instance method by name, asserting first that the method exists.
/// </summary>
/// <remarks>
/// Reflection wraps any exception thrown by the target in a
/// <see cref="TargetInvocationException"/>, so a caller asserting on the underlying failure
/// must assert on the inner exception.
/// </remarks>
internal static object InvokeNonPublic(
QfcCollectionController controller,
string name,
params object[] args
)
{
MethodInfo method = typeof(QfcCollectionController).GetMethod(name, NonPublicInstance);
method
.Should()
.NotBeNull(because: "method '" + name + "' must exist on QfcCollectionController");
return method.Invoke(controller, args);
}

/// <summary>
/// Allocates a <see cref="QfcCollectionController"/> without running its
/// WinForms-dependent constructor, and injects the one field that must never be left at its
/// uninitialized default.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="FormatterServices.GetUninitializedObject(Type)"/> bypasses field
/// initializers, so <c>_digits</c> is <c>0</c> rather than its declared <c>1</c>. The
/// <c>Digits</c> getter then sets <c>_digitRefreshNeeded = true</c>, which routes
/// <c>RegisterNavigation</c> into the WinForms-bound <c>SetVisualDigits</c> path. Every
/// builder that can reach <c>RegisterNavigation</c>, <c>UnregisterNavigation</c>, or
/// <c>RemoveSpecificControlGroupAsync</c> therefore injects <c>_digits = 1</c>, unless the
/// test specifically wants that path.
/// </para>
/// <para>
/// Note that <c>_moveMonitor</c> and <c>BackgroundLoadingTasks</c> are also field
/// initializers and are therefore <c>null</c> on the returned instance. Tests that need
/// them must inject them explicitly.
/// </para>
/// </remarks>
internal static QfcCollectionController CreateUninitializedController()
{
QfcCollectionController controller = (QfcCollectionController)
FormatterServices.GetUninitializedObject(typeof(QfcCollectionController));
SetField(controller, "_digits", 1);
return controller;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private static QfcCollectionController CreateController(out Mock<IOlObjects> moc
var mockHomeController = new Mock<IFilerHomeController>();
mockHomeController.SetupGet(h => h.KeyboardHandler).Returns(mockKeyboardHandler.Object);

var mockParent = new Mock<IFilerFormController>();
var mockParent = new Mock<IQfcFormController>();

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Expand Down
Loading
Loading