fix(sdk): Align scope types with engine output + project isolation + optional block progress
Some checks failed
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 46s
CI/CD / test-python-backend-compliance (push) Successful in 42s
CI/CD / test-python-document-crawler (push) Successful in 29s
CI/CD / test-python-dsms-gateway (push) Successful in 25s
CI/CD / deploy-hetzner (push) Failing after 2s

Type alignment (root cause of client-side crash):
- RiskFlag: id/title/description → severity/category/message/recommendation
- ScopeGap: id/title/recommendation/relatedDocuments → gapType/currentState/targetState/effort
- NextAction: id/priority:number/effortDays → actionType/priority:string/estimatedEffort
- ScopeReasoning: details → factors + impact
- TriggeredHardTrigger: {rule: HardTriggerRule} → flat fields (ruleId, description, etc.)
- All UI components updated to match engine output shape

Project isolation:
- Scope localStorage key now includes projectId (prevents data leak between projects)

Optional block progress:
- Blocks with only optional questions now show green checkmark when any question answered

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-11 14:58:29 +01:00
parent 46048554cb
commit cb48b8289e
7 changed files with 185 additions and 152 deletions

View File

@@ -37,14 +37,31 @@ const TABS: { id: TabId; label: string; icon: string }[] = [
export default function ComplianceScopePage() {
const { state: sdkState, dispatch } = useSDK()
// Project-specific storage key
const projectStorageKey = sdkState.projectId
? `${STORAGE_KEY}_${sdkState.projectId}`
: STORAGE_KEY
// Active tab state
const [activeTab, setActiveTab] = useState<TabId>('overview')
// Migrate old decision format: drop decision if it has old-format fields
const migrateState = (state: ComplianceScopeState): ComplianceScopeState => {
if (state.decision) {
const d = state.decision as Record<string, unknown>
// Old format had 'level' instead of 'determinedLevel', or docs with 'isMandatory'
if (d.level || !d.determinedLevel) {
return { ...state, decision: null }
}
}
return state
}
// Local scope state
const [scopeState, setScopeState] = useState<ComplianceScopeState>(() => {
// Try to load from SDK context first
if (sdkState.complianceScope) {
return sdkState.complianceScope
return migrateState(sdkState.complianceScope)
}
return createEmptyScopeState()
})
@@ -68,14 +85,14 @@ export default function ComplianceScopePage() {
const ctxScope = sdkState.complianceScope
if (ctxScope && ctxScope.answers?.length > 0) {
syncingFromSdk.current = true
setScopeState(ctxScope)
setScopeState(migrateState(ctxScope))
setIsLoading(false)
} else if (isLoading) {
// SDK has no scope data — try localStorage fallback, then give up
try {
const stored = localStorage.getItem(STORAGE_KEY)
const stored = localStorage.getItem(projectStorageKey)
if (stored) {
const parsed = JSON.parse(stored) as ComplianceScopeState
const parsed = migrateState(JSON.parse(stored) as ComplianceScopeState)
if (parsed.answers?.length > 0) {
setScopeState(parsed)
dispatch({ type: 'SET_COMPLIANCE_SCOPE', payload: parsed })
@@ -106,7 +123,7 @@ export default function ComplianceScopePage() {
return
}
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(scopeState))
localStorage.setItem(projectStorageKey, JSON.stringify(scopeState))
dispatch({ type: 'SET_COMPLIANCE_SCOPE', payload: scopeState })
} catch (error) {
console.error('Failed to save compliance scope state:', error)
@@ -194,7 +211,7 @@ export default function ComplianceScopePage() {
const emptyState = createEmptyScopeState()
setScopeState(emptyState)
setActiveTab('overview')
localStorage.removeItem(STORAGE_KEY)
localStorage.removeItem(projectStorageKey)
}, [])
// Calculate completion statistics