Skip to content

Commit dd4b891

Browse files
authored
Merge pull request #79 from managedcode/codex/consolidated-13-15-76
Consolidate workbench slices, provider groundwork, and PR 76 fixes
2 parents b23194b + a266e3d commit dd4b891

File tree

44 files changed

+719
-506
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+719
-506
lines changed

AGENTS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,18 @@ For this app:
143143
- the repo-root lowercase `.editorconfig` is the source of truth for formatting, naming, style, and analyzer severity
144144
- local and CI build commands must pass `-warnaserror`; warnings are not an acceptable "green" build state in this repository
145145
- do not run parallel `dotnet` or `MSBuild` work that shares the same checkout, target outputs, or NuGet package cache; the multi-target Uno app must build serially in CI to avoid `Uno.Resizetizer` file-lock failures
146+
- do not commit user-specific local paths, usernames, or machine-specific identifiers in tests, docs, snapshots, or fixtures; use neutral synthetic values so the repo stays portable and does not leak personal machine details
146147
- quality gates should prefer analyzer-backed build failures over separate one-off CI tools; for overloaded methods and maintainability drift, enable build-time analyzers such as `CA1502` instead of adding a formatting-only gate
147148
- `Directory.Build.props` owns the shared analyzer and warning policy for future projects
148149
- `Directory.Packages.props` owns centrally managed package versions
149150
- `global.json` pins the .NET SDK and Uno SDK version used by the app and tests
150151
- `DotPilot/DotPilot.csproj` keeps `GenerateDocumentationFile=true` with `CS1591` suppressed so `IDE0005` stays enforceable in CI across all target frameworks without inventing command-line-only build flags
151152
- architecture work must keep a vertical-slice shape: each feature owns its contracts, orchestration, and tests behind clear boundaries instead of growing a shared horizontal service layer
152153
- keep the Uno app project presentation-only; domain, runtime host, orchestration, integrations, and persistence code must live in separate class-library projects so UI composition does not mix with feature implementation
154+
- structure both `DotPilot.Tests` and `DotPilot.UITests` by vertical slice and explicit harness boundaries; do not keep test files in one flat project-root pile
155+
- GitHub is the backlog, not the product: use issues and PRs only to drive task scope and traceability, and never copy GitHub issue text, labels, workflow language, or tracker metadata into production code, runtime snapshots, or user-facing UI
156+
- Desktop responsiveness is a product requirement: avoid synchronous probe, filesystem, network, or process work on UI-facing construction and navigation paths so the app stays fast and immediately reactive
157+
- Do not invent a repo-specific product framing such as "workbench" unless the active issue or feature spec explicitly uses it; implement the app features described in the backlog instead of turning internal implementation language into the product narrative
153158
- GitHub Actions workflows must use descriptive names and filenames that reflect their purpose; do not use a generic `ci.yml` catch-all because build validation and release automation are separate operator flows
154159
- GitHub Actions must be split into at least one validation workflow for normal builds/tests and one release workflow for CI-driven version resolution, release-note generation, desktop publishing, and GitHub Release publication
155160
- meaningful GitHub review comments must be evaluated and fixed when they still apply even if the original PR was closed; closed review threads are not a reason to ignore valid engineering feedback
@@ -302,6 +307,7 @@ Local `AGENTS.md` files may tighten these values, but they must not loosen them
302307
- Hardcoded values are forbidden.
303308
- String literals are forbidden in implementation code. Declare them once as named constants, enums, configuration entries, or dedicated value objects, then reuse those symbols.
304309
- Avoid magic literals. Extract shared values into constants, enums, configuration, or dedicated types.
310+
- Backlog metadata does not belong in product code: issue numbers, PR numbers, review language, and planning terminology must never appear in production runtime models, diagnostics, or user-facing text unless the feature explicitly exposes source-control metadata.
305311
- Design boundaries so real behaviour can be tested through public interfaces.
306312
- For `.NET`, the repo-root `.editorconfig` is the source of truth for formatting, naming, style, and analyzer severity.
307313
- Use nested `.editorconfig` files when they serve a clear subtree-specific purpose. Do not let IDE defaults, pipeline flags, and repo config disagree.
@@ -360,6 +366,7 @@ Ask first:
360366
- Installing stale, non-canonical, or non-`mcaf-*` skills into the repo-local agent skill directory.
361367
- Moving root governance out of the repository root.
362368
- Mixing multiple `.NET` test frameworks in the active solution without a documented migration plan.
369+
- Running build, test, or verification commands for file-only structural reorganizations when the user explicitly asked for folder cleanup without behavior changes.
363370
- Adding fallback paths or alternate harnesses that only make failures disappear in tests while the primary product path remains broken.
364371
- Switching desktop Uno pages into stacked or mobile-style responsive layouts during resize work unless the user explicitly asks for a different composition; desktop pages must stay desktop-first and protect geometry through sizing constraints instead.
365372
- Adding extra UI-test orchestration complexity when the actual goal is simply to run the tests and get an honest pass or fail result.

