mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-12 03:36:59 +00:00
feat: add developer-mode startup and stack hydration timing (#1619)
* feat: add developer-mode startup and stack hydration timing Instrument boot-to-list and detail hydration with commit-aligned milestones, truthful request stages, and destination/gateway debug duration logs so performance work is guided by measurements. * fix: redact stack names and complete hydration request stages Stop logging stack identifiers in containers debug timing, and record state_dispatch (plus detail fetch spans) so copied reports match the advertised stage breakdown.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';
|
||||
import { markMilestone } from '@/lib/hydrationTiming';
|
||||
|
||||
type AppStatus = 'loading' | 'needsSetup' | 'notAuthenticated' | 'mfaChallenge' | 'authenticated';
|
||||
|
||||
@@ -109,6 +110,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
};
|
||||
|
||||
// One-shot boot milestone: the auth gate has resolved to a terminal status
|
||||
// (setup, login, MFA, or authenticated), so the app can leave the splash.
|
||||
useEffect(() => {
|
||||
if (appStatus === 'loading') return;
|
||||
markMilestone('auth_resolved');
|
||||
}, [appStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
checkAuth();
|
||||
const handleUnauthorized = () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import { apiFetch } from '@/lib/api';
|
||||
import { beginNodeSession, markMilestone } from '@/lib/hydrationTiming';
|
||||
import type { Capability } from '@/lib/capabilities';
|
||||
|
||||
export type NodeMode = 'proxy' | 'pilot_agent';
|
||||
@@ -56,6 +57,18 @@ export function NodeProvider({ children }: { children: React.ReactNode }) {
|
||||
const nodeMetaRef = useRef<Map<number, NodeMeta>>(nodeMeta);
|
||||
nodeMetaRef.current = nodeMeta;
|
||||
|
||||
// Open a fresh hydration-timing session the moment the active node id changes,
|
||||
// during render (guarded by a ref to fire once per id). This must precede the
|
||||
// shell's stack-list refresh so its attempt binds to the new node session:
|
||||
// child effects (EditorLayout's node-switch effect that calls refreshStacks)
|
||||
// run before this provider's effects, so an effect here would begin the
|
||||
// session too late and the list milestone would never commit.
|
||||
const hydrationNodeRef = useRef<number | null>(null);
|
||||
if (activeNode && hydrationNodeRef.current !== activeNode.id) {
|
||||
hydrationNodeRef.current = activeNode.id;
|
||||
beginNodeSession(activeNode.id);
|
||||
}
|
||||
|
||||
const fetchNodeMeta = useCallback(async (nodeId: number, force = false) => {
|
||||
const cached = nodeMetaRef.current.get(nodeId);
|
||||
if (cached && !force) {
|
||||
@@ -157,6 +170,13 @@ export function NodeProvider({ children }: { children: React.ReactNode }) {
|
||||
return () => window.removeEventListener('node-not-found', handleNodeNotFound);
|
||||
}, [refreshNodes]);
|
||||
|
||||
// One-shot boot milestone: nodes have finished loading and an active node is
|
||||
// resolved. Deduped by the store, so a later re-run is a no-op.
|
||||
useEffect(() => {
|
||||
if (isLoading || !activeNode) return;
|
||||
markMilestone('nodes_resolved');
|
||||
}, [isLoading, activeNode]);
|
||||
|
||||
const activeNodeMeta = useMemo(() => {
|
||||
if (!activeNode) return null;
|
||||
return nodeMeta.get(activeNode.id) ?? null;
|
||||
|
||||
Reference in New Issue
Block a user