Lagebild, Stufe 1: Einheiten als aufklappbare Container (#711)
- TeamOverview.RollUp: je Einheit die Zustaende aller Agenten darin und
darunter, offene Freigaben und der dringlichste Zustand (Urgency:
haengt > Problem > Freigabe > Budget > arbeitet > frei). Gezaehlt wird in
der Instanz, die Seite zeichnet nur.
- /api/team liefert Einheiten und je Agent Einheit und Leitung.
- team.js: Einheiten als Container, zugeklappt als Kasten mit
Zusammenfassung ("6 agents · ⚠ 1 · ✋ 1 · ▶ 2") in der Farbe des
dringlichsten Zustands. Kanten laufen zum sichtbaren Stellvertreter und
werden gebuendelt (×n); was in einer zugeklappten Einheit bleibt, wird
nicht gezeichnet. Bis 15 Agenten alles offen, darueber nur die oberste
Ebene; der Zustand je Instanz im localStorage.
- Seitenleiste fuer Einheiten: Pfad, Ziel, Mitglieder (Leitung zuerst),
Untereinheiten, Auf-/Zuklappen, Link zur Organisation.
- Layout: rectpacking trifft das Seitenverhaeltnis nur grob; es werden
bis zu vier Vorgaben probiert und die zur Flaeche passendste behalten.
- Sprachschluessel en/de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5b8656d969
commit
920d3fed64
@@ -1,5 +1,7 @@
|
||||
using ClawdDotNet.App.Services;
|
||||
using ClawdDotNet.Core.Activity;
|
||||
using ClawdDotNet.Core.Config;
|
||||
using ClawdDotNet.Core.Organisation;
|
||||
using Shouldly;
|
||||
|
||||
namespace ClawdDotNet.App.Tests.Services;
|
||||
@@ -79,4 +81,85 @@ public sealed class TeamOverviewTests
|
||||
public void A_cancelled_run_is_not_a_problem()
|
||||
// Cancelled means a human stopped it — that was a decision, not a fault.
|
||||
=> TeamOverview.Classify([], 0, "Cancelled", TimeSpan.Zero, Now).ShouldBe(TeamState.Idle);
|
||||
|
||||
// ═══════════════════ stage 1: units roll up ═══════════════════
|
||||
|
||||
/// <summary>marketing (lead) › amazon (lead, ads) ; accounting (bookkeeper) ; ceo at the top.</summary>
|
||||
private static OrgChart Shop() => new(
|
||||
[
|
||||
new UnitConfig { Id = "marketing", Name = "Marketing", LeaderAgentId = "marketing-lead" },
|
||||
new UnitConfig { Id = "amazon", Name = "Amazon", ParentId = "marketing", LeaderAgentId = "amazon-lead" },
|
||||
new UnitConfig { Id = "accounting", Name = "Accounting" }
|
||||
],
|
||||
[
|
||||
new AgentConfig { AgentId = "ceo" },
|
||||
new AgentConfig { AgentId = "marketing-lead", UnitId = "marketing" },
|
||||
new AgentConfig { AgentId = "amazon-lead", UnitId = "amazon" },
|
||||
new AgentConfig { AgentId = "ads", UnitId = "amazon" },
|
||||
new AgentConfig { AgentId = "bookkeeper", UnitId = "accounting" }
|
||||
]);
|
||||
|
||||
private static TeamMember Member(string id, string? unit, TeamState state, int approvals = 0)
|
||||
=> new(id, id, "m", state, [], approvals, null, null, null, 0, unit);
|
||||
|
||||
[Fact]
|
||||
public void A_problem_deep_down_shows_on_every_container_above_it()
|
||||
{
|
||||
var units = TeamOverview.RollUp(Shop(),
|
||||
[
|
||||
Member("ceo", null, TeamState.Working),
|
||||
Member("marketing-lead", "marketing", TeamState.Working),
|
||||
Member("amazon-lead", "amazon", TeamState.Idle),
|
||||
Member("ads", "amazon", TeamState.Problem),
|
||||
Member("bookkeeper", "accounting", TeamState.Idle)
|
||||
]);
|
||||
|
||||
var marketing = units.Single(u => u.Id == "marketing");
|
||||
marketing.Agents.ShouldBe(3, "its own lead and the two in amazon below");
|
||||
marketing.Worst.ShouldBe(TeamState.Problem);
|
||||
marketing.States[TeamState.Working].ShouldBe(1);
|
||||
marketing.States[TeamState.Problem].ShouldBe(1);
|
||||
|
||||
units.Single(u => u.Id == "amazon").Worst.ShouldBe(TeamState.Problem);
|
||||
units.Single(u => u.Id == "accounting").Worst.ShouldBe(TeamState.Idle);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Open_approvals_are_added_up_along_the_tree()
|
||||
{
|
||||
var units = TeamOverview.RollUp(Shop(),
|
||||
[
|
||||
Member("marketing-lead", "marketing", TeamState.WaitingForApproval, approvals: 1),
|
||||
Member("ads", "amazon", TeamState.WaitingForApproval, approvals: 2)
|
||||
]);
|
||||
|
||||
units.Single(u => u.Id == "marketing").PendingApprovals.ShouldBe(3);
|
||||
units.Single(u => u.Id == "amazon").PendingApprovals.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_unit_knows_its_path_and_its_lead()
|
||||
{
|
||||
var amazon = TeamOverview.RollUp(Shop(), []).Single(u => u.Id == "amazon");
|
||||
|
||||
amazon.Path.ShouldBe("Marketing / Amazon");
|
||||
amazon.LeaderAgentId.ShouldBe("amazon-lead");
|
||||
amazon.Agents.ShouldBe(0);
|
||||
amazon.Worst.ShouldBe(TeamState.Idle);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stuck_is_the_most_urgent_and_idle_the_least()
|
||||
{
|
||||
var byUrgency = Enum.GetValues<TeamState>().OrderByDescending(TeamOverview.Urgency).ToList();
|
||||
|
||||
byUrgency.First().ShouldBe(TeamState.Stuck);
|
||||
byUrgency.Last().ShouldBe(TeamState.Idle);
|
||||
TeamOverview.Urgency(TeamState.Problem).ShouldBeGreaterThan(TeamOverview.Urgency(TeamState.WaitingForApproval));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Without_units_there_is_nothing_to_roll_up()
|
||||
=> TeamOverview.RollUp(new OrgChart([], [new AgentConfig { AgentId = "a" }]), [Member("a", null, TeamState.Problem)])
|
||||
.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user