DotPilot.Core/Features/ToolchainCenter/ToolchainCenterContracts.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace DotPilot.Core.Features.ToolchainCenter;
44

55
public sealed record ToolchainCenterWorkstreamDescriptor(
66
int IssueNumber,
7-
string IssueLabel,
7+
string SectionLabel,
88
string Name,
99
string Summary);
1010

@@ -37,7 +37,7 @@ public sealed record ToolchainPollingDescriptor(
3737

3838
public sealed record ToolchainProviderSnapshot(
3939
int IssueNumber,
40-
string IssueLabel,
40+
string SectionLabel,
4141
ProviderDescriptor Provider,
4242
string ExecutablePath,
4343
string InstalledVersion,

DotPilot.Runtime/Features/RuntimeFoundation/DeterministicAgentRuntimeClient.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ namespace DotPilot.Runtime.Features.RuntimeFoundation;
88
public sealed class DeterministicAgentRuntimeClient : IAgentRuntimeClient
99
{
1010
private const string ApprovalKeyword = "approval";
11-
private const string DeterministicProviderDisplayName = "Deterministic Runtime Client";
1211
private const string PlanSummary =
1312
"Planned the runtime foundation flow with contracts first, then communication, host lifecycle, and orchestration.";
1413
private const string ExecuteSummary =
@@ -35,7 +34,7 @@ public ValueTask<Result<AgentTurnResult>> ExecuteAsync(AgentTurnRequest request,
3534
Result<AgentTurnResult>.Fail(
3635
RuntimeCommunicationProblems.ProviderUnavailable(
3736
request.ProviderStatus,
38-
DeterministicProviderDisplayName)));
37+
ProviderToolchainNames.DeterministicClientDisplayName)));
3938
}
4039

