fix: bescherm excluded en locked topics tegen AI-deletion/merge
Topics met learning_relevance="exclude" of relevance_locked=true werden door de Full Analysis stilletjes verwijderd: het model zag exclude als "irrelevant" en stelde ze voor in actions.deletions / actions.merges, en analyzeGraph paste die zonder filter toe. Het locked-vlaggetje werd in full-scope payload niet eens meegestuurd, dus zelfs een nieuwe prompt kon niet helpen. Defense-in-depth fixes: 1. Pure graphGuard.filterAiActions strips elke deletion/merge waarvan de target excluded of locked is, vóór bulkSave. Merges waarin een protected topic juist de keepId is (canonical survivor) blijven door. 2. SYSTEM_PROMPTS.full krijgt een expliciete "PROTECTED TOPICS" sectie die exclude en locked topics als nooit-te-verwijderen markeert. 3. relevance_locked wordt nu ook in de full-scope compactTopics payload meegestuurd, zodat het model de vlag überhaupt ziet. 4. UI-feedback: GraphControls toont een ShieldCheck banner met aantal geblokkeerde acties na de analyze, en het excluded-aantal naast de "Show Excluded Nodes" toggle (visible/hidden). 5. 13 nieuwe Vitest cases dekken protected detection en alle drop/keep paden van filterAiActions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { RotateCcw, RefreshCw, AlertCircle, ChevronDown } from 'lucide-react';
|
||||
import { RotateCcw, RefreshCw, AlertCircle, ChevronDown, ShieldCheck } from 'lucide-react';
|
||||
import Button from '../../ui/Button';
|
||||
import SuggestionsQueue from '../SuggestionsQueue';
|
||||
|
||||
@@ -24,9 +24,11 @@ const SCOPE_OPTIONS = [
|
||||
export default function GraphControls({
|
||||
showExcludeNodes,
|
||||
onShowExcludeChange,
|
||||
excludedCount = 0,
|
||||
onAnalyze,
|
||||
isAnalyzing,
|
||||
analyzeError,
|
||||
analyzeNotice,
|
||||
disabled,
|
||||
onApplied,
|
||||
snapshotMeta,
|
||||
@@ -50,7 +52,14 @@ export default function GraphControls({
|
||||
onChange={e => onShowExcludeChange(e.target.checked)}
|
||||
className="rounded bg-bg-warm border-transparent focus:ring-0 text-teal"
|
||||
/>
|
||||
Show Excluded Nodes (Reference Material)
|
||||
<span>
|
||||
Show Excluded Nodes (Reference Material)
|
||||
{excludedCount > 0 && (
|
||||
<span className="ml-1 text-xs text-fg-muted/80">
|
||||
· {excludedCount} {showExcludeNodes ? 'visible' : 'hidden'}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div className="space-y-2">
|
||||
@@ -106,6 +115,16 @@ export default function GraphControls({
|
||||
{analyzeError}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{analyzeNotice && (
|
||||
<p
|
||||
className="text-xs text-teal flex items-start gap-1 bg-teal/5 border border-teal/20 rounded-[var(--r-sm)] p-2"
|
||||
title="The AI suggested removing excluded/locked topics. Those suggestions were dropped client-side before saving."
|
||||
>
|
||||
<ShieldCheck size={14} className="shrink-0 mt-0.5" />
|
||||
{analyzeNotice}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<SuggestionsQueue onApplied={onApplied} />
|
||||
|
||||
Reference in New Issue
Block a user