fix(dashboard): repair undefined spacing tokens, settings-search gaps, and census/browser-lane test drift

Dashboard bare-run repair, census/token cluster. Real product bugs: the
Command Center activity panel (FN-8866) and structural-mail badge
(FN-8872) referenced undefined --space-* tokens, zeroing their
gaps/padding — mapped to the defined named scale; the settings search
index lagged FN-8829/FN-9021 additions and FN-8855's requiredChecks
entry had no scroll anchor (now a SettingsTextRow). Test-side: the
theme census learns FN-8730's intentional midnight theme, and the
Chromium touch-resize suite self-gates with describe.runIf per the
sibling browser-lane convention (CI/FUSION_BROWSER_SMOKE_REQUIRE still
fail loudly; all 62 tests still run where Chromium exists).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-08-15 22:13:17 -07:00
parent 9f5f981e33
commit 9673f15c11
14 changed files with 82 additions and 22 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix collapsed Command Center activity spacing and mailbox badge padding; make recommendation and required-checks settings searchable.
category: fix
dev: AgentActivityPanel.css used an undefined numeric `--space-1/2/3` scale (FN-8866) and MailboxStructuralItem.css referenced undefined `--space-2xs` (FN-8872), zeroing gaps/padding — mapped to the defined named token scale. Settings search index gains `maxRecommendationsPerTask` (FN-8829) and `recommendationMailboxNoticeEnabled` (FN-9021); MergeSection's `requiredChecks` row now uses the `SettingsTextRow` primitive so the FN-8855 search entry actually scroll-anchors.

View File

