Skip to content

Status and delivery follow the live overlay, not the roster primary - #1064

Merged
jim80net merged 4 commits into
mainfrom
agent/overlay-status-delivery
Sep 6, 2026
Merged

Status and delivery follow the live overlay, not the roster primary#1064
jim80net merged 4 commits into
mainfrom
agent/overlay-status-delivery

Conversation

@jim80net

@jim80net jim80net commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Operator summary

Before

After a switch of the coding agent program in a pane (the harness — Grok, Codex, and so on), the live desk ran the overlay recorded in active-harness.json, but flotilla status and message delivery still treated the roster's declared primary as that program. Screen chrome from the overlay was fed to the wrong driver, so a clean idle overlay session could show as errored.

Change

Status, send, watch assessment, and the dash now resolve the overlay surface first (the active-harness file), and only fall back to the roster primary when that overlay is missing or unreadable.

After

A desk whose overlay is codex while the roster still says grok is advertised and delivered as codex. A generic node pane falls back to the overlay driver, not the stale roster driver.

Identifiers

Summary

workspace.EffectiveSurface is the shared overlay-first seam. agentSurface delegates to it. Status JSON, CLI send (resolveSendLiveDriver), watch assessment, and dash board/control/goals pass that configured surface into display and ResolveLiveDriver.

A missing or torn overlay is fail-safe: routing stays on the roster primary so a live desk is never unroutable.

Tracks #1063.

Tests

Generic fixtures only (backend / frontend / xo): roster grok + overlay codex + live command node → driver and status surface codex. Coverage in internal/workspace, cmd/flotilla status/send/watch, and dash board tests.


Summary by cubic

Makes flotilla status, send, watch assessment, and dash routing follow the live overlay harness (active-harness.json) instead of the roster's declared primary, so a switched desk is advertised and delivered as the harness actually running. Fixes #1063.

Bug Fixes

  • Status JSON, CLI send, watch assessment, and dash board/control/goals now resolve the overlay surface first via workspace.EffectiveSurface, shared as a collection through workspace.EffectiveSurfaces.
  • A generic node pane now falls back to the overlay driver, not the stale roster driver.
  • Missing, unreadable, or unregistered-surface overlays fall back to the roster primary, so a live desk is never unroutable.
  • Dash goals fold surfaces in roster order, so case-insensitive name collisions resolve deterministically (last roster entry wins).

Written for commit de185b5. Summary will update on new commits.

Review in cubic

A switched desk keeps its roster primary while active-harness.json names the live harness. Status advertised the roster field, and flotilla send passed that field into ResolveLiveDriver, so a generic node pane fell back to the stale driver and classified overlay chrome as errored.

workspace.EffectiveSurface is the shared overlay-first seam. Status, CLI send, watch assessment, and dash routing all use it. Generic node + overlay codex + roster grok tests cover assess, send, and status JSON.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/flotilla/main.go">

<violation number="1" location="cmd/flotilla/main.go:491">
P2: When `active-harness.json` contains a parseable but unregistered `surface` and the pane command is generic (`node`), this call makes `flotilla send` fail instead of falling back to the roster. Validate the overlay surface or treat unregistered values as torn before resolving the live driver.</violation>
</file>

<file name="cmd/flotilla/send_test.go">

<violation number="1" location="cmd/flotilla/send_test.go:35">
P3: TestResolveSendLiveDriverGenericNodeUsesOverlay duplicates the send-check block of TestGenericNodeAssessAndSendUseOverlayNotRoster in watch_test.go — same agent, overlay, cfg, inputs, and assertions. If the send-path coverage is meant to live in send_test.go, drop the duplicated send block from watch_test.go so the same scenario is asserted once.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread cmd/flotilla/main.go
return fmt.Errorf("agent %q: read live command: %w", agentName, err)
}
drv, liveSurface, _, err := surface.ResolveLiveDriver(agent.Surface, pane, func(string) (string, error) { return liveCommand, nil })
drv, liveSurface, err := resolveSendLiveDriver(cfg, agentName, pane, liveCommand)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When active-harness.json contains a parseable but unregistered surface and the pane command is generic (node), this call makes flotilla send fail instead of falling back to the roster. Validate the overlay surface or treat unregistered values as torn before resolving the live driver.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/flotilla/main.go, line 491:

<comment>When `active-harness.json` contains a parseable but unregistered `surface` and the pane command is generic (`node`), this call makes `flotilla send` fail instead of falling back to the roster. Validate the overlay surface or treat unregistered values as torn before resolving the live driver.</comment>

<file context>
@@ -488,7 +488,7 @@ func cmdSend(args []string) error {
 		return fmt.Errorf("agent %q: read live command: %w", agentName, err)
 	}