4140
return ValueTask.FromResult(request.Mode switch
@@ -77,12 +76,12 @@ private static ArtifactDescriptor CreateArtifact(SessionId sessionId, string art
7776
{
7877
return new ArtifactDescriptor
7978
{
80-
Id = ArtifactId.New(),
79+
Id = RuntimeFoundationDeterministicIdentity.CreateArtifactId(sessionId, artifactName),
8180
SessionId = sessionId,
8281
Name = artifactName,
8382
Kind = artifactKind,
8483
RelativePath = artifactName,
85-
CreatedAt = DateTimeOffset.UtcNow,
84+
CreatedAt = RuntimeFoundationDeterministicIdentity.ArtifactCreatedAt,
8685
};
8786
}
8887
}

DotPilot.Runtime/Features/RuntimeFoundation/ProviderToolchainProbe.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ status is ProviderConnectionStatus.Available
2626

2727
return new ProviderDescriptor
2828
{
29-
Id = ProviderId.New(),
29+
Id = RuntimeFoundationDeterministicIdentity.CreateProviderId(commandName),
3030
DisplayName = displayName,
3131
CommandName = commandName,
3232
Status = status,
Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,44 @@
11
using DotPilot.Core.Features.ControlPlaneDomain;
22
using DotPilot.Core.Features.RuntimeFoundation;
3-
using DotPilot.Runtime.Features.ToolchainCenter;
4-
53
namespace DotPilot.Runtime.Features.RuntimeFoundation;
64

75
public sealed class RuntimeFoundationCatalog : IRuntimeFoundationCatalog
86
{
97
private const string EpicSummary =
10-
"Issue #12 is staged into isolated contracts, communication, host, and orchestration slices so the Uno workbench can stay presentation-only.";
8+
"Runtime contracts, host sequencing, and orchestration seams stay isolated so the Uno app can remain presentation-only.";
9+
private const string EpicLabelValue = "LOCAL RUNTIME READINESS";
1110
private const string DeterministicProbePrompt =
1211
"Summarize the runtime foundation readiness for a local-first session that may require approval.";
1312
private const string DeterministicClientStatusSummary = "Always available for in-repo and CI validation.";
13+
private const string DomainModelLabel = "DOMAIN";
1414
private const string DomainModelName = "Domain contracts";
1515
private const string DomainModelSummary =
1616
"Typed identifiers and durable agent, session, fleet, provider, and runtime contracts live outside the Uno app.";
17+
private const string CommunicationLabel = "CONTRACTS";
1718
private const string CommunicationName = "Communication contracts";
1819
private const string CommunicationSummary =
1920
"Public result and problem boundaries are isolated so later provider and orchestration slices share one contract language.";
21+
private const string HostLabel = "HOST";
2022
private const string HostName = "Embedded host";
2123
private const string HostSummary =
2224
"The Orleans host integration point is sequenced behind dedicated runtime contracts instead of being baked into page code.";
25+
private const string OrchestrationLabel = "ORCHESTRATION";
2326
private const string OrchestrationName = "Orchestration runtime";
2427
private const string OrchestrationSummary =
2528
"Agent Framework integration is prepared as a separate slice that can plug into the embedded host without reshaping the UI layer.";
29+
private readonly IReadOnlyList<ProviderDescriptor> _providers;
30+
31+
public RuntimeFoundationCatalog() => _providers = Array.AsReadOnly(CreateProviders());
2632

2733
public RuntimeFoundationSnapshot GetSnapshot()
2834
{
2935
return new(
30-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.EmbeddedAgentRuntimeHostEpic),
36+
EpicLabelValue,
3137
EpicSummary,
3238
ProviderToolchainNames.DeterministicClientDisplayName,
3339
DeterministicProbePrompt,
3440
CreateSlices(),
35-
CreateProviders());
41+
_providers);
3642
}
3743

3844
private static IReadOnlyList<RuntimeSliceDescriptor> CreateSlices()
@@ -41,46 +47,44 @@ private static IReadOnlyList<RuntimeSliceDescriptor> CreateSlices()
4147
[
4248
new(
4349
RuntimeFoundationIssues.DomainModel,
44-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.DomainModel),
50+
DomainModelLabel,
4551
DomainModelName,
4652
DomainModelSummary,
4753
RuntimeSliceState.ReadyForImplementation),
4854
new(
4955
RuntimeFoundationIssues.CommunicationContracts,
50-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.CommunicationContracts),
56+
CommunicationLabel,
5157
CommunicationName,
5258
CommunicationSummary,
5359
RuntimeSliceState.Sequenced),
5460
new(
5561
RuntimeFoundationIssues.EmbeddedOrleansHost,
56-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.EmbeddedOrleansHost),
62+
HostLabel,
5763
HostName,
5864
HostSummary,
5965
RuntimeSliceState.Sequenced),
6066
new(
6167
RuntimeFoundationIssues.AgentFrameworkRuntime,
62-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.AgentFrameworkRuntime),
68+
OrchestrationLabel,
6369
OrchestrationName,
6470
OrchestrationSummary,
6571
RuntimeSliceState.Sequenced),
6672
];
6773
}
6874