@@ -1,11 +1,12 @@
/* FNXC:StructuralMail 2026-08-09-09:09: Reports and approval references need a compact structural treatment without changing ordinary mail. */
/* FNXC:StructuralMail 2026-08-16-05:07: FN-8872 referenced undefined --space-2xs, which invalidated the badge padding to 0. --space-2xs is intentionally undefined (see space-token-defined.test.ts); use the established calc(var(--space-xs) / 2) half-step for the compact block padding instead. */
.mailbox-kind-badge {
border-radius: var(--radius-sm);
display: inline-flex;
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
margin-inline-start: var(--space-xs);
padding: var(--space-2xs) var(--space-xs);
padding: calc(var(--space-xs) / 2) var(--space-xs);
}
.mailbox-kind-badge--report {

View File

@@ -4,7 +4,8 @@ import type { ColorTheme } from "@fusion/core";
import { ThemeSelector } from "../ThemeSelector";
// FNXC:Theme 2026-07-16-14:30: FN-8146 pins the historical Settings-grid set, including restored shadcn-mono, so a removal from COLOR_THEMES cannot make the all-themes checks pass circularly.
const EXPECTED_THEME_IDS = ['default', 'ocean', 'forest', 'sunset', 'zen', 'berry', 'high-contrast', 'industrial', 'monochrome', 'slate', 'ash', 'air', 'graphite', 'silver', 'solarized', 'factory', 'factory-mono', 'ayu', 'one-dark', 'nord', 'dracula', 'gruvbox', 'tokyo-night', 'catppuccin-mocha', 'github-dark', 'everforest', 'rose-pine', 'kanagawa', 'night-owl', 'palenight', 'monokai-pro', 'slime', 'brutalist', 'neon-city', 'parchment', 'terminal', 'glass', 'glass-silver', 'horizon', 'vitesse', 'outrun', 'snazzy', 'porple', 'espresso', 'mars', 'poimandres', 'ember', 'rust', 'copper', 'foundry', 'carbon', 'sandstone', 'lagoon', 'frost', 'lavender', 'neon-bloom', 'sepia', 'cobalt', 'clay', 'moss', 'aurora', 'calm', 'dawn', 'sage', 'factory-dark', 'factory-light', 'shadcn', 'shadcn-ember', 'shadcn-custom', 'shadcn-blue', 'shadcn-green', 'shadcn-red', 'shadcn-purple', 'shadcn-pink', 'shadcn-orange', 'shadcn-yellow', 'shadcn-mono', 'shadcn-mono-red', 'shadcn-mono-blue', 'shadcn-mono-green', 'shadcn-mono-purple', 'shadcn-mono-pink', 'shadcn-mono-orange', 'shadcn-mono-yellow', 'shadcn-black', 'shadcn-gray', 'shadcn-gray-blue'] as const;
// FNXC:Theme 2026-08-16-05:07: FN-8730 (5931e10345) added the Midnight preset; the census gains "midnight" between sage and factory-dark to match COLOR_THEMES order.
const EXPECTED_THEME_IDS = ['default', 'ocean', 'forest', 'sunset', 'zen', 'berry', 'high-contrast', 'industrial', 'monochrome', 'slate', 'ash', 'air', 'graphite', 'silver', 'solarized', 'factory', 'factory-mono', 'ayu', 'one-dark', 'nord', 'dracula', 'gruvbox', 'tokyo-night', 'catppuccin-mocha', 'github-dark', 'everforest', 'rose-pine', 'kanagawa', 'night-owl', 'palenight', 'monokai-pro', 'slime', 'brutalist', 'neon-city', 'parchment', 'terminal', 'glass', 'glass-silver', 'horizon', 'vitesse', 'outrun', 'snazzy', 'porple', 'espresso', 'mars', 'poimandres', 'ember', 'rust', 'copper', 'foundry', 'carbon', 'sandstone', 'lagoon', 'frost', 'lavender', 'neon-bloom', 'sepia', 'cobalt', 'clay', 'moss', 'aurora', 'calm', 'dawn', 'sage', 'midnight', 'factory-dark', 'factory-light', 'shadcn', 'shadcn-ember', 'shadcn-custom', 'shadcn-blue', 'shadcn-green', 'shadcn-red', 'shadcn-purple', 'shadcn-pink', 'shadcn-orange', 'shadcn-yellow', 'shadcn-mono', 'shadcn-mono-red', 'shadcn-mono-blue', 'shadcn-mono-green', 'shadcn-mono-purple', 'shadcn-mono-pink', 'shadcn-mono-orange', 'shadcn-mono-yellow', 'shadcn-black', 'shadcn-gray', 'shadcn-gray-blue'] as const;
function renderedThemeIds(listbox: HTMLElement) {
return within(listbox).getAllByRole("option").map((option) => {

View File

@@ -1,10 +1,13 @@
/*
FNXC:CommandCenterAgentActivity 2026-08-10-01:54:
The activity panel keeps live and timeline controls touch-usable at desktop and mobile widths with existing design tokens. Timeline rows are manual windows, so this stylesheet deliberately adds no nested scroll owner.
FNXC:CommandCenterAgentActivity 2026-08-16-05:07:
FN-8866 shipped with the undefined numeric --space-1/2/3 scale, which invalidated every gap/padding to 0 and collapsed the panel's spacing. This design system defines only the named --space-xs/sm/md/lg/xl/2xl scale (styles.css), so the numeric references map to xs=4px, sm=8px, md=12px per CommandCenter.token-validity.css.test.ts.
*/
.cc-agent-activity {
display: grid;
gap: var(--space-3);
gap: var(--space-md);
min-inline-size: 0;
}
@@ -13,7 +16,7 @@ The activity panel keeps live and timeline controls touch-usable at desktop and
.cc-agent-activity-target {
display: flex;
align-items: center;
gap: var(--space-2);
gap: var(--space-sm);
min-inline-size: 0;
}
@@ -35,15 +38,15 @@ The activity panel keeps live and timeline controls touch-usable at desktop and
.cc-agent-activity-list {
display: grid;
gap: var(--space-2);
gap: var(--space-sm);
}
.cc-agent-activity-row {
display: flex;
align-items: center;
gap: var(--space-2);
gap: var(--space-sm);
min-inline-size: 0;
padding: var(--space-2);
padding: var(--space-sm);
border: 0;
border-radius: var(--radius-sm);
background: transparent;
@@ -63,7 +66,7 @@ button.cc-agent-activity-row:focus-visible {
.cc-agent-activity-copy {
display: grid;
gap: var(--space-1);
gap: var(--space-xs);
min-inline-size: 0;
overflow-wrap: anywhere;
}
@@ -93,6 +96,6 @@ button.cc-agent-activity-row:focus-visible {
@media (max-height: 480px) and (max-width: 768px) {
.cc-agent-activity {
gap: var(--space-2);
gap: var(--space-sm);
}
}

View File

@@ -76,7 +76,8 @@ function cssRule(css: string, selector: string) {
}
// FNXC:Theme 2026-07-16-14:30: FN-8146 pins the historical Settings-grid set, including restored shadcn-mono, so a removal from COLOR_THEMES cannot make the all-themes checks pass circularly.
const EXPECTED_THEME_IDS = ['default', 'ocean', 'forest', 'sunset', 'zen', 'berry', 'high-contrast', 'industrial', 'monochrome', 'slate', 'ash', 'air', 'graphite', 'silver', 'solarized', 'factory', 'factory-mono', 'ayu', 'one-dark', 'nord', 'dracula', 'gruvbox', 'tokyo-night', 'catppuccin-mocha', 'github-dark', 'everforest', 'rose-pine', 'kanagawa', 'night-owl', 'palenight', 'monokai-pro', 'slime', 'brutalist', 'neon-city', 'parchment', 'terminal', 'glass', 'glass-silver', 'horizon', 'vitesse', 'outrun', 'snazzy', 'porple', 'espresso', 'mars', 'poimandres', 'ember', 'rust', 'copper', 'foundry', 'carbon', 'sandstone', 'lagoon', 'frost', 'lavender', 'neon-bloom', 'sepia', 'cobalt', 'clay', 'moss', 'aurora', 'calm', 'dawn', 'sage', 'factory-dark', 'factory-light', 'shadcn', 'shadcn-ember', 'shadcn-custom', 'shadcn-blue', 'shadcn-green', 'shadcn-red', 'shadcn-purple', 'shadcn-pink', 'shadcn-orange', 'shadcn-yellow', 'shadcn-mono', 'shadcn-mono-red', 'shadcn-mono-blue', 'shadcn-mono-green', 'shadcn-mono-purple', 'shadcn-mono-pink', 'shadcn-mono-orange', 'shadcn-mono-yellow', 'shadcn-black', 'shadcn-gray', 'shadcn-gray-blue'] as const;
// FNXC:Theme 2026-08-16-05:07: FN-8730 (5931e10345) added the Midnight preset; the census gains "midnight" between sage and factory-dark to match COLOR_THEMES order.
const EXPECTED_THEME_IDS = ['default', 'ocean', 'forest', 'sunset', 'zen', 'berry', 'high-contrast', 'industrial', 'monochrome', 'slate', 'ash', 'air', 'graphite', 'silver', 'solarized', 'factory', 'factory-mono', 'ayu', 'one-dark', 'nord', 'dracula', 'gruvbox', 'tokyo-night', 'catppuccin-mocha', 'github-dark', 'everforest', 'rose-pine', 'kanagawa', 'night-owl', 'palenight', 'monokai-pro', 'slime', 'brutalist', 'neon-city', 'parchment', 'terminal', 'glass', 'glass-silver', 'horizon', 'vitesse', 'outrun', 'snazzy', 'porple', 'espresso', 'mars', 'poimandres', 'ember', 'rust', 'copper', 'foundry', 'carbon', 'sandstone', 'lagoon', 'frost', 'lavender', 'neon-bloom', 'sepia', 'cobalt', 'clay', 'moss', 'aurora', 'calm', 'dawn', 'sage', 'midnight', 'factory-dark', 'factory-light', 'shadcn', 'shadcn-ember', 'shadcn-custom', 'shadcn-blue', 'shadcn-green', 'shadcn-red', 'shadcn-purple', 'shadcn-pink', 'shadcn-orange', 'shadcn-yellow', 'shadcn-mono', 'shadcn-mono-red', 'shadcn-mono-blue', 'shadcn-mono-green', 'shadcn-mono-purple', 'shadcn-mono-pink', 'shadcn-mono-orange', 'shadcn-mono-yellow', 'shadcn-black', 'shadcn-gray', 'shadcn-gray-blue'] as const;
function renderedThemeIds(listbox: HTMLElement) {
return within(listbox).getAllByRole("option").map((option) => {

View File

@@ -23,6 +23,35 @@ export const generalSearchEntries: SettingsSearchEntry[] = [
helpFallback: "Report a bug, send feedback, share an idea, or get help from Fusion.",
keywords: ["bug", "feedback", "idea", "help", "support"],
},
{
/*
FNXC:SettingsSearch 2026-08-16-05:07:
FN-8829 (d450dbe971) added the recommendations cap descriptor row without an
index entry; the search-index drift guard caught the gap. Labels/help mirror
the section's t() copy verbatim.
*/
sectionId: "general",
key: "maxRecommendationsPerTask",
labelKey: "settings.general.maxRecommendationsPerTask",
labelFallback: "Maximum recommendations per task",
helpKey: "settings.general.maxRecommendationsPerTaskHelp",
helpFallback: "Default: 3. Set 0 to disable recommendations; choose a whole number from 1 to 20 to cap each completed task.",
keywords: ["suggestions", "follow-up", "cap", "limit"],
},
{
/*
FNXC:SettingsSearch 2026-08-16-05:07:
FN-9021 (ea53cbd4ff) added the mailbox-notice toggle without an index entry;
same drift-guard gap as above.
*/
sectionId: "general",
key: "recommendationMailboxNoticeEnabled",
labelKey: "settings.general.recommendationMailboxNoticeEnabled",
labelFallback: "Recommendation mailbox notices",
helpKey: "settings.general.recommendationMailboxNoticeEnabledHelp",
helpFallback: "Default: enabled. When a completed task captures recommendations, send a summary to your mailbox. Turning this off does not change whether recommendations are captured.",
keywords: ["notifications", "mail", "inbox", "suggestions"],
},
{
sectionId: "general",
key: "taskPrefix",

View File

@@ -5,6 +5,7 @@ import { fetchGitRemoteBranches } from "../../../api";
import { MovedSettingsStub } from "./MovedSettingsStub";
import { SettingsSelectRow } from "../SettingsSelectRow";
import { SettingsNumberRow } from "../SettingsNumberRow";
import { SettingsTextRow } from "../SettingsTextRow";
import { SettingsHelpTip } from "../SettingsHelpTip";
import type { SectionBaseProps } from "./context";
/*
@@ -279,19 +280,28 @@ export function MergeSection({ form, setForm, integrationBranchOptions, integrat
</select>
</div>
{form.mergeStrategy === "pull-request" && <>
<div className="form-group">
<div className="settings-field-label-row">
<label htmlFor="requiredChecks">{t("settings.merge.requiredChecks", "Required pull-request checks")}</label>
<SettingsHelpTip settingKey="requiredChecks">{t("settings.merge.requiredChecksHelp", "No default — unset. Comma-separated check names match GitHub exactly (case-sensitive). Leaving this empty uses GitHub required-status checks only; a named check that never reports blocks the merge.")}</SettingsHelpTip>
</div>
<input id="requiredChecks" className="input" value={requiredChecksInput} onChange={(event) => {
const rawValue = event.target.value;
{/*
FNXC:SettingsSearch 2026-08-16-05:07:
FN-8855 (d59c1b162f) indexed requiredChecks in MergeSection.search.ts but rendered it as a
bespoke label/input, so the search result had no `data-settings-key` anchor to scroll to and
the drift guard flagged the entry as stale. The row composes the typed SettingsTextRow like
its descriptor-row neighbors: same label/help copy, same comma-list parsing on change.
*/}
<SettingsTextRow
descriptor={{
key: "requiredChecks",
label: t("settings.merge.requiredChecks", "Required pull-request checks"),
help: t("settings.merge.requiredChecksHelp", "No default — unset. Comma-separated check names match GitHub exactly (case-sensitive). Leaving this empty uses GitHub required-status checks only; a named check that never reports blocks the merge."),
}}
value={requiredChecksInput}
onChange={(nextValue) => {
const rawValue = nextValue ?? "";
setRequiredChecksInput(rawValue);
const requiredChecks = resolveRequiredCheckNames({ requiredChecks: rawValue.split(",") });
emittedRequiredCheckNames.current = requiredChecks;
setForm((current) => ({ ...current, requiredChecks: requiredChecks.length > 0 ? requiredChecks : undefined }));
}}/>
</div>
}}
/>
<div className="form-group">
<div className="settings-field-label-row">
<label htmlFor="githubNativeAutoMerge" className="checkbox-label">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -23,8 +23,16 @@ FNXC:TaskDetailTitle 2026-08-05-19:18:
FN-8806's acceptance evidence is real Chromium geometry across production hosts. A skipped browser
lane can neither observe ResizeObserver delivery nor reject the original flicker, so fail discovery
explicitly instead of allowing a green suite without this required rendering-engine regression.
FNXC:TaskDetailTitle 2026-08-16-05:07:
The hard failure applies only where the dedicated dashboard-browser-touch lane is required to run:
CI, or an explicit FUSION_BROWSER_SMOKE_REQUIRE=1 opt-in (the same flag scripts/browser-layout-smoke.mjs
uses for its --require-browser mode). A bare local run on a Chromium-less machine follows the sibling
planning-browser-e2e.test.ts convention and self-gates via describe.runIf(executablePath) instead of
failing discovery — the module-load throw was breaking unrelated local runs that merely collected this file.
*/
if (!executablePath) {
const browserRequired = Boolean(process.env.CI) || process.env.FUSION_BROWSER_SMOKE_REQUIRE === "1";
if (!executablePath && browserRequired) {
throw new Error(
"[task-modal-touch-resize] Chromium is required for task-title stability coverage; set FUSION_BROWSER_SMOKE_BROWSER or CHROME_BIN.",
);
@@ -164,12 +172,12 @@ Browser CDP gestures are required because jsdom cannot resolve CSS hit targets.
both production resize paths and sends CSS-pixel touch input through Chromium so elementFromPoint,
pointer capture, persistence, and header-drag isolation use the same browser input path.
*/
describe("Task modal tablet touch resize browser regression", () => {
describe.runIf(executablePath)("Task modal tablet touch resize browser regression", () => {
let server: ViteDevServer; let browser: Browser; let baseUrl = "";
beforeAll(async () => {
server = await createServer({ root: process.cwd(), server: { host: "127.0.0.1", port: 0, watch: null }, logLevel: "error" });
await server.listen(); baseUrl = server.resolvedUrls?.local[0] ?? "";
browser = await chromium.launch({ executablePath, headless: true, ...(process.env.CI ? { args: ["--no-sandbox", "--disable-dev-shm-usage"] } : {}) });
browser = await chromium.launch({ executablePath: executablePath as string, headless: true, ...(process.env.CI ? { args: ["--no-sandbox", "--disable-dev-shm-usage"] } : {}) });
}, 30_000);
afterAll(async () => {
await browser?.close();