feat(FN-4762): merge fusion/fn-4762

Commits merged:
- merge fusion/fn-4762

Files changed:
AGENTS.md                                       | 25 ++++++++++++++++++++++---
 docs/README.md                                  |  6 ++++++
 packages/core/src/store.ts                      |  4 ++--
 packages/dashboard/app/App.tsx                  | 10 ++++++++--
 packages/dashboard/src/__tests__/github.test.ts |  3 ++-
 5 files changed, 40 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-4762

Fusion-Task-Lineage: 5c7ef3b8-fefc-4cc2-a703-634124d77cde
This commit is contained in:
Fusion (runfusion.ai)
2026-05-17 12:24:45 -07:00
committed by gsxdsm
parent 1543df69a4
commit 01caeb6d88
5 changed files with 39 additions and 7 deletions

View File

@@ -406,9 +406,28 @@ Breakpoints: 768px (primary mobile), 1024px (tablet `min-width: 769px and max-wi
**Safe area:** `max(var(--space-md), env(safe-area-inset-left, 0px))` for notch-aware horizontal padding. **Safe area:** `max(var(--space-md), env(safe-area-inset-left, 0px))` for notch-aware horizontal padding.
### Lazy-loaded heavy views ### Lazy-Loaded Heavy Views
18 views loaded via `React.lazy()` with `<Suspense fallback={null}>`: AgentsView, NodesView, ChatView, MemoryView, DevServerView, InsightsView, DocumentsView, SkillsView, ResearchView, ReliabilityView, EvalsView, TodoView, GoalsView, StashRecoveryView, SetupWizardModal, PluginManager, PiExtensionsManager, AgentDetailView. `prefetchLazyViews()` warms chunks once on mount via `requestIdleCallback`. **Do not make these eager.** These 18 views are lazy-loaded via `React.lazy()` with `<Suspense fallback={null}>`. `prefetchLazyViews()` warms chunks once on mount via `requestIdleCallback`. **Do not make these eager.**
- `AgentsView`
- `NodesView`
- `ChatView`
- `MemoryView`
- `DevServerView`
- `InsightsView`
- `DocumentsView`
- `SkillsView`
- `ResearchView`
- `ReliabilityView`
- `EvalsView`
- `TodoView`
- `GoalsView`
- `StashRecoveryView`
- `SetupWizardModal`
- `PluginManager`
- `PiExtensionsManager`
- `AgentDetailView`
When adding or removing entries, update `packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts` (expected set + count). When adding or removing entries, update `packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts` (expected set + count).

View File

@@ -59,6 +59,7 @@ For a full walkthrough (installation, onboarding, first task, and daily workflow
| [Docker](./docker.md) | Container builds, deployment, and persistence configuration | | [Docker](./docker.md) | Container builds, deployment, and persistence configuration |
| [Code Signing](./CODE_SIGNING.md) | macOS and Windows code signing configuration for release binaries | | [Code Signing](./CODE_SIGNING.md) | macOS and Windows code signing configuration for release binaries |
| [Sandbox Backends](./sandbox.md) | Pluggable sandbox backends for executor command isolation (bubblewrap, spawn-based) | | [Sandbox Backends](./sandbox.md) | Pluggable sandbox backends for executor command isolation (bubblewrap, spawn-based) |
| [Secrets](./secrets.md) | Encrypted secrets storage, per-secret access policies, scopes, and agent tool wiring |
| [Mobile](../MOBILE.md) | Capacitor/PWA mobile development setup and workflow | | [Mobile](../MOBILE.md) | Capacitor/PWA mobile development setup and workflow |
### Plugins ### Plugins
@@ -97,6 +98,11 @@ For a full walkthrough (installation, onboarding, first task, and daily workflow
| [Experiment Session Model](./research/experiment-session-model.md) | Experiment session data model: state transitions, iteration tracking, and persisted run state | | [Experiment Session Model](./research/experiment-session-model.md) | Experiment session data model: state transitions, iteration tracking, and persisted run state |
| [Experiment Session MVP Spec](./research/experiment-session-mvp-spec.md) | MVP specification for the experiment session feature: scope, invariants, and delivery milestones | | [Experiment Session MVP Spec](./research/experiment-session-mvp-spec.md) | MVP specification for the experiment session feature: scope, invariants, and delivery milestones |
| [Sandbox Options Research (FN-4635)](./research/sandbox-options.md) | Pluggable sandbox options research: threat model, backend evaluation, and spawn-based isolation design | | [Sandbox Options Research (FN-4635)](./research/sandbox-options.md) | Pluggable sandbox options research: threat model, backend evaluation, and spawn-based isolation design |
| [Triage Duplicate Detection Postmortem](./triage-duplicate-detection-postmortem.md) | Postmortem on duplicate task detection gaps and scheduler dedup hardening |
| [Multi-Node Runtime Readiness (FN-4814)](./design/fn-4814-multi-node-runtime-readiness.md) | Runtime readiness assessment for multi-node distributed coordination |
| [Distributed Multi-Node Coordination Gap (FN-4819)](./design/fn-4819-distributed-multi-node-coordination-gap.md) | Gap analysis for distributed multi-node agent coordination and cross-node task assignment |
| [Cross-Node Assignment Wake Contract (FN-4824)](./design/fn-4824-cross-node-assignment-wake-contract.md) | Contract specification for cross-node task assignment wake signaling |
| [Multi-Node Coordination Validation Findings (FN-4820)](./findings/fn-4820-multi-node-coordination-validation.md) | Validation findings from multi-node coordination testing and edge-case analysis |
## External Resources ## External Resources

View File

@@ -7299,7 +7299,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
return { moved: false, skipped: "not-merged" }; return { moved: false, skipped: "not-merged" };
} }
if (task.column !== "in-review") { if (task.column !== "in-review") {
this.logger?.warn?.(`[store] applyPrMergedTransition skipped for ${taskId}: column=${task.column}`); storeLog.warn(`[store] applyPrMergedTransition skipped for ${taskId}: column=${task.column}`);
return { moved: false, skipped: "wrong-column" }; return { moved: false, skipped: "wrong-column" };
} }
@@ -7314,7 +7314,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
return { moved: false, skipped: "not-merged" }; return { moved: false, skipped: "not-merged" };
} }
if (freshTask.column !== "in-review") { if (freshTask.column !== "in-review") {
this.logger?.warn?.(`[store] applyPrMergedTransition skipped for ${taskId}: column=${freshTask.column}`); storeLog.warn(`[store] applyPrMergedTransition skipped for ${taskId}: column=${freshTask.column}`);
return { moved: false, skipped: "wrong-column" }; return { moved: false, skipped: "wrong-column" };
} }

View File

@@ -799,9 +799,15 @@ function AppInner() {
const previousCapacityRiskBannerEnabledRef = useRef(capacityRiskBannerEnabled); const previousCapacityRiskBannerEnabledRef = useRef(capacityRiskBannerEnabled);
const previousCapacityRiskTodoThresholdRef = useRef(capacityRiskTodoThreshold); const previousCapacityRiskTodoThresholdRef = useRef(capacityRiskTodoThreshold);
const previousCapacityRiskProjectIdRef = useRef(currentProject?.id); const previousCapacityRiskProjectIdRef = useRef(currentProject?.id);
const capacityRiskSettingsHydratedRef = useRef(false);
useEffect(() => { useEffect(() => {
if (previousCapacityRiskProjectIdRef.current !== currentProject?.id) { if (!settingsLoaded) {
return;
}
if (!capacityRiskSettingsHydratedRef.current || previousCapacityRiskProjectIdRef.current !== currentProject?.id) {
capacityRiskSettingsHydratedRef.current = true;
previousCapacityRiskProjectIdRef.current = currentProject?.id; previousCapacityRiskProjectIdRef.current = currentProject?.id;
previousCapacityRiskBannerEnabledRef.current = capacityRiskBannerEnabled; previousCapacityRiskBannerEnabledRef.current = capacityRiskBannerEnabled;
previousCapacityRiskTodoThresholdRef.current = capacityRiskTodoThreshold; previousCapacityRiskTodoThresholdRef.current = capacityRiskTodoThreshold;
@@ -821,7 +827,7 @@ function AppInner() {
previousCapacityRiskProjectIdRef.current = currentProject?.id; previousCapacityRiskProjectIdRef.current = currentProject?.id;
previousCapacityRiskBannerEnabledRef.current = capacityRiskBannerEnabled; previousCapacityRiskBannerEnabledRef.current = capacityRiskBannerEnabled;
previousCapacityRiskTodoThresholdRef.current = capacityRiskTodoThreshold; previousCapacityRiskTodoThresholdRef.current = capacityRiskTodoThreshold;
}, [capacityRiskBannerEnabled, capacityRiskTodoThreshold, currentProject?.id]); }, [settingsLoaded, capacityRiskBannerEnabled, capacityRiskTodoThreshold, currentProject?.id]);
const skillsEnabled = experimentalFeatures.skillsView === true; const skillsEnabled = experimentalFeatures.skillsView === true;
const nodesEnabled = experimentalFeatures.nodesView === true; const nodesEnabled = experimentalFeatures.nodesView === true;

View File

@@ -325,6 +325,7 @@ describe("GitHubClient", () => {
url: "https://github.com/owner/repo/pull/42", url: "https://github.com/owner/repo/pull/42",
title: "Test PR", title: "Test PR",
state: "OPEN", state: "OPEN",
isDraft: false,
baseRefName: "main", baseRefName: "main",
headRefName: "feature-branch", headRefName: "feature-branch",
}); });
@@ -334,7 +335,7 @@ describe("GitHubClient", () => {
expect(mockRunGhJsonAsync).toHaveBeenCalledWith([ expect(mockRunGhJsonAsync).toHaveBeenCalledWith([
"pr", "view", "42", "pr", "view", "42",
"--repo", "owner/repo", "--repo", "owner/repo",
"--json", "number,url,title,state,baseRefName,headRefName", "--json", "number,url,title,state,isDraft,baseRefName,headRefName",
]); ]);
expect(result.number).toBe(42); expect(result.number).toBe(42);
expect(result.status).toBe("open"); expect(result.status).toBe("open");