Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
80 changes: 80 additions & 0 deletions .ai/rules/feature-completeness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
applyTo: "**/*"
---

# Feature Completeness

Build success proves compilation. It proves nothing about behaviour. These standards exist because code that compiles cleanly can still be silently broken — callbacks wired but never triggered, UI elements that advertise features that do not exist, actions that return without doing anything or telling anyone why.

## Build ≠ Behaviour

> **"Build succeeded" is never a signal that work is complete.**

Compilation verifies types and syntax. It does not verify that a button click actually fires, that a registered handler is ever called, that a feature works as documented, or that a user-facing interaction produces the expected result.

When work cannot be verified at runtime — because there is no test harness, the environment is unavailable, or the UI cannot be exercised programmatically — say so explicitly. Do not present unverified behaviour as confirmed.

## Complete Implementation Chains

Every user-facing feature is a chain: a trigger connects to a handler connects to an action. All links in the chain must exist before the feature is considered done.

**Common failure modes:**

- A button, keyboard shortcut, or menu item is documented and visible — but no handler exists for it
- A handler is registered (event, callback, delegate) — but nothing ever calls it
- An action is implemented — but no trigger connects it to user input
- An interface or abstract method is declared — but left as a no-op or stub and never noticed

**The rule:** When you add any one part of a chain, locate and verify all the other parts. If they do not exist, either create them in the same change or explicitly flag them as missing.

Chains to audit in this codebase:
- UI label / key hint → key handler case → trigger method → registered callback → executing action
- Interface method / abstract member → all implementations → all call sites
- Event subscription → event firing → observable side effect

## Document–Code Consistency

Everything the user can read — UI hints, help text, comments, XML docs, error messages — is a promise about what the code does. Broken promises are bugs.

**Rule:** Any text visible to a user that describes a behaviour, shortcut, or feature must have a corresponding implementation. If the implementation does not exist, remove the text. If the text is wrong, fix it. Never leave documentation and code in disagreement.

Examples:
- A tooltip says "Press R to replay" → `ConsoleKey.R` must have a handler
- An XML doc says "Throws X when Y" → the code must throw X when Y
- A README says "Supports Z format" → Z must be supported

## No Silent Failures

When a user-triggered operation cannot proceed because a precondition is not met, always surface feedback. A method that silently returns accomplishes nothing except making the user believe the feature is broken.

**Rule:** Every early-return guard in a user-facing operation must explain why it returned. Use status messages, error indicators, or log output — whatever is appropriate for the context. Never return silently.

```csharp
// ❌ User sees nothing, concludes the feature is broken
if (selected is null) return;

// ✅ User sees "Select a row first"
if (selected is null) { ShowHint("Select a row first"); return; }
```

This applies equally to:
- User input handlers that silently drop input
- API endpoints that return empty 200s instead of 404s
- Background jobs that swallow exceptions
- Validation that fails without reporting what was invalid

## Carry-Forward Integrity

When extending existing code, do not assume it is complete. Scaffold, boilerplate, and copy-pasted patterns frequently have missing links — stubs, unimplemented handlers, wired-but-never-called callbacks. The fact that existing code was not flagged as broken does not mean it works.

**Rule:** When touching an existing feature, trace the full implementation chain — trigger → handler → action — and verify each link. Any gap you find is a pre-existing bug. Either fix it in the same change or leave an explicit note.

## Verification Checklist

Before marking any feature complete, confirm:

- [ ] Every user-visible description of a behaviour has a corresponding implementation
- [ ] Every registered handler/callback/listener is called by something
- [ ] Every trigger (button, shortcut, event) connects to a handler
- [ ] Every early return in a user-facing path provides user feedback
- [ ] If runtime behaviour could not be verified, that is stated explicitly
1 change: 1 addition & 0 deletions .claude/rules/feature-completeness.md
1 change: 1 addition & 0 deletions .github/instructions/feature-completeness.instructions.md
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageVersion Include="MongoDB.Driver" Version="3.8.1" />
<PackageVersion Include="Spectre.Console" Version="0.55.2" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.55.0" />
<PackageVersion Include="SharpConsoleUI" Version="2.4.61" />
<PackageVersion Include="Grpc.Net.Client" Version="2.80.0" />
<PackageVersion Include="System.Text.Json" Version="10.0.8" />
<!-- Source Generator -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class and_running_state_has_quarantined_value : Specification

void Because() => _isQuarantined = ListObserversCommand.IsQuarantined(new ObserverInformation
{
RunningState = (ObserverRunningState)5
RunningState = ObserverRunningState.Quarantined
});