69-
private static IReadOnlyList<ProviderDescriptor> CreateProviders()
75+
private static ProviderDescriptor[] CreateProviders()
7076
{
7177
return
7278
[
7379
new ProviderDescriptor
7480
{
75-
Id = ProviderId.New(),
81+
Id = RuntimeFoundationDeterministicIdentity.CreateProviderId(ProviderToolchainNames.DeterministicClientCommandName),
7682
DisplayName = ProviderToolchainNames.DeterministicClientDisplayName,
7783
CommandName = ProviderToolchainNames.DeterministicClientCommandName,
7884
Status = ProviderConnectionStatus.Available,
7985
StatusSummary = DeterministicClientStatusSummary,
8086
RequiresExternalToolchain = false,
8187
},
82-
.. ToolchainProviderSnapshotFactory.Create(TimeProvider.System.GetUtcNow())
83-
.Select(snapshot => snapshot.Provider),
8488
];
8589
}
8690
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Security.Cryptography;
2+
using System.Text;
3+
using DotPilot.Core.Features.ControlPlaneDomain;
4+
5+
namespace DotPilot.Runtime.Features.RuntimeFoundation;
6+
7+
internal static class RuntimeFoundationDeterministicIdentity
8+
{
9+
private const string ArtifactSeedPrefix = "runtime-foundation-artifact";
10+
private const string ProviderSeedPrefix = "runtime-foundation-provider";
11+
private const string SeedSeparator = "|";
12+
13+
public static DateTimeOffset ArtifactCreatedAt { get; } = new(2026, 3, 13, 0, 0, 0, TimeSpan.Zero);
14+
15+
public static ArtifactId CreateArtifactId(SessionId sessionId, string artifactName)
16+
{
17+
return new(CreateGuid(string.Concat(ArtifactSeedPrefix, SeedSeparator, sessionId, SeedSeparator, artifactName)));
18+
}
19+
20+
public static ProviderId CreateProviderId(string commandName)
21+
{
22+
return new(CreateGuid(string.Concat(ProviderSeedPrefix, SeedSeparator, commandName)));
23+
}
24+
25+
private static Guid CreateGuid(string seed)
26+
{
27+
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(seed));
28+
Span<byte> guidBytes = stackalloc byte[16];
29+
hash[..guidBytes.Length].CopyTo(guidBytes);
30+
guidBytes[7] = (byte)((guidBytes[7] & 0x0F) | 0x80);
31+
guidBytes[8] = (byte)((guidBytes[8] & 0x3F) | 0x80);
32+
return new Guid(guidBytes);
33+
}
34+
}

DotPilot.Runtime/Features/ToolchainCenter/ToolchainCenterCatalog.cs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,31 @@ namespace DotPilot.Runtime.Features.ToolchainCenter;
44

55
public sealed class ToolchainCenterCatalog : IToolchainCenterCatalog, IDisposable
66
{
7+
private const string EpicLabelValue = "PRE-SESSION READINESS";
78
private const string EpicSummary =
8-
"Issue #14 keeps provider installation, auth, diagnostics, configuration, and polling visible before the first live session.";
9+
"Provider installation, launch checks, authentication, configuration, and refresh state stay visible before the first live session.";
10+
private const string UiWorkstreamLabel = "SURFACE";
911
private const string UiWorkstreamName = "Toolchain Center UI";
1012
private const string UiWorkstreamSummary =
1113
"The settings shell exposes a first-class desktop Toolchain Center with provider cards, detail panes, and operator actions.";
14+
private const string DiagnosticsWorkstreamLabel = "DIAGNOSTICS";
1215
private const string DiagnosticsWorkstreamName = "Connection diagnostics";
1316
private const string DiagnosticsWorkstreamSummary =
1417
"Launch, connection, resume, tool access, and auth diagnostics stay attributable before live work starts.";
18+
private const string ConfigurationWorkstreamLabel = "CONFIGURATION";
1519
private const string ConfigurationWorkstreamName = "Secrets and environment";
1620
private const string ConfigurationWorkstreamSummary =
1721
"Provider secrets, local overrides, and non-secret environment configuration stay visible without leaking values.";
22+
private const string PollingWorkstreamLabel = "POLLING";
1823
private const string PollingWorkstreamName = "Background polling";
1924
private const string PollingWorkstreamSummary =
20-
"Version and auth readiness refresh in the background so the workbench can surface stale state early.";
25+
"Version and auth readiness refresh in the background so the app can surface stale state early.";
2126
private readonly TimeProvider _timeProvider;
2227
private readonly CancellationTokenSource _disposeTokenSource = new();
2328
private readonly PeriodicTimer? _pollingTimer;
2429
private readonly Task _pollingTask;
2530
private ToolchainCenterSnapshot _snapshot;
31+
private int _disposeState;
2632

2733
public ToolchainCenterCatalog()
2834
: this(TimeProvider.System, startBackgroundPolling: true)
@@ -46,13 +52,29 @@ public ToolchainCenterCatalog(TimeProvider timeProvider, bool startBackgroundPol
4652
}
4753
}
4854

