FN-8255: hide inherited-default oversight eye

Suppress transient task-card oversight eyes for unconfigured inherited autonomous workflows.

- Reuse the meaningful oversight badge gate for planner-overseer eye rendering.
- Cover aggregate, selected-workflow, explicit override, and mobile states.
- Document the inherited-default suppression and add a patch changeset.

Files changed:
 .changeset/fn-8255-card-eye-inherited-default-oversight.md |  7 ++
 docs/dashboard-guide.md                            |  3 +-
 packages/dashboard/app/components/TaskCard.tsx     | 21 +++---
 .../__tests__/TaskCard.oversight.test.tsx          | 78 +++++++++++++++++++---
 4 files changed, 88 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-8255

Fusion-Task-Lineage: 2c48f96f-9f06-4def-b7bb-1020e8505d7b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-18 01:53:14 -07:00
parent e18c3b4584
commit d88e4f9c90
4 changed files with 88 additions and 21 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Hide the task-card overseer eye when a workflow only uses the default (unconfigured) oversight level.
category: fix
dev: TaskCard's showPlannerOverseerStateBadge now reuses showOversightBadge, so the transient eye follows the same FN-7539 inherited-default suppression as the oversight-level badge — an autonomous tier reached purely by workflow inheritance (no explicit per-task/workflow override) renders no eye even with a stale non-idle plannerOverseerState. FN-8221/FN-8239/FN-8251 guards are unchanged.

View File

@@ -234,10 +234,11 @@ Features:
- Task cards show a read-only **oversight-level badge** (`Observe`, `Steer`, or `Auto-recovery`) in the meta-badges cluster reflecting the effective planner-oversight level, but only when oversight is *meaningfully configured* — an explicit per-task override (including an explicit `autonomous` override), or a resolved workflow/effective tier of `observe`/`steer` (`data-testid="card-oversight-badge"`). A card that merely **inherits** the schema default `autonomous` tier (no per-task override, no non-default workflow tier) renders no badge and no empty `.card-meta-badges` shell. The badge is also absent when the effective level is explicitly "off", **and** while an inherited (no per-task-override) workflow tier is still being resolved (in flight or not yet fetched) — it never shows a guessed default during that window.
<!-- FNXC:PlannerOversight 2026-07-04-HH:MM: FN-7542 removed the FN-7516 active-overseer-state ("Executor") indicator described above as unwanted per-card noise — it fired on nearly every in-progress card. The oversight-level badge documented above is unaffected. -->
<!-- FNXC:PlannerOversight 2026-07-11-00:00: FN-7592 reintroduced a compact active-overseer state indicator as an Eye glyph instead of a wide text badge, using the engine-provided transient plannerOverseerState rather than locally guessing from task fields. -->
<!-- FNXC:PlannerOversight 2026-07-18-01:35: FN-8255 requires the transient card Eye to use the same meaningfully-configured gate as the level badge. A workflow declaration-default autonomous tier reached purely by inheritance (no explicit task override) is suppressed even with a stale non-idle runtime snapshot; the Eye and otherwise-empty card-header-badges shell remain absent. -->
<!-- FNXC:PlannerOversight 2026-07-17-15:50: FN-8251 requires selected-workflow cards to resolve inherited oversight from their trusted board workflow ID when aggregate workflowBadge metadata is absent. Identity-less, pending, failed, and malformed inherited resolution fails closed: the Eye and otherwise-empty card-header-badges wrapper appear only after active effective oversight is positively resolved. -->
<!-- FNXC:TaskRevert 2026-07-16-00:00: FN-8066 adds durable source-task revert provenance to the shared board/List TaskCard footer. -->
- Completed and archived task cards show a compact **Reverted** footer chip after a clean or already-reverted git outcome has persisted the source task's revert marker; conflicts, AI undo tasks, and revert PRs awaiting merge do not show it.
- Task cards show a compact **planner-overseer eye badge** (`data-testid="planner-overseer-state-badge"`) only when the engine reports a non-idle, non-off transient `plannerOverseerState` **and** the task's effective oversight is positively resolved as active. Aggregate cards resolve inherited oversight through their task workflow badge; selected-workflow board cards resolve it through their trusted board workflow ID. Identity-less cards and pending, failed, or malformed inherited workflow-setting loads fail closed. When effective oversight is off or cannot be positively resolved, the eye badge and otherwise-empty `.card-header-badges` wrapper are both absent. This matches—but does not alter—the separate Task Detail `EyeOff` menu trigger. The eye badge is an active-overseer state marker, not a human-read/view indicator: `watching` means passive monitoring, `steering`/`recovering` mean active guidance or recovery is underway, and `awaiting-confirmation` means a human decision is required before the overseer can continue. Hover exposes the composed tooltip with the overseer's reason, watched stage/signal, and pending-confirmation note when present.
- Task cards show a compact **planner-overseer eye badge** (`data-testid="planner-overseer-state-badge"`) only when the engine reports a non-idle, non-off transient `plannerOverseerState` **and** the task's effective oversight is positively resolved as active and meaningfully configured. The eye uses the same inherited-default suppression as the oversight-level badge: a workflow declaration-default `autonomous` tier with no explicit per-task override shows neither eye nor an otherwise-empty `.card-header-badges` wrapper, while an explicit per-task `autonomous` override or resolved `observe`/`steer` tier can show it. Aggregate cards resolve inherited oversight through their task workflow badge; selected-workflow board cards resolve it through their trusted board workflow ID. Identity-less cards and pending, failed, or malformed inherited workflow-setting loads fail closed. When effective oversight is off or cannot be positively resolved, the eye badge and otherwise-empty `.card-header-badges` wrapper are both absent. This matches—but does not alter—the separate Task Detail `EyeOff` menu trigger. The eye badge is an active-overseer state marker, not a human-read/view indicator: `watching` means passive monitoring, `steering`/`recovering` mean active guidance or recovery is underway, and `awaiting-confirmation` means a human decision is required before the overseer can continue. Hover exposes the composed tooltip with the overseer's reason, watched stage/signal, and pending-confirmation note when present.
<!-- FNXC:PlannerOversight 2026-07-04-17:00: FN-7517 adds interactive task-detail planner-overseer controls (quick level change, manual nudge, stop oversight, explain current action) alongside the FN-7516 read-only card badges above. These controls live ONLY in TaskDetailModal, not TaskCard.
FNXC:PlannerOversight 2026-07-05-00:00: FN-7604 collapses the desktop inline cluster (documented below through FN-7545/FN-7546) into the single universal overflow-menu dropdown that FN-7545 originally built for mobile only — the dropdown is now the ONE canonical surface on every viewport, desktop included.