[Fact] void should_return_true() => _isQuarantined.ShouldBeTrue();
Expand Down
1 change: 1 addition & 0 deletions Source/Cli/Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<PackageReference Include="Cratis.Fundamentals" />
<PackageReference Include="Spectre.Console" />
<PackageReference Include="Spectre.Console.Cli" />
<PackageReference Include="SharpConsoleUI" />
<PackageReference Include="Grpc.Net.Client" />
</ItemGroup>

Expand Down
12 changes: 0 additions & 12 deletions Source/Cli/Commands/Chronicle/Workbench/NavFrame.cs

This file was deleted.

68 changes: 0 additions & 68 deletions Source/Cli/Commands/Chronicle/Workbench/WorkbenchAction.cs

This file was deleted.

40 changes: 40 additions & 0 deletions Source/Cli/Commands/Chronicle/Workbench/WorkbenchApp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using SharpConsoleUI;
using SharpConsoleUI.Configuration;
using SharpConsoleUI.Drivers;
using SharpConsoleUI.Panel;

namespace Cratis.Cli.Commands.Chronicle.Workbench;

/// <summary>
/// Bootstraps and runs the Chronicle Workbench TUI application using SharpConsoleUI.
/// </summary>
/// <param name="dataService">The data service for fetching workbench snapshots.</param>
/// <param name="settings">The workbench command settings.</param>
/// <param name="services">The Chronicle gRPC service clients.</param>
/// <param name="initialData">Pre-fetched data snapshot to populate all views before the first frame is rendered.</param>
/// <param name="state">Persisted workbench state from the previous session.</param>
public class WorkbenchApp(WorkbenchDataService dataService, WorkbenchSettings settings, IServices services, WorkbenchData initialData, WorkbenchState state)
{
/// <summary>
/// Runs the workbench TUI and blocks until the user exits.
/// </summary>
/// <returns>The exit code.</returns>
public int Run()
{
var windowSystem = new ConsoleWindowSystem(
new NetConsoleDriver(RenderMode.Buffer),
options: new ConsoleWindowSystemOptions(
TopPanelConfig: panel => panel.Left(Elements.StatusText(string.Empty)),
BottomPanelConfig: panel => panel.Left(Elements.StatusText(WorkbenchHints.BottomBar))));

windowSystem.PanelStateService.TopStatus = "◆ CHRONICLE WORKBENCH";

var mainWindow = new MainWindow(windowSystem, dataService, settings, services, initialData, state);
windowSystem.AddWindow(mainWindow.Build(), activateWindow: true);

return windowSystem.Run();
}
}
51 changes: 51 additions & 0 deletions Source/Cli/Commands/Chronicle/Workbench/WorkbenchColors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using SColor = SharpConsoleUI.Color;

namespace Cratis.Cli.Commands.Chronicle.Workbench;

/// <summary>
/// SharpConsoleUI-compatible color constants for the workbench — midnight-blue palette with vibrant accents.
/// </summary>
public static class WorkbenchColors
{
/// <summary>Very deep midnight-navy background — the base window color.</summary>
public static readonly SColor Background = new(8, 12, 28, 255);

/// <summary>Deep navy surface — content pane background, slightly lighter than the window.</summary>
public static readonly SColor Surface = new(16, 22, 46, 255);

/// <summary>Primary foreground — blue-tinted white, easy on the eyes on dark backgrounds.</summary>
public static readonly SColor Foreground = new(220, 225, 250, 255);

/// <summary>Primary accent — electric blue, used for highlights, selected items, and borders.</summary>
public static readonly SColor Accent = new(100, 180, 255, 255);

/// <summary>Navigation pane selection background — rich royal blue.</summary>
public static readonly SColor SelectedBg = new(35, 70, 150, 255);

/// <summary>Muted secondary text — perceptible but not distracting.</summary>
public static readonly SColor Muted = new(90, 110, 160, 255);

/// <summary>Success / healthy state — vibrant mint green.</summary>
public static readonly SColor Success = new(100, 220, 130, 255);

/// <summary>Warning state — warm amber, used for the OBSERVATION nav section.</summary>
public static readonly SColor Warning = new(240, 190, 80, 255);

/// <summary>Danger / error state — bright coral-pink for failed partitions and errors.</summary>
public static readonly SColor Danger = new(255, 100, 130, 255);

/// <summary>Teal / cyan accent — used for the EVENTS nav section and event-type borders.</summary>
public static readonly SColor Teal = new(60, 220, 200, 255);

/// <summary>Mauve / violet accent — used for the PROJECTIONS nav section.</summary>
public static readonly SColor Mauve = new(200, 155, 255, 255);

/// <summary>Content pane border — subtle blue-violet, visible but not distracting.</summary>
public static readonly SColor ContentBorder = new(45, 65, 115, 255);

/// <summary>Dim table/panel border color — very subtle dark-blue chrome.</summary>
public static readonly SColor ChromeBorder = new(30, 40, 80, 255);
}
Loading
Loading