harryagasi
[NOTICKET] feat(knowledge,analysis): add deletion confirmation, duplicate-name guard, and unusable-analysis lockout
3baad7e
|
Raw
History Blame Contribute Delete
4.32 kB

Data Model: Knowledge Source Deletion Guardrails

No new persisted entities or backend schema changes. This feature only adds client-side derived values computed from existing orchestrationApi types (src/services/orchestrationApi.ts).

Existing entities reused (unchanged)

  • ApiDocument (orchestrationApi.ts:60-ish): { id, filename, file_type, file_size, status, created_at, ... } β€” a document knowledge source.
  • DatabaseClient: { id, name, db_type, status, ... } β€” a database knowledge source.
  • DataBindItem (orchestrationApi.ts:150): { id, name, group_type: "document" | "database", type } β€” a reference from an Analysis to a bound knowledge source.
  • Analysis (orchestrationApi.ts:157): { id, analysis_title, objective, business_questions, status: "active" | "inactive" | string, data_bind: DataBindItem[], data_bind_version, ... }. status is not modified by this feature (see Clarifications in spec.md).

New client-side derived values (not persisted, not sent to the backend)

DeletionImpactSummary

Computed at delete-intent time in KnowledgeManagement.tsx, before the confirmation dialog opens.

Field Type Description
sourceIds string[] The knowledge source id(s) about to be deleted (one for single delete, many for "clear all").
affectedAnalyses { id: string; analysis_title: string }[] Analyses whose data_bind contains at least one of sourceIds, deduplicated by analysis id. Derived by fetching listAnalyses() and filtering analysis.data_bind.some(item => sourceIds.includes(item.id)).

Lifecycle: Computed fresh each time a delete action is initiated; discarded when the dialog closes (confirm or cancel). Never persisted.

Analysis usability flag (isUnusable)

Reused, not newly computed: AnalysisShell.tsx already maintains staleSources: DataBindItem[] (state at AnalysisShell.tsx:80, computed in the useEffect at AnalysisShell.tsx:117-138) by cross-referencing activeAnalysis.data_bind against live getDocuments/getDatabaseClients results β€” this is exactly the "unusable" derivation this feature needs, built for feature 002's stale-sources banner. This feature derives from it rather than re-implementing it:

isActiveAnalysisUnusable: boolean = staleSources.length > 0
  • Any single missing bound source is sufficient to mark the whole analysis unusable (per spec Assumptions / Acceptance Scenario US3.6) β€” already true of staleSources's existing filter logic.
  • This is a pure, stateless derivation β€” not stored on the Analysis object returned by the API, and never written back via updateAnalysis/UpdateAnalysisPayload.
  • Recomputed on-demand (page load / analysis selection), not via a live subscription (per research.md Unknown 3 and the plan's Constraints) β€” matching staleSources's existing recompute trigger (the activeAnalysis-keyed effect).

State transitions: isActiveAnalysisUnusable has no explicit transition function in this feature β€” it is a pure function of current data. It can only become true when a bound source is deleted (User Story 1 β†’ 3). Per clarification, this feature has no transition back to false: once staleSources.length > 0, the update-bound-data action (including the pre-existing "Update binding" control that would normally clear staleSources) is itself disabled by this feature (FR-011/FR-014), so there is no in-app path to restore usability.

Validation rule: analysis name uniqueness

Enforced client-side in NewAnalysisDialog.tsx before calling createAnalysis():

isDuplicateName(candidate: string, existing: Analysis[]): boolean =
  existing.some(a => a.analysis_title.trim().toLowerCase() === candidate.trim().toLowerCase())
  • Requires the dialog to have access to the current analysis list (already loaded by the parent AnalysisShell.tsx as analyses state) β€” passed down as a new prop, or fetched via listAnalyses() if the dialog is opened without it in scope.
  • This is a client-side guard only; per FR-007/FR-008 no backend contract change is assumed to exist, so this check does not guarantee uniqueness under concurrent creation from two sessions (acceptable per spec β€” no server-side uniqueness constraint was requested).