helixevo 0.7.0 → 0.8.1
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 +22 -0
- package/README.md +14 -6
- package/dashboard/app/api/run/route.ts +19 -1
- package/dashboard/app/commands/page.tsx +28 -4
- package/dashboard/app/guide/page.tsx +44 -5
- package/dashboard/app/page.tsx +254 -135
- package/dashboard/app/proof/client.tsx +56 -3
- package/dashboard/app/topology/client.tsx +39 -0
- package/dashboard/lib/data.ts +177 -0
- package/dashboard/lib/release-spotlight.ts +10 -10
- package/dist/cli.js +1249 -225
- package/package.json +2 -2
package/dashboard/app/page.tsx
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import Link from 'next/link'
|
|
2
|
-
import { getDashboardSummary, getOntologyDashboardSummary, loadCoEvolutionSummary, loadFailures, loadFrontier, loadGraph, loadHistory, listProjects } from '@/lib/data'
|
|
2
|
+
import { getDashboardSummary, getOntologyDashboardSummary, loadCoEvolutionSummary, loadFailures, loadFrontier, loadGraph, loadHistory, listProjects, loadTopologyDashboardSummary, loadLlmRuntimeState } from '@/lib/data'
|
|
3
3
|
import { loadProofDashboardSummary } from '@/lib/proof'
|
|
4
4
|
import { OverviewActions } from '@/components/overview-actions'
|
|
5
5
|
import { PageHero } from '@/components/page-hero'
|
|
6
6
|
import { MetricCard } from '@/components/metric-card'
|
|
7
7
|
import { SectionFrame } from '@/components/section-frame'
|
|
8
8
|
import { OperatorLoopTrail } from '@/components/operator-loop-trail'
|
|
9
|
-
import { SurfaceJumpLinks } from '@/components/surface-jump-links'
|
|
10
|
-
import { CURRENT_RELEASE_SPOTLIGHT } from '@/lib/release-spotlight'
|
|
11
9
|
|
|
12
10
|
export const dynamic = 'force-dynamic'
|
|
13
11
|
|
|
@@ -17,10 +15,94 @@ function scoreColor(score: number) {
|
|
|
17
15
|
return 'var(--red)'
|
|
18
16
|
}
|
|
19
17
|
|
|
18
|
+
function providerLabel(provider: 'claude-code' | 'codex' | 'ollama') {
|
|
19
|
+
if (provider === 'claude-code') return 'Claude Code'
|
|
20
|
+
if (provider === 'codex') return 'GPT Codex'
|
|
21
|
+
return 'Ollama'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function providerTone(status: 'healthy' | 'degraded' | 'unavailable' | 'unknown') {
|
|
25
|
+
if (status === 'healthy') return 'green' as const
|
|
26
|
+
if (status === 'degraded') return 'yellow' as const
|
|
27
|
+
if (status === 'unavailable') return 'red' as const
|
|
28
|
+
return 'neutral' as const
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getPriorityActions(params: {
|
|
32
|
+
unresolved: number
|
|
33
|
+
proofOpen: number
|
|
34
|
+
topologyOpen: number
|
|
35
|
+
optimizeStatus: 'idle' | 'healthy' | 'partial' | 'failed'
|
|
36
|
+
optimizeNextStep?: string
|
|
37
|
+
providerStatus: 'healthy' | 'degraded' | 'unavailable' | 'unknown'
|
|
38
|
+
providerSummary: string
|
|
39
|
+
providerNextStep?: string
|
|
40
|
+
}) {
|
|
41
|
+
const actions: Array<{ href: string; title: string; description: string; tone: 'blue' | 'green' | 'purple' | 'yellow' }> = []
|
|
42
|
+
|
|
43
|
+
if (params.providerStatus === 'degraded' || params.providerStatus === 'unavailable') {
|
|
44
|
+
actions.push({
|
|
45
|
+
href: '/commands',
|
|
46
|
+
title: 'Stabilize provider control',
|
|
47
|
+
description: params.providerNextStep ?? params.providerSummary,
|
|
48
|
+
tone: 'yellow',
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (params.optimizeStatus === 'partial' || params.optimizeStatus === 'failed') {
|
|
53
|
+
actions.push({
|
|
54
|
+
href: '/topology',
|
|
55
|
+
title: 'Review degraded optimize state',
|
|
56
|
+
description: params.optimizeNextStep ?? 'The review queue refreshed, but structural enrichment did not complete fully. Inspect topology control before treating the queue as fully enriched.',
|
|
57
|
+
tone: 'yellow',
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (params.proofOpen > 0) {
|
|
62
|
+
actions.push({
|
|
63
|
+
href: '/proof',
|
|
64
|
+
title: 'Review open proof records',
|
|
65
|
+
description: `${params.proofOpen} proof record${params.proofOpen === 1 ? '' : 's'} currently need operator review before the prove layer becomes more trustworthy.`,
|
|
66
|
+
tone: 'blue',
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (params.topologyOpen > 0) {
|
|
71
|
+
actions.push({
|
|
72
|
+
href: '/topology',
|
|
73
|
+
title: 'Triage structural backlog',
|
|
74
|
+
description: `${params.topologyOpen} topology review item${params.topologyOpen === 1 ? '' : 's'} are waiting for accept/defer/reject decisions.`,
|
|
75
|
+
tone: 'purple',
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (params.unresolved > 0) {
|
|
80
|
+
actions.push({
|
|
81
|
+
href: '/coevolution',
|
|
82
|
+
title: 'Route live pressure',
|
|
83
|
+
description: `${params.unresolved} unresolved correction${params.unresolved === 1 ? '' : 's'} still need to be routed back into the learning loop.`,
|
|
84
|
+
tone: 'green',
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (actions.length === 0) {
|
|
89
|
+
actions.push({
|
|
90
|
+
href: '/projects',
|
|
91
|
+
title: 'Setup a project',
|
|
92
|
+
description: 'Analyze a folder or GitHub repo, match skills, and identify capability gaps to seed the next loop.',
|
|
93
|
+
tone: 'green',
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return actions.slice(0, 3)
|
|
98
|
+
}
|
|
99
|
+
|
|
20
100
|
export default function Overview() {
|
|
21
101
|
const summary = getDashboardSummary()
|
|
22
102
|
const ontology = getOntologyDashboardSummary()
|
|
23
103
|
const coevolution = loadCoEvolutionSummary()
|
|
104
|
+
const topologyControl = loadTopologyDashboardSummary()
|
|
105
|
+
const llmRuntime = loadLlmRuntimeState()
|
|
24
106
|
const proof = loadProofDashboardSummary()
|
|
25
107
|
const frontier = loadFrontier()
|
|
26
108
|
const history = loadHistory()
|
|
@@ -29,6 +111,37 @@ export default function Overview() {
|
|
|
29
111
|
const unresolved = failures.filter((failure) => !failure.resolved)
|
|
30
112
|
const recentRuns = history.iterations.slice(-4).reverse()
|
|
31
113
|
const topSkills = [...graph.nodes].sort((a, b) => b.score - a.score).slice(0, 10)
|
|
114
|
+
const defaultProvider = llmRuntime.providers[llmRuntime.defaultProvider]
|
|
115
|
+
const lastExecutionUsedFallback = Boolean(llmRuntime.lastExecution?.fallbackUsed)
|
|
116
|
+
const isProviderDegraded =
|
|
117
|
+
defaultProvider.status === 'degraded' ||
|
|
118
|
+
defaultProvider.status === 'unavailable'
|
|
119
|
+
const showProviderAttention = isProviderDegraded || lastExecutionUsedFallback
|
|
120
|
+
const providerAttentionTone = defaultProvider.status === 'unavailable' ? 'red' : 'yellow'
|
|
121
|
+
const providerAttentionEyebrow = isProviderDegraded ? 'Provider attention' : 'Recent provider deviation'
|
|
122
|
+
const providerAttentionTitle = defaultProvider.status === 'unavailable'
|
|
123
|
+
? 'Backend recovery needed'
|
|
124
|
+
: isProviderDegraded
|
|
125
|
+
? 'Live backend truth'
|
|
126
|
+
: 'Fallback used recently'
|
|
127
|
+
const providerAttentionDescription = isProviderDegraded
|
|
128
|
+
? lastExecutionUsedFallback
|
|
129
|
+
? 'The default provider currently needs attention and a recent provider-backed run used explicit fallback. Review runtime health before treating execution as fully healthy or default-aligned.'
|
|
130
|
+
: 'The default provider currently needs attention. Review runtime health before treating provider-backed execution as fully healthy.'
|
|
131
|
+
: llmRuntime.lastExecution?.usedProvider
|
|
132
|
+
? `A recent provider-backed run did not stay on the selected default provider. The default remained ${providerLabel(llmRuntime.defaultProvider)}, but the last successful execution used ${providerLabel(llmRuntime.lastExecution.usedProvider)}. Review provider health and fallback policy before assuming default-provider execution.`
|
|
133
|
+
: 'A recent provider-backed run used explicit fallback instead of staying on the selected default provider. Review provider health and fallback policy before assuming default-provider execution.'
|
|
134
|
+
const priorityActions = getPriorityActions({
|
|
135
|
+
unresolved: summary.failures.unresolved,
|
|
136
|
+
proofOpen: proof.summary.reviewOpen,
|
|
137
|
+
topologyOpen: topologyControl.summary.open,
|
|
138
|
+
optimizeStatus: topologyControl.optimizeStatus.status,
|
|
139
|
+
optimizeNextStep: topologyControl.optimizeStatus.nextStep,
|
|
140
|
+
providerStatus: defaultProvider.status,
|
|
141
|
+
providerSummary: defaultProvider.summary,
|
|
142
|
+
providerNextStep: defaultProvider.nextStep,
|
|
143
|
+
})
|
|
144
|
+
const primaryAction = priorityActions[0]
|
|
32
145
|
|
|
33
146
|
return (
|
|
34
147
|
<div className="overview-grid">
|
|
@@ -37,27 +150,23 @@ export default function Overview() {
|
|
|
37
150
|
title="Dashboard"
|
|
38
151
|
description="Monitor the live state of your self-evolving skill ecosystem, launch high-value actions, and trace how failures turn into frontier-worthy improvements."
|
|
39
152
|
chips={[
|
|
40
|
-
{ label: `${summary.skills.total} skills`, tone: 'purple' },
|
|
41
153
|
{ label: `${summary.failures.unresolved} unresolved corrections`, tone: summary.failures.unresolved > 0 ? 'yellow' : 'green' },
|
|
42
|
-
{ label: `${summary.evolution.runs} evolution runs`, tone: 'blue' },
|
|
43
|
-
{ label: `${frontier.programs.length}/${frontier.capacity} frontier slots`, tone: 'green' },
|
|
44
|
-
{ label: `${coevolution.pressureMotifs.promotionReady} promotion-ready motifs`, tone: coevolution.pressureMotifs.promotionReady > 0 ? 'purple' : 'neutral' },
|
|
45
|
-
{ label: `${coevolution.topologyReviews.open} topology reviews`, tone: coevolution.topologyReviews.open > 0 ? 'yellow' : 'green' },
|
|
46
|
-
{ label: `${coevolution.topologyExecution.prepared} prepared structural plans`, tone: coevolution.topologyExecution.prepared > 0 ? 'blue' : 'neutral' },
|
|
47
154
|
{ label: `${proof.summary.reviewOpen} proof reviews`, tone: proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral' },
|
|
155
|
+
{ label: `${coevolution.topologyReviews.open} topology reviews`, tone: coevolution.topologyReviews.open > 0 ? 'yellow' : 'green' },
|
|
156
|
+
{ label: `${providerLabel(llmRuntime.defaultProvider)} ${defaultProvider.status}`, tone: providerTone(defaultProvider.status) },
|
|
48
157
|
{ label: `mode: ${coevolution.governance.activeMode.replace(/-/g, ' ')}`, tone: coevolution.governance.activeMode === 'transfer-focused' ? 'purple' : coevolution.governance.activeMode === 'project-critical' ? 'yellow' : 'blue' },
|
|
49
158
|
]}
|
|
50
159
|
actions={
|
|
51
|
-
<Link href=
|
|
52
|
-
<div className=
|
|
160
|
+
<Link href={primaryAction.href} className="metric-card-anchor" style={{ minWidth: 260, display: 'block' }}>
|
|
161
|
+
<div className={`metric-card metric-card-${primaryAction.tone} metric-card-link`}>
|
|
53
162
|
<div className="metric-card-header">
|
|
54
163
|
<div>
|
|
55
|
-
<div className="metric-card-label">
|
|
56
|
-
<div className="metric-card-value" style={{ fontSize: 24 }}>
|
|
164
|
+
<div className="metric-card-label">Priority now</div>
|
|
165
|
+
<div className="metric-card-value" style={{ fontSize: 24 }}>{primaryAction.title}</div>
|
|
57
166
|
</div>
|
|
58
167
|
<div className="metric-card-icon">↗</div>
|
|
59
168
|
</div>
|
|
60
|
-
<div className="metric-card-sublabel">
|
|
169
|
+
<div className="metric-card-sublabel">{primaryAction.description}</div>
|
|
61
170
|
</div>
|
|
62
171
|
</Link>
|
|
63
172
|
}
|
|
@@ -65,38 +174,98 @@ export default function Overview() {
|
|
|
65
174
|
|
|
66
175
|
<OperatorLoopTrail surface="overview" />
|
|
67
176
|
|
|
68
|
-
<div
|
|
69
|
-
<MetricCard label="Total skills" value={summary.skills.total} sublabel={`${summary.skills.evolved} evolved • ${summary.skillTests} skill tests`} tone="purple" href="/network" icon="◆" />
|
|
70
|
-
<MetricCard label="Accepted proposals" value={summary.evolution.accepted} sublabel={`${summary.evolution.rejected} rejected`} tone="green" href="/evolution" icon="✓" />
|
|
177
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
|
|
71
178
|
<MetricCard label="Unresolved corrections" value={summary.failures.unresolved} sublabel={`out of ${summary.failures.total} captured failures`} tone={summary.failures.unresolved > 0 ? 'yellow' : 'green'} href={summary.failures.unresolved > 0 ? '#attention' : '/evolution'} icon="!" />
|
|
72
|
-
<MetricCard label="Discoveries" value={summary.buffer.discoveries} sublabel={`${summary.buffer.drafts} drafts in progress`} tone="blue" href="/research" icon="◎" />
|
|
73
|
-
<MetricCard label="Frontier candidates" value={frontier.programs.length} sublabel={`${summary.canaries} active canaries`} tone="neutral" href="/frontier" icon="▲" />
|
|
74
179
|
<MetricCard label="Proof review" value={proof.summary.reviewOpen} sublabel={`${proof.summary.effective} effective • ${proof.summary.regressed} regressed`} tone={proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral'} href="/proof" icon="◇" />
|
|
180
|
+
<MetricCard
|
|
181
|
+
label="Open pressure"
|
|
182
|
+
value={ontology.pressureLifecycle.open}
|
|
183
|
+
sublabel={`${ontology.pressureLifecycle.inProgress} in progress • ${ontology.pressureLifecycle.addressed} addressed`}
|
|
184
|
+
tone={ontology.pressureLifecycle.open > 0 ? 'yellow' : 'green'}
|
|
185
|
+
href="/coevolution"
|
|
186
|
+
icon="!"
|
|
187
|
+
/>
|
|
188
|
+
<MetricCard
|
|
189
|
+
label="Topology review"
|
|
190
|
+
value={ontology.topologyReviews.open}
|
|
191
|
+
sublabel={`${ontology.topologyExecution.prepared} prepared • ${ontology.topologyExecution.applied} applied`}
|
|
192
|
+
tone={ontology.topologyReviews.open > 0 ? 'yellow' : ontology.topologyExecution.applied > 0 ? 'green' : 'blue'}
|
|
193
|
+
href="/topology"
|
|
194
|
+
icon="⇄"
|
|
195
|
+
/>
|
|
196
|
+
<MetricCard
|
|
197
|
+
label="Ontology frontier"
|
|
198
|
+
value={ontology.ontologyLoop.frontier}
|
|
199
|
+
sublabel={`${ontology.ontologyLoop.reviewOpen} open • ${ontology.ontologyLoop.extensions} approved extensions`}
|
|
200
|
+
tone={ontology.ontologyLoop.reviewOpen > 0 ? 'yellow' : ontology.ontologyLoop.extensions > 0 ? 'blue' : 'neutral'}
|
|
201
|
+
href="/ontology"
|
|
202
|
+
icon="◎"
|
|
203
|
+
/>
|
|
204
|
+
<MetricCard
|
|
205
|
+
label="Active semantic concepts"
|
|
206
|
+
value={ontology.ontologyLoop.adoption.activeConcepts}
|
|
207
|
+
sublabel={`${ontology.ontologyLoop.adoption.totalBindings} bindings • ${ontology.ontologyLoop.adoption.routesInfluenced} influenced routes`}
|
|
208
|
+
tone={ontology.ontologyLoop.adoption.activeConcepts > 0 ? 'green' : 'neutral'}
|
|
209
|
+
href="/ontology"
|
|
210
|
+
icon="⇄"
|
|
211
|
+
/>
|
|
75
212
|
</div>
|
|
76
213
|
|
|
77
214
|
<SectionFrame
|
|
78
|
-
eyebrow=
|
|
79
|
-
title=
|
|
80
|
-
description=
|
|
215
|
+
eyebrow="Priority now"
|
|
216
|
+
title="Top ranked next actions"
|
|
217
|
+
description="This layer compresses the live loop into the most important operator moves right now instead of leaving everything as flat dashboard signal."
|
|
81
218
|
tone="blue"
|
|
82
219
|
>
|
|
83
|
-
<div className="
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
<div
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
))}
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
<SurfaceJumpLinks surface="overview" variant="panel" title="Jump into the updated loop" />
|
|
220
|
+
<div className="summary-list">
|
|
221
|
+
{priorityActions.map((action, index) => (
|
|
222
|
+
<Link key={`${action.href}-${index}`} href={action.href} className="summary-row">
|
|
223
|
+
<div className="summary-row-main">
|
|
224
|
+
<div className="summary-row-title">{index + 1}. {action.title}</div>
|
|
225
|
+
<div className="summary-row-meta">{action.description}</div>
|
|
226
|
+
</div>
|
|
227
|
+
<span className={`hero-chip hero-chip-${action.tone}`}>open</span>
|
|
228
|
+
</Link>
|
|
229
|
+
))}
|
|
97
230
|
</div>
|
|
98
231
|
</SectionFrame>
|
|
99
232
|
|
|
233
|
+
{unresolved.length > 0 ? (
|
|
234
|
+
<SectionFrame
|
|
235
|
+
eyebrow="Attention"
|
|
236
|
+
title="Unresolved corrections"
|
|
237
|
+
description="These user corrections have been captured but not yet folded back into the skill network."
|
|
238
|
+
tone="yellow"
|
|
239
|
+
className="anchor-target"
|
|
240
|
+
>
|
|
241
|
+
<div id="attention" className="signal-list">
|
|
242
|
+
{unresolved.slice(0, 5).map((failure, index) => (
|
|
243
|
+
<div key={`${failure.id}-${index}`} className="signal-row signal-row-attention">
|
|
244
|
+
<div className="signal-dot" />
|
|
245
|
+
<div style={{ flex: 1 }}>
|
|
246
|
+
<div className="signal-title">
|
|
247
|
+
{failure.userRequest.slice(0, 120)}
|
|
248
|
+
{failure.userRequest.length > 120 ? '…' : ''}
|
|
249
|
+
</div>
|
|
250
|
+
<div className="signal-text">
|
|
251
|
+
Correction: {failure.correction.slice(0, 180)}
|
|
252
|
+
{failure.correction.length > 180 ? '…' : ''}
|
|
253
|
+
</div>
|
|
254
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
|
|
255
|
+
<span className="badge badge-yellow">{failure.correctionType}</span>
|
|
256
|
+
{failure.project ? <span className="badge badge-gray">{failure.project}</span> : null}
|
|
257
|
+
<span className="badge badge-gray">{new Date(failure.timestamp).toLocaleDateString()}</span>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
))}
|
|
262
|
+
{unresolved.length > 5 ? (
|
|
263
|
+
<div className="signal-text" style={{ textAlign: 'center' }}>+{unresolved.length - 5} more unresolved corrections</div>
|
|
264
|
+
) : null}
|
|
265
|
+
</div>
|
|
266
|
+
</SectionFrame>
|
|
267
|
+
) : null}
|
|
268
|
+
|
|
100
269
|
<SectionFrame
|
|
101
270
|
eyebrow="Execution layer"
|
|
102
271
|
title="Quick actions"
|
|
@@ -112,37 +281,60 @@ export default function Overview() {
|
|
|
112
281
|
/>
|
|
113
282
|
</SectionFrame>
|
|
114
283
|
|
|
284
|
+
{showProviderAttention ? (
|
|
285
|
+
<SectionFrame
|
|
286
|
+
eyebrow={providerAttentionEyebrow}
|
|
287
|
+
title={providerAttentionTitle}
|
|
288
|
+
description={providerAttentionDescription}
|
|
289
|
+
tone={providerAttentionTone}
|
|
290
|
+
actions={<Link href="/commands" className="badge badge-gray">Open commands</Link>}
|
|
291
|
+
>
|
|
292
|
+
<div className="grid-2" style={{ gap: 16 }}>
|
|
293
|
+
<div className="summary-list">
|
|
294
|
+
{(['claude-code', 'codex', 'ollama'] as const).map((provider) => {
|
|
295
|
+
const snapshot = llmRuntime.providers[provider]
|
|
296
|
+
return (
|
|
297
|
+
<div key={provider} className="summary-row">
|
|
298
|
+
<div className="summary-row-main">
|
|
299
|
+
<div className="summary-row-title">{providerLabel(provider)}</div>
|
|
300
|
+
<div className="summary-row-meta">{snapshot.summary}</div>
|
|
301
|
+
{snapshot.nextStep ? <div className="summary-row-meta" style={{ marginTop: 6 }}>Next: {snapshot.nextStep}</div> : null}
|
|
302
|
+
</div>
|
|
303
|
+
<span className={`hero-chip hero-chip-${providerTone(snapshot.status)}`}>{snapshot.status}</span>
|
|
304
|
+
</div>
|
|
305
|
+
)
|
|
306
|
+
})}
|
|
307
|
+
</div>
|
|
308
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
309
|
+
<MetricCard
|
|
310
|
+
label="Default provider"
|
|
311
|
+
value={providerLabel(llmRuntime.defaultProvider)}
|
|
312
|
+
sublabel={`fallback: ${llmRuntime.fallbackPolicy}${llmRuntime.fallbackOrder.length > 0 ? ` • ${llmRuntime.fallbackOrder.map((provider) => providerLabel(provider)).join(' → ')}` : ''}`}
|
|
313
|
+
tone={providerTone(defaultProvider.status)}
|
|
314
|
+
href="/commands"
|
|
315
|
+
icon="☍"
|
|
316
|
+
/>
|
|
317
|
+
<MetricCard
|
|
318
|
+
label="Last provider run"
|
|
319
|
+
value={llmRuntime.lastExecution?.usedProvider ? providerLabel(llmRuntime.lastExecution.usedProvider) : 'None'}
|
|
320
|
+
sublabel={llmRuntime.lastExecution ? llmRuntime.lastExecution.summary : 'No provider-backed execution has been recorded yet.'}
|
|
321
|
+
tone={llmRuntime.lastExecution?.success ? 'green' : llmRuntime.lastExecution ? 'yellow' : 'neutral'}
|
|
322
|
+
href="/commands"
|
|
323
|
+
icon="↺"
|
|
324
|
+
/>
|
|
325
|
+
<div className="signal-text">Commands, status, and dashboard surfaces now track whether execution stayed on the selected provider, degraded, or used an explicit fallback path.</div>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
</SectionFrame>
|
|
329
|
+
) : null}
|
|
330
|
+
|
|
115
331
|
<SectionFrame
|
|
116
|
-
eyebrow="Brain
|
|
332
|
+
eyebrow="Brain summary"
|
|
117
333
|
title="Semantic backbone"
|
|
118
|
-
description="
|
|
334
|
+
description="Compact summary of pressure, response, structural review, ontology growth, and semantic adoption across the living brain."
|
|
119
335
|
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>}
|
|
120
336
|
>
|
|
121
337
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
|
|
122
|
-
<MetricCard
|
|
123
|
-
label="Generalist skills"
|
|
124
|
-
value={ontology.skillRoles.generalist}
|
|
125
|
-
sublabel={`${ontology.stabilityStates.stable} stable abstractions`}
|
|
126
|
-
tone="purple"
|
|
127
|
-
href="/network"
|
|
128
|
-
icon="◎"
|
|
129
|
-
/>
|
|
130
|
-
<MetricCard
|
|
131
|
-
label="Project specialists"
|
|
132
|
-
value={ontology.skillRoles.specialist}
|
|
133
|
-
sublabel={`${ontology.plasticityStates.candidate} candidate rewires`}
|
|
134
|
-
tone="blue"
|
|
135
|
-
href="/projects"
|
|
136
|
-
icon="◉"
|
|
137
|
-
/>
|
|
138
|
-
<MetricCard
|
|
139
|
-
label="Hybrid bridges"
|
|
140
|
-
value={ontology.skillRoles.hybrid}
|
|
141
|
-
sublabel={`${ontology.plasticityStates.volatile} volatile transitions`}
|
|
142
|
-
tone="green"
|
|
143
|
-
href="/network"
|
|
144
|
-
icon="⇄"
|
|
145
|
-
/>
|
|
146
338
|
<MetricCard
|
|
147
339
|
label="Open pressure"
|
|
148
340
|
value={ontology.pressureLifecycle.open}
|
|
@@ -159,14 +351,6 @@ export default function Overview() {
|
|
|
159
351
|
href="/coevolution"
|
|
160
352
|
icon="↺"
|
|
161
353
|
/>
|
|
162
|
-
<MetricCard
|
|
163
|
-
label="Pressure motifs"
|
|
164
|
-
value={ontology.pressureMotifs.total}
|
|
165
|
-
sublabel={`${ontology.pressureMotifs.promotionReady} promotion-ready • ${ontology.transferEvents.realized} realized transfers`}
|
|
166
|
-
tone="purple"
|
|
167
|
-
href="/coevolution"
|
|
168
|
-
icon="⇄"
|
|
169
|
-
/>
|
|
170
354
|
<MetricCard
|
|
171
355
|
label="Topology review"
|
|
172
356
|
value={ontology.topologyReviews.open}
|
|
@@ -191,81 +375,16 @@ export default function Overview() {
|
|
|
191
375
|
href="/ontology"
|
|
192
376
|
icon="⇄"
|
|
193
377
|
/>
|
|
194
|
-
<MetricCard
|
|
195
|
-
label="Observed mutation verbs"
|
|
196
|
-
value={ontology.mutationOperationsObserved}
|
|
197
|
-
sublabel={ontology.observedMutationActions.length > 0 ? ontology.observedMutationActions.join(' • ') : 'Waiting for evolution history'}
|
|
198
|
-
tone="yellow"
|
|
199
|
-
href="/evolution"
|
|
200
|
-
icon="◇"
|
|
201
|
-
/>
|
|
202
378
|
</div>
|
|
203
379
|
|
|
204
380
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 16 }}>
|
|
205
|
-
<span className="badge badge-gray">{ontology.relationFamiliesObserved} relation families live in the graph</span>
|
|
206
|
-
<span className="badge badge-gray">{ontology.evidenceBackedProposals} proposals already carry judge + regression evidence</span>
|
|
207
|
-
<span className="badge badge-gray">{ontology.artifacts.total} evolution artifacts ({ontology.artifacts.native} native • {ontology.artifacts.derived} derived)</span>
|
|
208
|
-
<span className="badge badge-gray">{ontology.activationTraces.total} activation traces ({ontology.activationTraces.native} native • {ontology.activationTraces.derived} derived)</span>
|
|
209
|
-
<span className="badge badge-gray">{ontology.pressureSignals.total} pressure signals ({ontology.pressureSignals.native} native • {ontology.pressureSignals.derived} derived)</span>
|
|
210
|
-
<span className="badge badge-gray">{ontology.pressureInterventions.total} response interventions logged</span>
|
|
211
|
-
<span className="badge badge-gray">{ontology.pressureLifecycle.open} open • {ontology.pressureLifecycle.inProgress} in progress • {ontology.pressureLifecycle.addressed} addressed</span>
|
|
212
381
|
<Link href="/coevolution" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open co-evolution control</Link>
|
|
213
|
-
<span className="badge badge-gray">{coevolution.topProjects.length} pressured project hotspots</span>
|
|
214
|
-
<span className="badge badge-gray">{coevolution.crossProjectGapAreas.length} cross-project gap motifs</span>
|
|
215
|
-
<span className="badge badge-gray">{ontology.pressureMotifs.promotionReady} motifs currently recommend generalize</span>
|
|
216
|
-
<span className="badge badge-gray">{ontology.transferEvents.total} transfer events logged ({ontology.transferEvents.realized} realized)</span>
|
|
217
|
-
<span className="badge badge-gray">ontology → {ontology.ontologyLoop.frontier} frontier • {ontology.ontologyLoop.extensions} extensions • {ontology.ontologyLoop.changeEvents} changes</span>
|
|
218
|
-
<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>
|
|
219
|
-
<span className="badge badge-gray">deprecation risk → {ontology.ontologyLoop.adoption.conceptsAtDeprecationRisk} concepts • {ontology.ontologyLoop.adoption.unusedExtensions} unused extensions</span>
|
|
220
|
-
<span className="badge badge-gray">topology → {ontology.topologyReviews.open} open • {ontology.topologyReviews.accepted} accepted • {ontology.topologyReviews.generatedFromManualReview} manual-route</span>
|
|
221
|
-
<span className="badge badge-gray">execution → {ontology.topologyExecution.prepared} prepared • {ontology.topologyExecution.applied} applied • {ontology.topologyExecution.rolledBack} rolled back</span>
|
|
222
|
-
<span className="badge badge-gray">proof → {proof.summary.total} total • {proof.summary.effective} effective • {proof.summary.reviewOpen} open review</span>
|
|
223
382
|
<Link href="/ontology" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open ontology control</Link>
|
|
224
383
|
<Link href="/topology" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open topology control</Link>
|
|
225
384
|
<Link href="/proof" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open proof control</Link>
|
|
226
|
-
<span className="badge badge-gray">governance: {ontology.governance.activeMode.replace(/-/g, ' ')} ({ontology.governance.source})</span>
|
|
227
|
-
<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>
|
|
228
|
-
<span className="badge badge-gray">{ontology.enrichedSkillNodes} skills carry explicit brain metadata</span>
|
|
229
|
-
<span className="badge badge-gray">{ontology.annotatedFailures.pressureSignals} failures annotated with pressure signals</span>
|
|
230
385
|
</div>
|
|
231
386
|
</SectionFrame>
|
|
232
387
|
|
|
233
|
-
{unresolved.length > 0 ? (
|
|
234
|
-
<SectionFrame
|
|
235
|
-
eyebrow="Attention"
|
|
236
|
-
title="Unresolved corrections"
|
|
237
|
-
description="These user corrections have been captured but not yet folded back into the skill network."
|
|
238
|
-
tone="yellow"
|
|
239
|
-
className="anchor-target"
|
|
240
|
-
>
|
|
241
|
-
<div id="attention" className="signal-list">
|
|
242
|
-
{unresolved.slice(0, 5).map((failure, index) => (
|
|
243
|
-
<div key={`${failure.id}-${index}`} className="signal-row signal-row-attention">
|
|
244
|
-
<div className="signal-dot" />
|
|
245
|
-
<div style={{ flex: 1 }}>
|
|
246
|
-
<div className="signal-title">
|
|
247
|
-
{failure.userRequest.slice(0, 120)}
|
|
248
|
-
{failure.userRequest.length > 120 ? '…' : ''}
|
|
249
|
-
</div>
|
|
250
|
-
<div className="signal-text">
|
|
251
|
-
Correction: {failure.correction.slice(0, 180)}
|
|
252
|
-
{failure.correction.length > 180 ? '…' : ''}
|
|
253
|
-
</div>
|
|
254
|
-
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
|
|
255
|
-
<span className="badge badge-yellow">{failure.correctionType}</span>
|
|
256
|
-
{failure.project ? <span className="badge badge-gray">{failure.project}</span> : null}
|
|
257
|
-
<span className="badge badge-gray">{new Date(failure.timestamp).toLocaleDateString()}</span>
|
|
258
|
-
</div>
|
|
259
|
-
</div>
|
|
260
|
-
</div>
|
|
261
|
-
))}
|
|
262
|
-
{unresolved.length > 5 ? (
|
|
263
|
-
<div className="signal-text" style={{ textAlign: 'center' }}>+{unresolved.length - 5} more unresolved corrections</div>
|
|
264
|
-
) : null}
|
|
265
|
-
</div>
|
|
266
|
-
</SectionFrame>
|
|
267
|
-
) : null}
|
|
268
|
-
|
|
269
388
|
<div className="grid-2">
|
|
270
389
|
<SectionFrame
|
|
271
390
|
eyebrow="Frontier"
|
|
@@ -51,11 +51,42 @@ function consoleTone(state: RunState): 'neutral' | 'green' | 'red' | 'yellow' {
|
|
|
51
51
|
return 'neutral'
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function getRecommendedProofMove(dashboard: ProofDashboardSummary) {
|
|
55
|
+
const firstOpen = dashboard.reviewQueue[0]
|
|
56
|
+
if (firstOpen) {
|
|
57
|
+
return {
|
|
58
|
+
label: 'Review queue first',
|
|
59
|
+
title: firstOpen.title,
|
|
60
|
+
copy: 'Open proof records should usually be resolved before you treat the prove layer as settled. Verify, defer, or contest the most important record first.',
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (dashboard.summary.regressed > 0) {
|
|
64
|
+
return {
|
|
65
|
+
label: 'Investigate regressed evidence',
|
|
66
|
+
title: 'A regressed outcome needs explanation before retry',
|
|
67
|
+
copy: 'Regressed records mean HelixEvo has explicit negative evidence. Inspect the linked structural or intervention path before repeating the same move.',
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (dashboard.summary.measuring > 0) {
|
|
71
|
+
return {
|
|
72
|
+
label: 'Let evidence mature',
|
|
73
|
+
title: 'Measuring means live, not yet proven',
|
|
74
|
+
copy: 'A measuring record is active but still too recent or incomplete for a stronger claim. Let downstream evidence accumulate before over-interpreting it.',
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
label: 'Seed proof',
|
|
79
|
+
title: 'No proof targets yet',
|
|
80
|
+
copy: 'Proof becomes useful after live interventions, semantic adoption, transfer, or topology execution create evaluable downstream evidence.',
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
54
84
|
export default function ProofClient({ initialDashboard }: { initialDashboard: ProofDashboardSummary }) {
|
|
55
85
|
const [dashboard, setDashboard] = useState(initialDashboard)
|
|
56
86
|
const [runState, setRunState] = useState<RunState>('idle')
|
|
57
87
|
const [output, setOutput] = useState('')
|
|
58
88
|
const [pendingKey, setPendingKey] = useState<string | null>(null)
|
|
89
|
+
const recommendedMove = getRecommendedProofMove(dashboard)
|
|
59
90
|
|
|
60
91
|
const runReview = async (recordId: string, decision: ProofReviewDecisionStatus, label: string) => {
|
|
61
92
|
setPendingKey(recordId)
|
|
@@ -106,9 +137,9 @@ export default function ProofClient({ initialDashboard }: { initialDashboard: Pr
|
|
|
106
137
|
actions={
|
|
107
138
|
<div style={{ display: 'grid', gap: 12 }}>
|
|
108
139
|
<div className="hero-note-card">
|
|
109
|
-
<div className="hero-note-label">
|
|
110
|
-
<div className="hero-note-title">
|
|
111
|
-
<div className="hero-note-copy">
|
|
140
|
+
<div className="hero-note-label">{recommendedMove.label}</div>
|
|
141
|
+
<div className="hero-note-title">{recommendedMove.title}</div>
|
|
142
|
+
<div className="hero-note-copy">{recommendedMove.copy}</div>
|
|
112
143
|
</div>
|
|
113
144
|
<SurfaceJumpLinks surface="proof" variant="compact" title="Proof handoffs" />
|
|
114
145
|
</div>
|
|
@@ -125,6 +156,28 @@ export default function ProofClient({ initialDashboard }: { initialDashboard: Pr
|
|
|
125
156
|
<MetricCard label="Open review" value={dashboard.summary.reviewOpen} sublabel={`${dashboard.summary.deferred} deferred • ${dashboard.summary.verified} verified`} tone={dashboard.summary.reviewOpen > 0 ? 'yellow' : 'green'} icon="◇" />
|
|
126
157
|
</div>
|
|
127
158
|
|
|
159
|
+
<SectionFrame
|
|
160
|
+
eyebrow="Meaning"
|
|
161
|
+
title="What these proof states mean operationally"
|
|
162
|
+
description="Proof is only useful if the operator can tell what each state should trigger next. Measuring, regressed, and verified are not cosmetic labels — they change what HelixEvo should do next."
|
|
163
|
+
tone="blue"
|
|
164
|
+
>
|
|
165
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12 }}>
|
|
166
|
+
<div className="guide-dimension-card" style={{ borderLeftColor: 'var(--blue)' }}>
|
|
167
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)' }}>Measuring</div>
|
|
168
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.7 }}>Live evidence exists, but HelixEvo should wait for more downstream signal before claiming success.</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div className="guide-dimension-card" style={{ borderLeftColor: 'var(--yellow)' }}>
|
|
171
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)' }}>Regressed</div>
|
|
172
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.7 }}>A move created explicit negative evidence. Inspect the linked route or structural execution before retrying the same strategy.</div>
|
|
173
|
+
</div>
|
|
174
|
+
<div className="guide-dimension-card" style={{ borderLeftColor: 'var(--green)' }}>
|
|
175
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)' }}>Verified</div>
|
|
176
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.7 }}>An operator has reviewed the record explicitly. Verification increases trust in the review state, not necessarily in causal certainty.</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</SectionFrame>
|
|
180
|
+
|
|
128
181
|
<SectionFrame
|
|
129
182
|
eyebrow="Coverage"
|
|
130
183
|
title="Proof now spans the live brain loop"
|
|
@@ -56,6 +56,12 @@ function toneForChange(changeType: string): 'blue' | 'green' | 'purple' | 'yello
|
|
|
56
56
|
return 'neutral'
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function toneForOptimizeStatus(status: TopologyDashboardSummary['optimizeStatus']['status']): 'green' | 'yellow' | 'neutral' {
|
|
60
|
+
if (status === 'healthy') return 'green'
|
|
61
|
+
if (status === 'partial' || status === 'failed') return 'yellow'
|
|
62
|
+
return 'neutral'
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
function consoleTone(state: RunState): 'neutral' | 'green' | 'red' | 'yellow' {
|
|
60
66
|
if (state === 'success') return 'green'
|
|
61
67
|
if (state === 'error') return 'red'
|
|
@@ -247,6 +253,7 @@ export default function TopologyClient({
|
|
|
247
253
|
{ label: `${dashboard.summary.open} open reviews`, tone: dashboard.summary.open > 0 ? 'yellow' : 'green' },
|
|
248
254
|
{ label: `${dashboard.execution.prepared} prepared`, tone: dashboard.execution.prepared > 0 ? 'blue' : 'neutral' },
|
|
249
255
|
{ label: `${dashboard.execution.applied} applied`, tone: dashboard.execution.applied > 0 ? 'green' : 'neutral' },
|
|
256
|
+
{ label: `optimize: ${dashboard.optimizeStatus.status}`, tone: toneForOptimizeStatus(dashboard.optimizeStatus.status) },
|
|
250
257
|
{ label: `${proof.summary.reviewOpen} proof review`, tone: proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral' },
|
|
251
258
|
{ label: formatMode(dashboard.governance.activeMode), tone: toneForMode(dashboard.governance.activeMode) },
|
|
252
259
|
{ label: dashboard.governance.source === 'operator-selected' ? 'operator-steered' : 'derived mode', tone: dashboard.governance.source === 'operator-selected' ? 'purple' : 'neutral' },
|
|
@@ -258,9 +265,15 @@ export default function TopologyClient({
|
|
|
258
265
|
<div className="hero-note-title">{formatMode(dashboard.governance.activeMode)}</div>
|
|
259
266
|
<div className="hero-note-copy">{dashboard.governance.profile.explanation}</div>
|
|
260
267
|
<div style={{ marginTop: 8, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
|
268
|
+
<span className={`badge badge-${dashboard.optimizeStatus.status === 'healthy' ? 'green' : dashboard.optimizeStatus.status === 'idle' ? 'gray' : 'yellow'}`}>optimize {dashboard.optimizeStatus.status}</span>
|
|
261
269
|
<span className="badge badge-gray">{proof.summary.effective} effective records</span>
|
|
262
270
|
<Link href="/proof" className="badge badge-gray" style={{ textDecoration: 'none' }}>open proof</Link>
|
|
263
271
|
</div>
|
|
272
|
+
{dashboard.optimizeStatus.status !== 'idle' ? (
|
|
273
|
+
<div style={{ marginTop: 10, fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>
|
|
274
|
+
{dashboard.optimizeStatus.summary}
|
|
275
|
+
</div>
|
|
276
|
+
) : null}
|
|
264
277
|
</div>
|
|
265
278
|
<div style={{ display: 'grid', gap: 10 }}>
|
|
266
279
|
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
|
|
@@ -284,6 +297,32 @@ export default function TopologyClient({
|
|
|
284
297
|
<MetricCard label="Snapshots" value={dashboard.execution.snapshots} sublabel="before/after state preserved for reviewed execution" tone="purple" icon="◎" />
|
|
285
298
|
</div>
|
|
286
299
|
|
|
300
|
+
<SectionFrame
|
|
301
|
+
eyebrow="Latest optimize run"
|
|
302
|
+
title="Full vs partial structural refresh is now explicit"
|
|
303
|
+
description="Queue refresh and conflict enrichment are tracked separately so a degraded optimize run can still expose a real review queue without pretending the full enrichment pass succeeded."
|
|
304
|
+
tone={toneForOptimizeStatus(dashboard.optimizeStatus.status)}
|
|
305
|
+
>
|
|
306
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
307
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
|
308
|
+
<span className={`badge badge-${dashboard.optimizeStatus.status === 'healthy' ? 'green' : dashboard.optimizeStatus.status === 'idle' ? 'gray' : 'yellow'}`}>{dashboard.optimizeStatus.status}</span>
|
|
309
|
+
<span className="badge badge-gray">queue {dashboard.optimizeStatus.queue.total} total • {dashboard.optimizeStatus.queue.open} open</span>
|
|
310
|
+
<span className="badge badge-gray">created {dashboard.optimizeStatus.queue.created} • updated {dashboard.optimizeStatus.queue.updated}</span>
|
|
311
|
+
<span className="badge badge-gray">enrichment {dashboard.optimizeStatus.enrichment.resolved} resolved • {dashboard.optimizeStatus.enrichment.failed} failed • {dashboard.optimizeStatus.enrichment.skipped} skipped</span>
|
|
312
|
+
{dashboard.optimizeStatus.ranAt ? <span className="badge badge-gray">ran {formatDate(dashboard.optimizeStatus.ranAt)}</span> : null}
|
|
313
|
+
</div>
|
|
314
|
+
<div className="signal-text">{dashboard.optimizeStatus.summary}</div>
|
|
315
|
+
{dashboard.optimizeStatus.nextStep ? <div className="signal-text">→ {dashboard.optimizeStatus.nextStep}</div> : null}
|
|
316
|
+
{dashboard.optimizeStatus.notices.length > 0 ? (
|
|
317
|
+
<div style={{ display: 'grid', gap: 4 }}>
|
|
318
|
+
{dashboard.optimizeStatus.notices.slice(0, 3).map((notice, index) => (
|
|
319
|
+
<div key={`${dashboard.optimizeStatus.status}-${index}`} className="signal-text">• {notice}</div>
|
|
320
|
+
))}
|
|
321
|
+
</div>
|
|
322
|
+
) : null}
|
|
323
|
+
</div>
|
|
324
|
+
</SectionFrame>
|
|
325
|
+
|
|
287
326
|
<SectionFrame
|
|
288
327
|
eyebrow="Execution pipeline"
|
|
289
328
|
title="Accepted review can now become prepared and applied structure"
|