mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 04:32:32 +00:00
feat(web): expose EnableIndexDualWrite toggle in admin Global Settings
Closes the operator-facing gap left by M5.d.2: the feature flag was plumbed server-side (AdminConfiguration model + TableConfigRepository Store/Map) but the UI had no way to flip it. Without this change the entire M5.d pipeline was unreachable in production. Wire-up mirrors the existing allowAgentDowngrade toggle (closest analog — also a developer/ops-level bool with caveat copy): - types/adminConfig.ts: add optional enableIndexDualWrite field with a doc comment pointing at Plan §M5.d so future maintainers understand what toggling it does. - AdminConfigContext.tsx: add state + setter, hydrate from GET response (default false), include in save-spread, include in reset fallback, expose via context value + save-useCallback deps. - AdminConfigSettingsSection.tsx: add prop pair and a checkbox-style toggle below the ModernDeployment harmless-IDs input. Copy explains the 5 index tables + the 2h safety-net timer + warns that it should only be flipped after release-gate verification. - SectionGlobalSettings.tsx: destructure + forward the new prop pair. Round-trip is safe: GET (System.Text.Json, camelCase) emits enableIndexDualWrite, UI spreads it back into PUT body, Newtonsoft case-insensitive deserialization binds to EnableIndexDualWrite on the model — so an operator save no longer silently resets the flag. Verification: npx tsc --noEmit clean, vitest suite 168/168 green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,8 @@ interface AdminConfigContextValue {
|
||||
setAllowAgentDowngrade: (value: boolean) => void;
|
||||
modernDeploymentHarmlessEventIds: string;
|
||||
setModernDeploymentHarmlessEventIds: (value: string) => void;
|
||||
enableIndexDualWrite: boolean;
|
||||
setEnableIndexDualWrite: (value: boolean) => void;
|
||||
|
||||
// Diagnostics log paths
|
||||
globalDiagPaths: DiagnosticsLogPath[];
|
||||
@@ -110,6 +112,7 @@ export function AdminConfigProvider({ children }: { children: React.ReactNode })
|
||||
const [opsEventRetentionDays, setOpsEventRetentionDays] = useState(90);
|
||||
const [allowAgentDowngrade, setAllowAgentDowngrade] = useState(false);
|
||||
const [modernDeploymentHarmlessEventIds, setModernDeploymentHarmlessEventIds] = useState("100, 1005");
|
||||
const [enableIndexDualWrite, setEnableIndexDualWrite] = useState(false);
|
||||
|
||||
// Diagnostics Log Paths state
|
||||
const [globalDiagPaths, setGlobalDiagPaths] = useState<DiagnosticsLogPath[]>([]);
|
||||
@@ -162,6 +165,7 @@ export function AdminConfigProvider({ children }: { children: React.ReactNode })
|
||||
setOpsEventRetentionDays(data.opsEventRetentionDays ?? 90);
|
||||
setAllowAgentDowngrade(data.allowAgentDowngrade ?? false);
|
||||
setModernDeploymentHarmlessEventIds(parseHarmlessEventIdsJson(data.modernDeploymentHarmlessEventIdsJson));
|
||||
setEnableIndexDualWrite(data.enableIndexDualWrite ?? false);
|
||||
try {
|
||||
setGlobalDiagPaths(data.diagnosticsGlobalLogPathsJson ? JSON.parse(data.diagnosticsGlobalLogPathsJson) : []);
|
||||
} catch {
|
||||
@@ -255,6 +259,7 @@ export function AdminConfigProvider({ children }: { children: React.ReactNode })
|
||||
opsEventRetentionDays,
|
||||
allowAgentDowngrade,
|
||||
modernDeploymentHarmlessEventIdsJson: serializeHarmlessEventIds(modernDeploymentHarmlessEventIds),
|
||||
enableIndexDualWrite,
|
||||
};
|
||||
|
||||
const response = await authenticatedFetch(api.globalConfig.get(), getAccessToken, {
|
||||
@@ -281,7 +286,7 @@ export function AdminConfigProvider({ children }: { children: React.ReactNode })
|
||||
} finally {
|
||||
setSavingConfig(false);
|
||||
}
|
||||
}, [adminConfig, globalRateLimit, platformStatsBlobSasUrl, collectorIdleTimeoutMinutes, maxSessionWindowHours, maintenanceBlockDurationHours, opsEventRetentionDays, allowAgentDowngrade, modernDeploymentHarmlessEventIds, getAccessToken]);
|
||||
}, [adminConfig, globalRateLimit, platformStatsBlobSasUrl, collectorIdleTimeoutMinutes, maxSessionWindowHours, maintenanceBlockDurationHours, opsEventRetentionDays, allowAgentDowngrade, modernDeploymentHarmlessEventIds, enableIndexDualWrite, getAccessToken]);
|
||||
|
||||
// Reset admin config
|
||||
const handleResetAdminConfig = useCallback(() => {
|
||||
@@ -294,6 +299,7 @@ export function AdminConfigProvider({ children }: { children: React.ReactNode })
|
||||
setOpsEventRetentionDays(adminConfig.opsEventRetentionDays ?? 90);
|
||||
setAllowAgentDowngrade(adminConfig.allowAgentDowngrade ?? false);
|
||||
setModernDeploymentHarmlessEventIds(parseHarmlessEventIdsJson(adminConfig.modernDeploymentHarmlessEventIdsJson));
|
||||
setEnableIndexDualWrite(adminConfig.enableIndexDualWrite ?? false);
|
||||
try {
|
||||
setGlobalDiagPaths(adminConfig.diagnosticsGlobalLogPathsJson ? JSON.parse(adminConfig.diagnosticsGlobalLogPathsJson) : []);
|
||||
} catch {
|
||||
@@ -418,6 +424,7 @@ export function AdminConfigProvider({ children }: { children: React.ReactNode })
|
||||
opsEventRetentionDays, setOpsEventRetentionDays,
|
||||
allowAgentDowngrade, setAllowAgentDowngrade,
|
||||
modernDeploymentHarmlessEventIds, setModernDeploymentHarmlessEventIds,
|
||||
enableIndexDualWrite, setEnableIndexDualWrite,
|
||||
globalDiagPaths, setGlobalDiagPaths, savingDiagPaths,
|
||||
opsAlertRules, setOpsAlertRules,
|
||||
opsAlertTelegramEnabled, setOpsAlertTelegramEnabled,
|
||||
|
||||
@@ -16,6 +16,8 @@ interface AdminConfigSettingsSectionProps {
|
||||
setAllowAgentDowngrade: (value: boolean) => void;
|
||||
modernDeploymentHarmlessEventIds: string;
|
||||
setModernDeploymentHarmlessEventIds: (value: string) => void;
|
||||
enableIndexDualWrite: boolean;
|
||||
setEnableIndexDualWrite: (value: boolean) => void;
|
||||
onSave: () => Promise<void>;
|
||||
onReset: () => void;
|
||||
}
|
||||
@@ -34,6 +36,8 @@ export function AdminConfigSettingsSection({
|
||||
setAllowAgentDowngrade,
|
||||
modernDeploymentHarmlessEventIds,
|
||||
setModernDeploymentHarmlessEventIds,
|
||||
enableIndexDualWrite,
|
||||
setEnableIndexDualWrite,
|
||||
onSave,
|
||||
onReset,
|
||||
}: AdminConfigSettingsSectionProps) {
|
||||
@@ -152,6 +156,34 @@ export function AdminConfigSettingsSection({
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-start space-x-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={enableIndexDualWrite}
|
||||
onChange={(e) => setEnableIndexDualWrite(e.target.checked)}
|
||||
className="mt-1 h-5 w-5 rounded border-indigo-300 dark:border-indigo-600 text-indigo-600 focus:ring-indigo-500"
|
||||
/>
|
||||
<span>
|
||||
<span className="text-indigo-900 dark:text-indigo-100 font-medium">V2 Decision Engine: Enable index-table dual-write</span>
|
||||
<p className="text-sm text-indigo-800 dark:text-gray-300 mt-1">
|
||||
Feature flag for the M5.d secondary-index fan-out. When <strong>off</strong> (default), the V2 telemetry
|
||||
ingest path writes only the primary <code className="text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">Signals</code> and
|
||||
<code className="ml-1 text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">DecisionTransitions</code> tables. When
|
||||
<strong> on</strong>, each committed primary row is also enqueued onto the <code className="text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">telemetry-index-reconcile</code> queue
|
||||
and fanned out into the 5 cross-session index tables
|
||||
(<code className="text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">SessionsByTerminal</code>,
|
||||
<code className="ml-1 text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">SessionsByStage</code>,
|
||||
<code className="ml-1 text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">DeadEndsByReason</code>,
|
||||
<code className="ml-1 text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">ClassifierVerdictsByIdLevel</code>,
|
||||
<code className="ml-1 text-xs bg-indigo-100 dark:bg-indigo-900 dark:text-indigo-200 px-1 rounded">SignalsByKind</code>).
|
||||
A 2h timer re-scans the last 4h as a safety net against queue failures. Flip this on only after the
|
||||
M5.d release-gate tests have been verified in the target environment.
|
||||
</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{adminConfig && (
|
||||
<div className="bg-blue-50 dark:bg-gray-700 border border-blue-200 dark:border-indigo-600 rounded-lg p-3">
|
||||
<div className="flex items-start space-x-2">
|
||||
|
||||
@@ -12,6 +12,7 @@ export function SectionGlobalSettings() {
|
||||
collectorIdleTimeoutMinutes, setCollectorIdleTimeoutMinutes,
|
||||
allowAgentDowngrade, setAllowAgentDowngrade,
|
||||
modernDeploymentHarmlessEventIds, setModernDeploymentHarmlessEventIds,
|
||||
enableIndexDualWrite, setEnableIndexDualWrite,
|
||||
handleSaveAdminConfig, handleResetAdminConfig,
|
||||
} = useAdminConfig();
|
||||
|
||||
@@ -32,6 +33,8 @@ export function SectionGlobalSettings() {
|
||||
setAllowAgentDowngrade={setAllowAgentDowngrade}
|
||||
modernDeploymentHarmlessEventIds={modernDeploymentHarmlessEventIds}
|
||||
setModernDeploymentHarmlessEventIds={setModernDeploymentHarmlessEventIds}
|
||||
enableIndexDualWrite={enableIndexDualWrite}
|
||||
setEnableIndexDualWrite={setEnableIndexDualWrite}
|
||||
onSave={handleSaveAdminConfig}
|
||||
onReset={handleResetAdminConfig}
|
||||
/>
|
||||
|
||||
@@ -24,6 +24,13 @@ export interface AdminConfiguration {
|
||||
opsAlertSlackWebhookUrl?: string;
|
||||
allowAgentDowngrade?: boolean;
|
||||
modernDeploymentHarmlessEventIdsJson?: string;
|
||||
/**
|
||||
* Feature flag for the V2 Decision Engine index-table dual-write (Plan §M5.d).
|
||||
* When true, IngestTelemetryFunction enqueues telemetry-index-reconcile envelopes after
|
||||
* committing each primary row, and the 2h IndexReconcileTimer re-scans the last 4h as
|
||||
* a safety net. Default false — keeps pre-M5.d behaviour bit-exact until explicitly flipped.
|
||||
*/
|
||||
enableIndexDualWrite?: boolean;
|
||||
}
|
||||
|
||||
export interface OpsAlertRule {
|
||||
|
||||
Reference in New Issue
Block a user