49-
public ToolchainCenterSnapshot GetSnapshot() => _snapshot;
55+
public ToolchainCenterSnapshot GetSnapshot() => Volatile.Read(ref _snapshot);
5056

5157
public void Dispose()
5258
{
59+
if (Interlocked.Exchange(ref _disposeState, 1) != 0)
60+
{
61+
return;
62+
}
63+
5364
_disposeTokenSource.Cancel();
5465
_pollingTimer?.Dispose();
66+
67+
try
68+
{
69+
_pollingTask.GetAwaiter().GetResult();
70+
}
71+
catch (OperationCanceledException)
72+
{
73+
// Expected during shutdown.
74+
}
75+
5576
_disposeTokenSource.Dispose();
77+
GC.SuppressFinalize(this);
5678
}
5779

5880
private async Task PollAsync()
@@ -66,21 +88,25 @@ private async Task PollAsync()
6688
{
6789
while (await _pollingTimer.WaitForNextTickAsync(_disposeTokenSource.Token))
6890
{
69-
_snapshot = EvaluateSnapshot();
91+
Volatile.Write(ref _snapshot, EvaluateSnapshot());
7092
}
7193
}
7294
catch (OperationCanceledException)
7395
{
7496
// Expected during app shutdown.
7597
}
98+
catch (ObjectDisposedException) when (_disposeTokenSource.IsCancellationRequested)
99+
{
100+
// Expected when the timer is disposed during shutdown.
101+
}
76102
}
77103

78104
private ToolchainCenterSnapshot EvaluateSnapshot()
79105
{
80106
var evaluatedAt = _timeProvider.GetUtcNow();
81107
var providers = ToolchainProviderSnapshotFactory.Create(evaluatedAt);
82108
return new(
83-
ToolchainCenterIssues.FormatIssueLabel(ToolchainCenterIssues.ToolchainCenterEpic),
109+
EpicLabelValue,
84110
EpicSummary,
85111
CreateWorkstreams(),
86112
providers,
@@ -95,22 +121,22 @@ private static IReadOnlyList<ToolchainCenterWorkstreamDescriptor> CreateWorkstre
95121
[
96122
new(
97123
ToolchainCenterIssues.ToolchainCenterUi,
98-
ToolchainCenterIssues.FormatIssueLabel(ToolchainCenterIssues.ToolchainCenterUi),
124+
UiWorkstreamLabel,
99125
UiWorkstreamName,
100126
UiWorkstreamSummary),
101127
new(
102128
ToolchainCenterIssues.ConnectionDiagnostics,
103-
ToolchainCenterIssues.FormatIssueLabel(ToolchainCenterIssues.ConnectionDiagnostics),
129+
DiagnosticsWorkstreamLabel,
104130
DiagnosticsWorkstreamName,
105131
DiagnosticsWorkstreamSummary),
106132
new(
107133
ToolchainCenterIssues.ProviderConfiguration,
108-
ToolchainCenterIssues.FormatIssueLabel(ToolchainCenterIssues.ProviderConfiguration),
134+
ConfigurationWorkstreamLabel,
109135
ConfigurationWorkstreamName,
110136
ConfigurationWorkstreamSummary),
111137
new(
112138
ToolchainCenterIssues.BackgroundPolling,
113-
ToolchainCenterIssues.FormatIssueLabel(ToolchainCenterIssues.BackgroundPolling),
139+
PollingWorkstreamLabel,
114140
PollingWorkstreamName,
115141
PollingWorkstreamSummary),
116142
];

0 commit comments

Comments
 (0)