chore: remove shadow policy machinery (ADR-0013 step 10)
Deletes shadowPolicies map, getShadowPolicies, setPolicyActive from recommender.ts; removes /api/admin/policies routes from admin.ts; removes getPolicies, togglePolicy, PolicyInfo from admin api.ts; removes the policy toggle section from the ops page. 168 API tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,32 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { AdminShell } from '@/components/AdminShell';
|
||||
import { getPolicies, togglePolicy, replaySignal, PolicyInfo } from '@/lib/api';
|
||||
import { replaySignal } from '@/lib/api';
|
||||
|
||||
const VALID_SUBJECTS = ['signals.tip.served', 'signals.tip.feedback', 'signals.task.synced'];
|
||||
|
||||
export default function OpsPage() {
|
||||
const [policies, setPolicies] = useState<PolicyInfo[]>([]);
|
||||
const [replaySubject, setReplaySubject] = useState(VALID_SUBJECTS[0]);
|
||||
const [replayPayload, setReplayPayload] = useState('{\n "userId": "",\n "tipId": ""\n}');
|
||||
const [msg, setMsg] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
getPolicies().then((r) => setPolicies(r.policies)).catch(() => {});
|
||||
}, []);
|
||||
|
||||
const handleToggle = async (name: string, active: boolean) => {
|
||||
try {
|
||||
await togglePolicy(name, active);
|
||||
setPolicies((prev) => prev.map((p) => p.name === name ? { ...p, active } : p));
|
||||
setMsg(`Policy "${name}" ${active ? 'enabled' : 'disabled'}.`);
|
||||
} catch (e: any) {
|
||||
setError(e.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReplay = async () => {
|
||||
let payload: Record<string, unknown>;
|
||||
try {
|
||||
@@ -50,36 +35,14 @@ export default function OpsPage() {
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold">Ops</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Live system controls — toggle shadow recommendation policies, replay past signals
|
||||
for backfill or debugging, and find per-user actions (token revoke, bandit reset)
|
||||
on the <a href="/users" className="text-indigo-400 hover:underline">Users page</a>.
|
||||
Live system controls — replay past signals for backfill or debugging, and find
|
||||
per-user actions (token revoke) on the{' '}
|
||||
<a href="/users" className="text-indigo-400 hover:underline">Users page</a>.
|
||||
</p>
|
||||
</div>
|
||||
{msg && <p className="text-green-400 text-sm">{msg}</p>}
|
||||
{error && <p className="text-red-400 text-sm">{error}</p>}
|
||||
|
||||
{/* Policy toggles */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-base font-medium text-gray-300">Policies</h2>
|
||||
{policies.length === 0 ? (
|
||||
<p className="text-gray-500 text-sm">No shadow policies registered. Shadow policies can be added to the recommender source.</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{policies.map((p) => (
|
||||
<div key={p.name} className="flex items-center justify-between bg-gray-900 border border-gray-800 rounded p-3">
|
||||
<span className="text-sm text-gray-300 font-mono">{p.name}</span>
|
||||
<button
|
||||
onClick={() => handleToggle(p.name, !p.active)}
|
||||
className={`px-3 py-1 rounded text-xs ${p.active ? 'bg-green-800 text-green-200' : 'bg-gray-800 text-gray-400'}`}
|
||||
>
|
||||
{p.active ? 'Active' : 'Disabled'}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Replay signal */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-base font-medium text-gray-300">Replay signal</h2>
|
||||
|
||||
@@ -91,10 +91,6 @@ export interface HealthStatus {
|
||||
services: { name: string; status: string; latencyMs: number }[];
|
||||
}
|
||||
|
||||
export interface PolicyInfo {
|
||||
name: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface SavedQuery {
|
||||
id: string;
|
||||
@@ -223,16 +219,6 @@ export function getHealth() {
|
||||
return apiFetch<HealthStatus>('/admin/health');
|
||||
}
|
||||
|
||||
export function getPolicies() {
|
||||
return apiFetch<{ policies: PolicyInfo[] }>('/admin/policies');
|
||||
}
|
||||
|
||||
export function togglePolicy(name: string, active: boolean) {
|
||||
return apiFetch<{ ok: boolean }>(`/admin/policies/${name}/toggle`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ active }),
|
||||
});
|
||||
}
|
||||
|
||||
export function replaySignal(subject: string, payload: Record<string, unknown>) {
|
||||
return apiFetch<{ ok: boolean }>('/admin/replay-signal', {
|
||||
|
||||
Reference in New Issue
Block a user