View File

@@ -1702,20 +1702,21 @@ function TaskCardComponent({
!isInheritedDefaultOversightLevel;
/*
* FNXC:PlannerOversight 2026-07-18-00:00:
* FN-8239 requires the transient Eye badge and its header-wrapper gate to
* share the freshly-resolved effective level used by the level badge and
* Task Detail trigger. An inherited task with an unresolved workflow tier
* must wait for that fetch; a stale non-off snapshot must not guess a level,
* show an icon, or leave an empty header-badge shell in that window.
* FNXC:PlannerOversight 2026-07-18-01:30:
* FN-8255 requires the transient Eye badge and its header-wrapper gate to
* reuse the same meaningfully-configured gate as the level badge. A workflow
* declaration-default autonomous tier with no explicit task override is not
* active card oversight, so a stale non-idle snapshot must not show an icon
* or leave an empty header-badge shell; `showOversightBadge` preserves the
* FN-7539 inherited-default suppression and the FN-8239/FN-8251 fail-closed
* resolution guards.
*/
const plannerOverseerState = task.plannerOverseerState;
const showPlannerOverseerStateBadge = Boolean(
plannerOverseerState
showOversightBadge
&& plannerOverseerState
&& plannerOverseerState.state !== "idle"
&& plannerOverseerState.oversightLevel !== "off"
&& (hasTaskOversightOverride || workflowOversightResolved)
&& effectiveOversightLevel !== "off",
&& plannerOverseerState.oversightLevel !== "off",
);
/*

View File

@@ -430,6 +430,55 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
});
});
it.each(["in-progress", "in-review"] as const)("suppresses inherited-default autonomous stale eyes before and after selected-workflow resolution in %s (FN-8255)", async (column) => {
vi.mocked(fetchWorkflowSettingValues).mockResolvedValueOnce({
stored: {},
effective: { plannerOversightLevel: "autonomous" },
orphaned: [],
});
renderCard(staleSnapshot(column), { planningWorkflowId: `selected-inherited-default-${column}`, projectId: "project-8255" });
// The selected workflow's declaration default is not a meaningful oversight
// configuration, so a stale runtime snapshot cannot create an eye or shell.
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
expect(screen.queryByTestId("card-header-badges")).toBeNull();
await waitFor(() => {
expect(fetchWorkflowSettingValues).toHaveBeenCalledWith(`selected-inherited-default-${column}`, "project-8255");
});
await waitFor(() => {
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
expect(screen.queryByTestId("card-header-badges")).toBeNull();
});
});
/*
* FNXC:PlannerOversight 2026-07-18-01:32:
* FN-8255 requires every TaskCard provider to suppress a stale Eye for an
* autonomous workflow declaration default without a task override. The
* aggregate workflow-badge path must match selected-workflow board behavior
* and leave no eye element with a title or aria-label behind.
*/
it("suppresses the inherited-default autonomous stale eye for the aggregate workflow-badge provider (FN-8255)", async () => {
vi.mocked(fetchWorkflowSettingValues).mockResolvedValueOnce({
stored: {},
effective: { plannerOversightLevel: "autonomous" },
orphaned: [],
});
const { container } = renderCard(staleSnapshot("in-progress"), {
workflowBadge: { workflowId: "aggregate-inherited-default", workflowName: "All workflows" },
});
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
expect(screen.queryByTestId("card-header-badges")).toBeNull();
await waitFor(() => {
expect(fetchWorkflowSettingValues).toHaveBeenCalledWith("aggregate-inherited-default", undefined);
});
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
expect(screen.queryByTestId("card-header-badges")).toBeNull();
expect(container.querySelector(".card-planner-overseer-state[title][aria-label]")).toBeNull();
});
it.each([
["rejected", () => vi.mocked(fetchWorkflowSettingValues).mockRejectedValueOnce(new Error("unavailable"))],
["missing", () => vi.mocked(fetchWorkflowSettingValues).mockResolvedValueOnce({ stored: {}, effective: {}, orphaned: [] })],
@@ -461,13 +510,20 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
expect(screen.getByTestId("planner-overseer-state-badge")).toBeTruthy();
});
it("keeps an explicit per-task autonomous override authoritative for the stale eye", () => {
renderCard({ ...staleSnapshot("in-progress"), plannerOversightLevel: "autonomous" });
expect(screen.getByTestId("planner-overseer-state-badge")).toBeTruthy();
});
it.each([
["aggregate", { workflowBadge: { workflowId: "aggregate-active", workflowName: "Aggregate active" } }],
["selected workflow", { planningWorkflowId: "selected-active" }],
])("renders the eye only after positively resolved active oversight for %s cards", async (_surface, props) => {
["aggregate observe", "observe", { workflowBadge: { workflowId: "aggregate-observe", workflowName: "Aggregate observe" } }],
["aggregate steer", "steer", { workflowBadge: { workflowId: "aggregate-steer", workflowName: "Aggregate steer" } }],
["selected workflow steer", "steer", { planningWorkflowId: "selected-steer" }],
] as const)("renders the eye only after positively resolved active %s oversight", async (_surface, level, props) => {
vi.mocked(fetchWorkflowSettingValues).mockResolvedValueOnce({
stored: { plannerOversightLevel: "steer" },
effective: { plannerOversightLevel: "steer" },
stored: { plannerOversightLevel: level },
effective: { plannerOversightLevel: level },
orphaned: [],
});
renderCard(staleSnapshot("in-progress"), props);
@@ -513,18 +569,20 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
});
it("keeps selected-workflow suppression at the 375px mobile viewport", async () => {
it("keeps selected-workflow inherited-default suppression at the 375px mobile viewport", async () => {
Object.defineProperty(window, "innerWidth", { value: 375, configurable: true });
vi.mocked(fetchWorkflowSettingValues).mockResolvedValueOnce({
stored: { plannerOversightLevel: "off" },
effective: { plannerOversightLevel: "off" },
stored: {},
effective: { plannerOversightLevel: "autonomous" },
orphaned: [],
});
renderCard(staleSnapshot("in-review"), { planningWorkflowId: "mobile-off" });
const { container } = renderCard(staleSnapshot("in-review"), { planningWorkflowId: "mobile-inherited-default" });
await waitFor(() => expect(fetchWorkflowSettingValues).toHaveBeenCalledWith("mobile-off", undefined));
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
await waitFor(() => expect(fetchWorkflowSettingValues).toHaveBeenCalledWith("mobile-inherited-default", undefined));
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
expect(screen.queryByTestId("card-header-badges")).toBeNull();
expect(container.querySelector(".card-planner-overseer-state[title][aria-label]")).toBeNull();
});
});