helixevo 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +20 -4
- package/dashboard/app/api/ontology/route.ts +89 -0
- package/dashboard/app/coevolution/client.tsx +32 -2
- package/dashboard/app/coevolution/page.tsx +3 -2
- package/dashboard/app/commands/page.tsx +80 -3
- package/dashboard/app/guide/page.tsx +80 -7
- package/dashboard/app/ontology/client.tsx +295 -0
- package/dashboard/app/ontology/page.tsx +9 -0
- package/dashboard/app/page.tsx +22 -2
- package/dashboard/app/topology/client.tsx +4 -0
- package/dashboard/components/sidebar-nav.tsx +10 -5
- package/dashboard/lib/data.ts +953 -53
- package/dist/cli.js +1133 -41
- package/package.json +1 -1
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link'
|
|
4
|
+
import { useState } from 'react'
|
|
5
|
+
import { ConsolePanel } from '@/components/console-panel'
|
|
6
|
+
import { MetricCard } from '@/components/metric-card'
|
|
7
|
+
import { PageHero } from '@/components/page-hero'
|
|
8
|
+
import { SectionFrame } from '@/components/section-frame'
|
|
9
|
+
import type { OntologyControlDashboardSummary, OntologyReviewDecisionStatus } from '@/lib/data'
|
|
10
|
+
|
|
11
|
+
type RunState = 'idle' | 'running' | 'success' | 'error'
|
|
12
|
+
|
|
13
|
+
function consoleTone(state: RunState): 'neutral' | 'green' | 'red' | 'yellow' {
|
|
14
|
+
if (state === 'success') return 'green'
|
|
15
|
+
if (state === 'error') return 'red'
|
|
16
|
+
return 'neutral'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toneForConcept(kind: string): 'blue' | 'green' | 'purple' | 'yellow' | 'neutral' {
|
|
20
|
+
if (kind === 'capability') return 'blue'
|
|
21
|
+
if (kind === 'pressure-type') return 'yellow'
|
|
22
|
+
if (kind === 'topology-motif') return 'purple'
|
|
23
|
+
return 'neutral'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function formatDate(value: string | undefined) {
|
|
27
|
+
if (!value) return '—'
|
|
28
|
+
return new Date(value).toLocaleString()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatMode(value: string) {
|
|
32
|
+
return value.replace(/-/g, ' ')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function OntologyClient({ initialDashboard }: { initialDashboard: OntologyControlDashboardSummary }) {
|
|
36
|
+
const [dashboard, setDashboard] = useState(initialDashboard)
|
|
37
|
+
const [runState, setRunState] = useState<RunState>('idle')
|
|
38
|
+
const [output, setOutput] = useState('')
|
|
39
|
+
const [pendingKey, setPendingKey] = useState<string | null>(null)
|
|
40
|
+
|
|
41
|
+
const runAction = async (action: 'refresh' | 'review' | 'deprecate', payload: { conceptId?: string; decision?: OntologyReviewDecisionStatus; rationale?: string }, label: string) => {
|
|
42
|
+
setPendingKey(payload.conceptId ?? action)
|
|
43
|
+
setRunState('running')
|
|
44
|
+
setOutput('')
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch('/api/ontology', {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: { 'Content-Type': 'application/json' },
|
|
49
|
+
body: JSON.stringify({ action, ...payload }),
|
|
50
|
+
})
|
|
51
|
+
const data = await res.json()
|
|
52
|
+
if (!res.ok || !data.success) {
|
|
53
|
+
setRunState('error')
|
|
54
|
+
setOutput(data.error ?? `${label} failed`)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
setDashboard(data.dashboard)
|
|
58
|
+
setRunState('success')
|
|
59
|
+
setOutput(data.output || `${label} completed`)
|
|
60
|
+
} catch (err: unknown) {
|
|
61
|
+
setRunState('error')
|
|
62
|
+
setOutput(err instanceof Error ? err.message : `${label} failed`)
|
|
63
|
+
} finally {
|
|
64
|
+
setPendingKey(null)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div style={{ display: 'grid', gap: 22 }}>
|
|
70
|
+
<PageHero
|
|
71
|
+
eyebrow="Semantic control"
|
|
72
|
+
title="Ontology Semantic Control"
|
|
73
|
+
description="Inspect the stable kernel, review frontier concept hypotheses, promote approved extensions, track active semantic consumers, and keep ontology adoption governed rather than free-form."
|
|
74
|
+
chips={[
|
|
75
|
+
{ label: `${dashboard.summary.frontier} frontier`, tone: dashboard.summary.frontier > 0 ? 'yellow' : 'neutral' },
|
|
76
|
+
{ label: `${dashboard.summary.reviewOpen} open review`, tone: dashboard.summary.reviewOpen > 0 ? 'yellow' : 'green' },
|
|
77
|
+
{ label: `${dashboard.summary.extensions} approved extensions`, tone: dashboard.summary.extensions > 0 ? 'blue' : 'neutral' },
|
|
78
|
+
{ label: `${dashboard.adoption.activeConcepts} active concepts`, tone: dashboard.adoption.activeConcepts > 0 ? 'green' : 'neutral' },
|
|
79
|
+
{ label: `${dashboard.adoption.routesInfluenced} semantically influenced routes`, tone: dashboard.adoption.routesInfluenced > 0 ? 'purple' : 'neutral' },
|
|
80
|
+
{ label: formatMode(dashboard.governance.activeMode), tone: dashboard.governance.activeMode === 'transfer-focused' ? 'purple' : dashboard.governance.activeMode === 'project-critical' ? 'yellow' : 'blue' },
|
|
81
|
+
]}
|
|
82
|
+
actions={
|
|
83
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
84
|
+
<div className="hero-note-card">
|
|
85
|
+
<div className="hero-note-label">Native ontology state</div>
|
|
86
|
+
<div className="hero-note-title">Kernel + review + semantic adoption</div>
|
|
87
|
+
<div className="hero-note-copy">Use this surface to move from frontier hypotheses into approved extensions, inspect active semantic consumers, and manage deprecation with operator-visible risk rather than hidden drift.</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
|
|
90
|
+
<button onClick={() => runAction('refresh', {}, 'ontology refresh')} disabled={runState === 'running'} className="badge badge-blue" style={{ border: 'none', cursor: 'pointer' }}>Refresh ontology frontier</button>
|
|
91
|
+
<Link href="/coevolution" className="badge badge-gray">Open co-evolution</Link>
|
|
92
|
+
<Link href="/topology" className="badge badge-gray">Open topology</Link>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
}
|
|
96
|
+
/>
|
|
97
|
+
|
|
98
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
|
|
99
|
+
<MetricCard label="Kernel concepts" value={dashboard.summary.kernelConcepts} sublabel={`${dashboard.kernel.relationFamilies.length} relation families`} tone="purple" icon="◎" />
|
|
100
|
+
<MetricCard label="Frontier hypotheses" value={dashboard.summary.frontier} sublabel={`${dashboard.summary.reviewOpen} open • ${dashboard.summary.deferred} deferred`} tone={dashboard.summary.reviewOpen > 0 ? 'yellow' : 'neutral'} icon="◇" />
|
|
101
|
+
<MetricCard label="Approved extensions" value={dashboard.summary.extensions} sublabel={`${dashboard.summary.deprecated} deprecated • ${dashboard.adoption.unusedExtensions} unused`} tone={dashboard.summary.extensions > 0 ? 'blue' : 'neutral'} icon="↑" />
|
|
102
|
+
<MetricCard label="Active semantic concepts" value={dashboard.adoption.activeConcepts} sublabel={`${dashboard.adoption.totalBindings} bindings • ${dashboard.adoption.routesInfluenced} routed influences`} tone={dashboard.adoption.activeConcepts > 0 ? 'green' : 'neutral'} icon="⇄" />
|
|
103
|
+
<MetricCard label="Deprecation-sensitive" value={dashboard.adoption.conceptsAtDeprecationRisk} sublabel="approved concepts with live consumers" tone={dashboard.adoption.conceptsAtDeprecationRisk > 0 ? 'yellow' : 'neutral'} icon="!" />
|
|
104
|
+
<MetricCard label="Concept changes" value={dashboard.summary.changeEvents} sublabel={`${dashboard.summary.promoted} promoted • ${dashboard.summary.rejected} rejected`} tone={dashboard.summary.changeEvents > 0 ? 'green' : 'neutral'} icon="⇄" />
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<SectionFrame
|
|
108
|
+
eyebrow="Kernel"
|
|
109
|
+
title="Stable semantic anchor"
|
|
110
|
+
description="The kernel remains the constitutional layer. HelixEvo still keeps it stable while governed frontier review, approved extensions, and semantic adoption grow around it."
|
|
111
|
+
>
|
|
112
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
|
113
|
+
<span className="badge badge-gray">version {dashboard.kernel.version}</span>
|
|
114
|
+
<span className="badge badge-gray">{dashboard.kernel.entityNames.length} kernel entities</span>
|
|
115
|
+
<span className="badge badge-gray">{dashboard.kernel.mutationOperations.length} mutation operations</span>
|
|
116
|
+
<span className="badge badge-gray">{dashboard.kernel.invariantIds.length} invariants</span>
|
|
117
|
+
</div>
|
|
118
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12, marginTop: 16 }}>
|
|
119
|
+
<div className="guide-dimension-card" style={{ borderLeftColor: 'var(--blue)' }}>
|
|
120
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--blue)' }}>Kernel pillars</div>
|
|
121
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.6 }}>{dashboard.kernel.pillars.join(' • ')}</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="guide-dimension-card" style={{ borderLeftColor: 'var(--green)' }}>
|
|
124
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--green)' }}>Allowed layers</div>
|
|
125
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.6 }}>{dashboard.kernel.layers.join(' • ')}</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</SectionFrame>
|
|
129
|
+
|
|
130
|
+
<SectionFrame
|
|
131
|
+
eyebrow="Adoption"
|
|
132
|
+
title="Semantic consumer coverage"
|
|
133
|
+
description="Approved concepts now become visible as live semantic consumers across pressure, route rationale, transfer, and topology interpretation rather than remaining passive approved vocabulary."
|
|
134
|
+
>
|
|
135
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 16 }}>
|
|
136
|
+
<div className="guide-dimension-card" style={{ borderLeftColor: 'var(--green)' }}>
|
|
137
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--green)' }}>Top active concepts</div>
|
|
138
|
+
<div style={{ display: 'grid', gap: 8, marginTop: 10 }}>
|
|
139
|
+
{dashboard.adoption.topActiveConcepts.length > 0 ? dashboard.adoption.topActiveConcepts.map((concept) => (
|
|
140
|
+
<div key={concept.conceptId} style={{ fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>
|
|
141
|
+
<strong style={{ color: 'var(--text)' }}>{concept.conceptName}</strong>
|
|
142
|
+
<div>{concept.totalBindings} bindings • {Object.entries(concept.bindingsByTargetType).map(([target, count]) => `${target}:${count}`).join(' • ')}</div>
|
|
143
|
+
</div>
|
|
144
|
+
)) : <div style={{ fontSize: 12, color: 'var(--text-dim)' }}>No active semantic consumers yet.</div>}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
<div className="guide-dimension-card" style={{ borderLeftColor: 'var(--yellow)' }}>
|
|
148
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--yellow)' }}>Deprecation risk</div>
|
|
149
|
+
<div style={{ display: 'grid', gap: 8, marginTop: 10 }}>
|
|
150
|
+
{dashboard.adoption.atRiskConcepts.length > 0 ? dashboard.adoption.atRiskConcepts.map((concept) => (
|
|
151
|
+
<div key={concept.conceptId} style={{ fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>
|
|
152
|
+
<strong style={{ color: 'var(--text)' }}>{concept.conceptName}</strong>
|
|
153
|
+
<div>{concept.warning}</div>
|
|
154
|
+
</div>
|
|
155
|
+
)) : <div style={{ fontSize: 12, color: 'var(--text-dim)' }}>No deprecation-sensitive concepts right now.</div>}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 16 }}>
|
|
160
|
+
<span className="badge badge-gray">bindings → signals {dashboard.adoption.bindingsByTargetType['pressure-signal'] ?? 0}</span>
|
|
161
|
+
<span className="badge badge-gray">motifs {dashboard.adoption.bindingsByTargetType['pressure-motif'] ?? 0}</span>
|
|
162
|
+
<span className="badge badge-gray">routes {dashboard.adoption.bindingsByTargetType['route-recommendation'] ?? 0}</span>
|
|
163
|
+
<span className="badge badge-gray">transfers {dashboard.adoption.bindingsByTargetType['transfer-event'] ?? 0}</span>
|
|
164
|
+
<span className="badge badge-gray">topology {dashboard.adoption.bindingsByTargetType['topology-review'] ?? 0}</span>
|
|
165
|
+
<span className="badge badge-gray">unused extensions {dashboard.adoption.unusedExtensions}</span>
|
|
166
|
+
</div>
|
|
167
|
+
</SectionFrame>
|
|
168
|
+
|
|
169
|
+
<SectionFrame
|
|
170
|
+
eyebrow="Frontier"
|
|
171
|
+
title="Reviewable concept hypotheses"
|
|
172
|
+
description="These concepts remain provisional until reviewed. Promotion materializes an approved extension; reject and defer preserve semantic memory without silent drift."
|
|
173
|
+
>
|
|
174
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
175
|
+
{dashboard.frontier.length > 0 ? dashboard.frontier.map((concept) => (
|
|
176
|
+
<div key={concept.id} style={{ padding: '16px 18px', borderRadius: 18, border: '1px solid var(--border)', background: 'rgba(255,255,255,0.74)' }}>
|
|
177
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
|
178
|
+
<div style={{ display: 'grid', gap: 4, flex: 1 }}>
|
|
179
|
+
<div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)' }}>{concept.name}</div>
|
|
180
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>{concept.description ?? concept.sourceSummary ?? 'Provisional ontology hypothesis.'}</div>
|
|
181
|
+
</div>
|
|
182
|
+
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
|
183
|
+
<span className={`badge badge-${toneForConcept(concept.conceptKind)}`}>{concept.conceptKind}</span>
|
|
184
|
+
<span className="badge badge-gray">{concept.reviewStatus}</span>
|
|
185
|
+
<span className="badge badge-gray">{concept.observationCount ?? 0} obs</span>
|
|
186
|
+
<span className="badge badge-gray">{((concept.confidence ?? 0) * 100).toFixed(0)}% confidence</span>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 12 }}>
|
|
190
|
+
<span className="badge badge-gray">{(concept.projectIds?.length ?? 0)} projects</span>
|
|
191
|
+
<span className="badge badge-gray">{(concept.evidenceIds?.length ?? 0)} evidence ids</span>
|
|
192
|
+
<span className="badge badge-gray">last seen {formatDate(concept.lastObservedAt)}</span>
|
|
193
|
+
</div>
|
|
194
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 14 }}>
|
|
195
|
+
<button onClick={() => runAction('review', { conceptId: concept.id, decision: 'promote', rationale: `Promote ${concept.name} into approved ontology extension.` }, `promote ${concept.name}`)} disabled={pendingKey === concept.id} className="badge badge-blue" style={{ border: 'none', cursor: 'pointer' }}>Promote</button>
|
|
196
|
+
<button onClick={() => runAction('review', { conceptId: concept.id, decision: 'defer', rationale: `Defer ${concept.name} for more evidence.` }, `defer ${concept.name}`)} disabled={pendingKey === concept.id} className="badge badge-purple" style={{ border: 'none', cursor: 'pointer' }}>Defer</button>
|
|
197
|
+
<button onClick={() => runAction('review', { conceptId: concept.id, decision: 'reject', rationale: `Reject ${concept.name} as a weak or overlapping frontier concept.` }, `reject ${concept.name}`)} disabled={pendingKey === concept.id} className="badge badge-gray" style={{ border: 'none', cursor: 'pointer' }}>Reject</button>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
)) : (
|
|
201
|
+
<div className="empty-state" style={{ padding: 24 }}>
|
|
202
|
+
<div className="empty-state-title">No ontology frontier concepts yet</div>
|
|
203
|
+
<div className="empty-state-desc">Run “Refresh ontology frontier” after pressure motifs, project gaps, or topology review patterns accumulate.</div>
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
</div>
|
|
207
|
+
</SectionFrame>
|
|
208
|
+
|
|
209
|
+
<SectionFrame
|
|
210
|
+
eyebrow="Extensions"
|
|
211
|
+
title="Approved ontology extensions"
|
|
212
|
+
description="Promoted concepts live here as approved semantic vocabulary. Once active, they can shape pressure, route rationale, transfer interpretation, and topology review while deprecation remains bounded and explicit."
|
|
213
|
+
>
|
|
214
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
215
|
+
{dashboard.extensions.length > 0 ? dashboard.extensions.map((concept) => (
|
|
216
|
+
<div key={concept.id} style={{ padding: '16px 18px', borderRadius: 18, border: '1px solid var(--border)', background: 'rgba(255,255,255,0.74)' }}>
|
|
217
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
|
218
|
+
<div style={{ display: 'grid', gap: 4, flex: 1 }}>
|
|
219
|
+
<div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)' }}>{concept.name}</div>
|
|
220
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>{concept.description ?? concept.sourceSummary ?? 'Approved ontology extension.'}</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
|
223
|
+
<span className={`badge badge-${toneForConcept(concept.conceptKind)}`}>{concept.conceptKind}</span>
|
|
224
|
+
<span className="badge badge-gray">{concept.status}</span>
|
|
225
|
+
<span className="badge badge-gray">{concept.adoptionCount} bindings</span>
|
|
226
|
+
<span className="badge badge-gray">{(concept.evidenceIds?.length ?? 0)} evidence ids</span>
|
|
227
|
+
{concept.warning ? <span className="badge badge-yellow">deprecation risk</span> : null}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 12 }}>
|
|
231
|
+
<span className="badge badge-gray">{(concept.projectIds?.length ?? 0)} projects</span>
|
|
232
|
+
<span className="badge badge-gray">signals {concept.bindingsByTargetType['pressure-signal'] ?? 0}</span>
|
|
233
|
+
<span className="badge badge-gray">routes {concept.bindingsByTargetType['route-recommendation'] ?? 0}</span>
|
|
234
|
+
<span className="badge badge-gray">transfers {concept.bindingsByTargetType['transfer-event'] ?? 0}</span>
|
|
235
|
+
<span className="badge badge-gray">created {formatDate(concept.createdAt)}</span>
|
|
236
|
+
{concept.status !== 'deprecated' ? (
|
|
237
|
+
<button onClick={() => runAction('deprecate', { conceptId: concept.id, rationale: `Deprecate ontology extension ${concept.name} after operator review.` }, `deprecate ${concept.name}`)} disabled={pendingKey === concept.id} className="badge badge-gray" style={{ border: 'none', cursor: 'pointer' }}>Deprecate</button>
|
|
238
|
+
) : null}
|
|
239
|
+
</div>
|
|
240
|
+
{concept.warning ? <div className="signal-text" style={{ marginTop: 10 }}>• {concept.warning}</div> : null}
|
|
241
|
+
</div>
|
|
242
|
+
)) : (
|
|
243
|
+
<div className="empty-state" style={{ padding: 24 }}>
|
|
244
|
+
<div className="empty-state-title">No approved extensions yet</div>
|
|
245
|
+
<div className="empty-state-desc">Promoted frontier concepts will appear here once they pass semantic review.</div>
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
</SectionFrame>
|
|
250
|
+
|
|
251
|
+
<SectionFrame
|
|
252
|
+
eyebrow="Change log"
|
|
253
|
+
title="Recent semantic transitions"
|
|
254
|
+
description="Every meaningful ontology action should leave behind a visible change trail instead of becoming invisible semantic drift."
|
|
255
|
+
>
|
|
256
|
+
<div style={{ display: 'grid', gap: 10 }}>
|
|
257
|
+
{dashboard.recentChanges.length > 0 ? dashboard.recentChanges.map((event) => (
|
|
258
|
+
<div key={event.id} style={{ padding: '14px 16px', borderRadius: 18, border: '1px solid var(--border)', background: 'rgba(255,255,255,0.72)' }}>
|
|
259
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
|
|
260
|
+
<div style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--text)' }}>{event.changeType}</div>
|
|
261
|
+
<span className="badge badge-gray">{formatDate(event.timestamp)}</span>
|
|
262
|
+
</div>
|
|
263
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 6, lineHeight: 1.6 }}>{event.reason}</div>
|
|
264
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
|
|
265
|
+
<span className="badge badge-gray">{event.conceptIds.join(', ')}</span>
|
|
266
|
+
<span className="badge badge-gray">{(event.evidenceIds?.length ?? 0)} evidence ids</span>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
)) : (
|
|
270
|
+
<div className="empty-state" style={{ padding: 24 }}>
|
|
271
|
+
<div className="empty-state-title">No ontology changes logged yet</div>
|
|
272
|
+
<div className="empty-state-desc">Refresh the frontier or review a concept to create the first native ontology events.</div>
|
|
273
|
+
</div>
|
|
274
|
+
)}
|
|
275
|
+
</div>
|
|
276
|
+
</SectionFrame>
|
|
277
|
+
|
|
278
|
+
<ConsolePanel
|
|
279
|
+
title="Ontology console"
|
|
280
|
+
tone={consoleTone(runState)}
|
|
281
|
+
actions={<span className="badge badge-gray">{runState === 'running' ? 'running' : runState === 'success' ? 'success' : runState === 'error' ? 'error' : 'idle'}</span>}
|
|
282
|
+
>
|
|
283
|
+
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', fontSize: 12, lineHeight: 1.7, color: 'var(--text-dim)' }}>
|
|
284
|
+
{runState === 'running'
|
|
285
|
+
? 'Running ontology control…\n\n' + (output || '')
|
|
286
|
+
: runState === 'success'
|
|
287
|
+
? (output || 'Ontology action completed.')
|
|
288
|
+
: runState === 'error'
|
|
289
|
+
? (output || 'Ontology action failed.')
|
|
290
|
+
: 'Refresh the frontier or review concepts to inspect semantic changes.'}
|
|
291
|
+
</pre>
|
|
292
|
+
</ConsolePanel>
|
|
293
|
+
</div>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import OntologyClient from './client'
|
|
2
|
+
import { loadOntologyControlSummary } from '@/lib/data'
|
|
3
|
+
|
|
4
|
+
export const dynamic = 'force-dynamic'
|
|
5
|
+
|
|
6
|
+
export default function OntologyPage() {
|
|
7
|
+
const dashboard = loadOntologyControlSummary()
|
|
8
|
+
return <OntologyClient initialDashboard={dashboard} />
|
|
9
|
+
}
|
package/dashboard/app/page.tsx
CHANGED
|
@@ -83,8 +83,8 @@ export default function Overview() {
|
|
|
83
83
|
<SectionFrame
|
|
84
84
|
eyebrow="Brain foundation"
|
|
85
85
|
title="Semantic backbone"
|
|
86
|
-
description="A
|
|
87
|
-
actions={<span className="badge badge-gray">{ontology.source === 'graph' ? `Ontology ${ontology.specVersion}` : 'Compatibility-derived'}</span>}
|
|
86
|
+
description="A hybrid ontology view of the living brain, combining derived skill-graph semantics with native ontology frontier, extension, and semantic review activity."
|
|
87
|
+
actions={<span className="badge badge-gray">{ontology.source === 'hybrid-native-derived' ? `Ontology ${ontology.specVersion} · hybrid native + derived` : ontology.source === 'graph' ? `Ontology ${ontology.specVersion}` : 'Compatibility-derived'}</span>}
|
|
88
88
|
>
|
|
89
89
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
|
|
90
90
|
<MetricCard
|
|
@@ -143,6 +143,22 @@ export default function Overview() {
|
|
|
143
143
|
href="/topology"
|
|
144
144
|
icon="⇄"
|
|
145
145
|
/>
|
|
146
|
+
<MetricCard
|
|
147
|
+
label="Ontology frontier"
|
|
148
|
+
value={ontology.ontologyLoop.frontier}
|
|
149
|
+
sublabel={`${ontology.ontologyLoop.reviewOpen} open • ${ontology.ontologyLoop.extensions} approved extensions`}
|
|
150
|
+
tone={ontology.ontologyLoop.reviewOpen > 0 ? 'yellow' : ontology.ontologyLoop.extensions > 0 ? 'blue' : 'neutral'}
|
|
151
|
+
href="/ontology"
|
|
152
|
+
icon="◎"
|
|
153
|
+
/>
|
|
154
|
+
<MetricCard
|
|
155
|
+
label="Active semantic concepts"
|
|
156
|
+
value={ontology.ontologyLoop.adoption.activeConcepts}
|
|
157
|
+
sublabel={`${ontology.ontologyLoop.adoption.totalBindings} bindings • ${ontology.ontologyLoop.adoption.routesInfluenced} influenced routes`}
|
|
158
|
+
tone={ontology.ontologyLoop.adoption.activeConcepts > 0 ? 'green' : 'neutral'}
|
|
159
|
+
href="/ontology"
|
|
160
|
+
icon="⇄"
|
|
161
|
+
/>
|
|
146
162
|
<MetricCard
|
|
147
163
|
label="Observed mutation verbs"
|
|
148
164
|
value={ontology.mutationOperationsObserved}
|
|
@@ -166,8 +182,12 @@ export default function Overview() {
|
|
|
166
182
|
<span className="badge badge-gray">{coevolution.crossProjectGapAreas.length} cross-project gap motifs</span>
|
|
167
183
|
<span className="badge badge-gray">{ontology.pressureMotifs.promotionReady} motifs currently recommend generalize</span>
|
|
168
184
|
<span className="badge badge-gray">{ontology.transferEvents.total} transfer events logged ({ontology.transferEvents.realized} realized)</span>
|
|
185
|
+
<span className="badge badge-gray">ontology → {ontology.ontologyLoop.frontier} frontier • {ontology.ontologyLoop.extensions} extensions • {ontology.ontologyLoop.changeEvents} changes</span>
|
|
186
|
+
<span className="badge badge-gray">semantic adoption → {ontology.ontologyLoop.adoption.activeConcepts} active concepts • {ontology.ontologyLoop.adoption.totalBindings} bindings • {ontology.ontologyLoop.adoption.routesInfluenced} influenced routes</span>
|
|
187
|
+
<span className="badge badge-gray">deprecation risk → {ontology.ontologyLoop.adoption.conceptsAtDeprecationRisk} concepts • {ontology.ontologyLoop.adoption.unusedExtensions} unused extensions</span>
|
|
169
188
|
<span className="badge badge-gray">topology → {ontology.topologyReviews.open} open • {ontology.topologyReviews.accepted} accepted • {ontology.topologyReviews.generatedFromManualReview} manual-route</span>
|
|
170
189
|
<span className="badge badge-gray">execution → {ontology.topologyExecution.prepared} prepared • {ontology.topologyExecution.applied} applied • {ontology.topologyExecution.rolledBack} rolled back</span>
|
|
190
|
+
<Link href="/ontology" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open ontology control</Link>
|
|
171
191
|
<Link href="/topology" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open topology control</Link>
|
|
172
192
|
<span className="badge badge-gray">governance: {ontology.governance.activeMode.replace(/-/g, ' ')} ({ontology.governance.source})</span>
|
|
173
193
|
<span className="badge badge-gray">routes → research {ontology.governedRoutes.research} • specialize {ontology.governedRoutes.specialize} • evolve {ontology.governedRoutes.evolve} • generalize {ontology.governedRoutes.generalize} • manual-review {ontology.governedRoutes['manual-review']}</span>
|
|
@@ -253,6 +253,7 @@ export default function TopologyClient({
|
|
|
253
253
|
</div>
|
|
254
254
|
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
|
|
255
255
|
<button onClick={handleOptimize} disabled={runState === 'running'} className="badge badge-blue" style={{ border: 'none', cursor: 'pointer' }}>Run graph optimize</button>
|
|
256
|
+
<Link href="/ontology" className="badge badge-gray">Open ontology</Link>
|
|
256
257
|
<Link href="/coevolution" className="badge badge-gray">Open co-evolution</Link>
|
|
257
258
|
<Link href="/network" className="badge badge-gray">Open network</Link>
|
|
258
259
|
</div>
|
|
@@ -285,6 +286,7 @@ export default function TopologyClient({
|
|
|
285
286
|
<span className={`badge badge-${toneForChange(candidate.changeType)}`}>{candidate.changeType}</span>
|
|
286
287
|
<span className="badge badge-gray">accepted</span>
|
|
287
288
|
<span className="badge badge-gray">risk {(candidate.riskScore * 100).toFixed(0)}%</span>
|
|
289
|
+
{(candidate.semanticConceptIds ?? []).slice(0, 2).map((conceptId) => <span key={`${candidate.id}-${conceptId}`} className="badge badge-green">{conceptId}</span>)}
|
|
288
290
|
</div>
|
|
289
291
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 12 }}>
|
|
290
292
|
<button
|
|
@@ -413,6 +415,7 @@ export default function TopologyClient({
|
|
|
413
415
|
<span className={`badge badge-${toneForChange(candidate.changeType)}`}>{candidate.changeType}</span>
|
|
414
416
|
<span className="badge badge-gray">{candidate.source}</span>
|
|
415
417
|
<span className="badge badge-gray">confidence {(candidate.confidence * 100).toFixed(0)}%</span>
|
|
418
|
+
{(candidate.semanticConceptIds ?? []).slice(0, 2).map((conceptId) => <span key={`${candidate.id}-${conceptId}`} className="badge badge-green">{conceptId}</span>)}
|
|
416
419
|
</div>
|
|
417
420
|
{candidate.latestDecision ? (
|
|
418
421
|
<div style={{ display: 'grid', gap: 4, marginTop: 10 }}>
|
|
@@ -465,6 +468,7 @@ export default function TopologyClient({
|
|
|
465
468
|
<span className="badge badge-gray">{candidate.source}</span>
|
|
466
469
|
<span className="badge badge-gray">confidence {(candidate.confidence * 100).toFixed(0)}%</span>
|
|
467
470
|
<span className="badge badge-gray">risk {(candidate.riskScore * 100).toFixed(0)}%</span>
|
|
471
|
+
{(candidate.semanticConceptIds ?? []).slice(0, 2).map((conceptId) => <span key={`${candidate.id}-${conceptId}`} className="badge badge-green">{conceptId}</span>)}
|
|
468
472
|
{(candidate.projectIds ?? []).slice(0, 3).map((projectId) => <span key={`${candidate.id}-${projectId}`} className="badge badge-gray">{projectId}</span>)}
|
|
469
473
|
</div>
|
|
470
474
|
<div style={{ display: 'grid', gap: 4, marginTop: 10 }}>
|
|
@@ -9,15 +9,19 @@ interface NavItem {
|
|
|
9
9
|
icon: string
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const OPERATE_NAV: NavItem[] = [
|
|
13
13
|
{ href: '/', label: 'Overview', icon: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-4 0a1 1 0 01-1-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 01-1 1' },
|
|
14
|
+
{ href: '/projects', label: 'Projects', icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z' },
|
|
15
|
+
{ href: '/research', label: 'Research', icon: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z' },
|
|
16
|
+
{ href: '/coevolution', label: 'Co-Evolution', icon: 'M4 12h4m8 0h4M12 4v4m0 8v4M7.05 7.05l2.83 2.83m4.24 4.24l2.83 2.83m0-9.9l-2.83 2.83m-4.24 4.24l-2.83 2.83' },
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
const STRUCTURE_NAV: NavItem[] = [
|
|
14
20
|
{ href: '/network', label: 'Skill Network', icon: 'M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1' },
|
|
21
|
+
{ href: '/ontology', label: 'Ontology', icon: 'M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3zm0 4.2l-4 2.25v4.95l4 2.25 4-2.25V9.45L12 7.2zm0 0V21' },
|
|
15
22
|
{ href: '/topology', label: 'Topology', icon: 'M4 7h6m4 0h6M7 4v6m10-6v6M5 17h14M12 10v10' },
|
|
16
|
-
{ href: '/coevolution', label: 'Co-Evolution', icon: 'M4 12h4m8 0h4M12 4v4m0 8v4M7.05 7.05l2.83 2.83m4.24 4.24l2.83 2.83m0-9.9l-2.83 2.83m-4.24 4.24l-2.83 2.83' },
|
|
17
23
|
{ href: '/evolution', label: 'Evolution', icon: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6' },
|
|
18
|
-
{ href: '/research', label: 'Research', icon: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z' },
|
|
19
24
|
{ href: '/frontier', label: 'Frontier', icon: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z' },
|
|
20
|
-
{ href: '/projects', label: 'Projects', icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z' },
|
|
21
25
|
]
|
|
22
26
|
|
|
23
27
|
const REFERENCE_NAV: NavItem[] = [
|
|
@@ -70,7 +74,8 @@ export function SidebarNav() {
|
|
|
70
74
|
|
|
71
75
|
return (
|
|
72
76
|
<>
|
|
73
|
-
<NavGroup title="
|
|
77
|
+
<NavGroup title="Operate" items={OPERATE_NAV} pathname={pathname} />
|
|
78
|
+
<NavGroup title="Structure" items={STRUCTURE_NAV} pathname={pathname} />
|
|
74
79
|
<NavGroup title="Reference" items={REFERENCE_NAV} pathname={pathname} />
|
|
75
80
|
</>
|
|
76
81
|
)
|