-	drv, liveSurface, _, err := surface.ResolveLiveDriver(agent.Surface, pane, func(string) (string, error) { return liveCommand, nil })
+	drv, liveSurface, err := resolveSendLiveDriver(cfg, agentName, pane, liveCommand)
 	if err != nil {
 		return fmt.Errorf("agent %q: %w", agentName, err)
</file context>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIX-landed at ca83f559b4d6a3212455f583974a04371eba9cde.

workspace.EffectiveSurface treats an unregistered overlay Surface as torn (surface.Registered) and fail-safes to the roster. Coverage: TestEffectiveSurfaceUnregisteredOverlayFallsBackToRoster, TestResolveSendLiveDriverUnregisteredOverlayFallsBackToRoster (generic node + overlay not-a-driver + roster grok → send driver grok, no error). Shared seam, not send-only.

Comment thread internal/dash/readmodel.go Outdated
Comment thread cmd/flotilla/send_test.go

// --mirror and --no-mirror together is a clear error (caught right after flag parse,
// before any roster load or tmux delivery).
func TestResolveSendLiveDriverGenericNodeUsesOverlay(t *testing.T) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: TestResolveSendLiveDriverGenericNodeUsesOverlay duplicates the send-check block of TestGenericNodeAssessAndSendUseOverlayNotRoster in watch_test.go — same agent, overlay, cfg, inputs, and assertions. If the send-path coverage is meant to live in send_test.go, drop the duplicated send block from watch_test.go so the same scenario is asserted once.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/flotilla/send_test.go, line 35:

<comment>TestResolveSendLiveDriverGenericNodeUsesOverlay duplicates the send-check block of TestGenericNodeAssessAndSendUseOverlayNotRoster in watch_test.go — same agent, overlay, cfg, inputs, and assertions. If the send-path coverage is meant to live in send_test.go, drop the duplicated send block from watch_test.go so the same scenario is asserted once.</comment>

<file context>
@@ -30,6 +32,19 @@ func TestShouldMirror(t *testing.T) {
 
 // --mirror and --no-mirror together is a clear error (caught right after flag parse,
 // before any roster load or tmux delivery).
+func TestResolveSendLiveDriverGenericNodeUsesOverlay(t *testing.T) {
+	root := t.TempDir()
+	writeAgentOverlay(t, root, "backend", `{"slot":"fallback-0","surface":"codex"}`)
</file context>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIX-landed at ca83f559b4d6a3212455f583974a04371eba9cde.

Watch coverage is TestGenericNodeAssessUsesOverlayNotRoster (assess only). Send coverage stays in cmd/flotilla/send_test.go (TestResolveSendLiveDriverGenericNodeUsesOverlay). Duplicate send-check block removed.

Comment thread cmd/flotilla/send_test.go
Comment thread cmd/flotilla/status.go Outdated
An overlay Surface that is not a registered driver is fail-safe to the roster at workspace.EffectiveSurface (shared seam for status, send, watch, dash). BuildBoard and buildStatusJSON take pre-resolved surfaces so they stay pure. Send coverage for unregistered overlay + generic node lives in send_test.go.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 13 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/flotilla/status.go">

<violation number="1" location="cmd/flotilla/status.go:190">
P3: statusSurfaces and loadAgentSurfaces are identical helpers (iterate roster agents, map each to EffectiveSurface) added in two different call layers, and a third copy already exists in goals.agentSurfacesFromRoster. Extract one helper into internal/workspace (e.g. EffectiveSurfaces(cfg)) and call it from status, dash server, and goals so overlay-first resolution stays in one place.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread cmd/flotilla/status.go
// statusSurfaces resolves overlay-first configured surfaces for every roster
// agent. The command layer (cmdStatus, notify) calls this so buildStatusJSON
// stays pure.
func statusSurfaces(cfg *roster.Config) map[string]string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: statusSurfaces and loadAgentSurfaces are identical helpers (iterate roster agents, map each to EffectiveSurface) added in two different call layers, and a third copy already exists in goals.agentSurfacesFromRoster. Extract one helper into internal/workspace (e.g. EffectiveSurfaces(cfg)) and call it from status, dash server, and goals so overlay-first resolution stays in one place.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/flotilla/status.go, line 190:

<comment>statusSurfaces and loadAgentSurfaces are identical helpers (iterate roster agents, map each to EffectiveSurface) added in two different call layers, and a third copy already exists in goals.agentSurfacesFromRoster. Extract one helper into internal/workspace (e.g. EffectiveSurfaces(cfg)) and call it from status, dash server, and goals so overlay-first resolution stays in one place.</comment>

<file context>
@@ -179,9 +184,23 @@ func summarizeStatusItems(items []statusItem) utilization.Summary {
+// statusSurfaces resolves overlay-first configured surfaces for every roster
+// agent. The command layer (cmdStatus, notify) calls this so buildStatusJSON
+// stays pure.
+func statusSurfaces(cfg *roster.Config) map[string]string {
+	if cfg == nil {
+		return nil
</file context>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIX-landed at 8cbd684d3c75ef6d56b35b49d3935ad11a33047b.

workspace.EffectiveSurfaces([]NamedSurface) is the shared seam (no roster import). statusSurfaces, loadAgentSurfaces, and agentSurfacesFromRoster call it. Goals still lowercases keys and skips empty after that map. Overlay-first semantics unchanged. Generic-fixture coverage: TestEffectiveSurfacesGenericFixturesOverlayFirst.

statusSurfaces, loadAgentSurfaces, and agentSurfacesFromRoster all iterated roster agents into EffectiveSurface. workspace.EffectiveSurfaces takes name+roster-surface pairs with no roster import. Goals still lowercases keys and skips empty after the shared map. Overlay-first semantics unchanged.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread internal/dash/goals.go
agentSurfacesFromRoster ranged the EffectiveSurfaces map, so case-insensitive name collisions were map-iteration order. Fold cfg.Agents in roster order, look up raw[a.Name], then ToLower. Last roster duplicate wins stably. Generic-fixture test: Backend/grok then backend/codex → backend=codex.
@jim80net
jim80net merged commit befa9f1 into main Sep 6, 2026
6 checks passed
@jim80net
jim80net deleted the agent/overlay-status-delivery branch September 6, 2026 02:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

status/delivery must classify against active-harness overlay, not roster primary

1 participant