helixevo 0.6.0 → 0.7.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 +21 -0
- package/README.md +9 -2
- package/dashboard/app/api/proof/route.ts +71 -0
- package/dashboard/app/api/run/route.ts +1 -0
- package/dashboard/app/coevolution/client.tsx +60 -31
- package/dashboard/app/coevolution/page.tsx +3 -1
- package/dashboard/app/commands/page.tsx +32 -5
- package/dashboard/app/guide/page.tsx +34 -21
- package/dashboard/app/ontology/client.tsx +39 -17
- package/dashboard/app/ontology/page.tsx +3 -1
- package/dashboard/app/page.tsx +34 -0
- package/dashboard/app/projects/client.tsx +13 -19
- package/dashboard/app/proof/client.tsx +295 -0
- package/dashboard/app/proof/page.tsx +9 -0
- package/dashboard/app/research/client.tsx +29 -8
- package/dashboard/app/topology/client.tsx +60 -29
- package/dashboard/app/topology/page.tsx +3 -1
- package/dashboard/components/guide-deep-link.tsx +22 -0
- package/dashboard/components/next-step-empty-state.tsx +53 -0
- package/dashboard/components/operator-loop-trail.tsx +46 -0
- package/dashboard/components/sidebar-nav.tsx +1 -0
- package/dashboard/components/surface-jump-links.tsx +69 -0
- package/dashboard/lib/loop-map.ts +210 -0
- package/dashboard/lib/proof.ts +577 -0
- package/dashboard/lib/release-spotlight.ts +17 -0
- package/dist/cli.js +500 -0
- package/package.json +2 -2
|
@@ -6,7 +6,11 @@ import { ConsolePanel } from '@/components/console-panel'
|
|
|
6
6
|
import { MetricCard } from '@/components/metric-card'
|
|
7
7
|
import { PageHero } from '@/components/page-hero'
|
|
8
8
|
import { SectionFrame } from '@/components/section-frame'
|
|
9
|
+
import { OperatorLoopTrail } from '@/components/operator-loop-trail'
|
|
10
|
+
import { SurfaceJumpLinks } from '@/components/surface-jump-links'
|
|
11
|
+
import { NextStepEmptyState } from '@/components/next-step-empty-state'
|
|
9
12
|
import type { OntologyControlDashboardSummary, OntologyReviewDecisionStatus } from '@/lib/data'
|
|
13
|
+
import type { ProofDashboardSummary } from '@/lib/proof'
|
|
10
14
|
|
|
11
15
|
type RunState = 'idle' | 'running' | 'success' | 'error'
|
|
12
16
|
|
|
@@ -32,7 +36,7 @@ function formatMode(value: string) {
|
|
|
32
36
|
return value.replace(/-/g, ' ')
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
export default function OntologyClient({ initialDashboard }: { initialDashboard: OntologyControlDashboardSummary }) {
|
|
39
|
+
export default function OntologyClient({ initialDashboard, proof }: { initialDashboard: OntologyControlDashboardSummary; proof: ProofDashboardSummary }) {
|
|
36
40
|
const [dashboard, setDashboard] = useState(initialDashboard)
|
|
37
41
|
const [runState, setRunState] = useState<RunState>('idle')
|
|
38
42
|
const [output, setOutput] = useState('')
|
|
@@ -77,6 +81,7 @@ export default function OntologyClient({ initialDashboard }: { initialDashboard:
|
|
|
77
81
|
{ label: `${dashboard.summary.extensions} approved extensions`, tone: dashboard.summary.extensions > 0 ? 'blue' : 'neutral' },
|
|
78
82
|
{ label: `${dashboard.adoption.activeConcepts} active concepts`, tone: dashboard.adoption.activeConcepts > 0 ? 'green' : 'neutral' },
|
|
79
83
|
{ label: `${dashboard.adoption.routesInfluenced} semantically influenced routes`, tone: dashboard.adoption.routesInfluenced > 0 ? 'purple' : 'neutral' },
|
|
84
|
+
{ label: `${proof.summary.reviewOpen} proof review`, tone: proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral' },
|
|
80
85
|
{ label: formatMode(dashboard.governance.activeMode), tone: dashboard.governance.activeMode === 'transfer-focused' ? 'purple' : dashboard.governance.activeMode === 'project-critical' ? 'yellow' : 'blue' },
|
|
81
86
|
]}
|
|
82
87
|
actions={
|
|
@@ -85,22 +90,30 @@ export default function OntologyClient({ initialDashboard }: { initialDashboard:
|
|
|
85
90
|
<div className="hero-note-label">Native ontology state</div>
|
|
86
91
|
<div className="hero-note-title">Kernel + review + semantic adoption</div>
|
|
87
92
|
<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>
|
|
93
|
+
<div style={{ marginTop: 8, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
|
94
|
+
<Link href="/proof" className="badge badge-gray" style={{ textDecoration: 'none' }}>open proof</Link>
|
|
95
|
+
<span className="badge badge-gray">{proof.summary.effective} effective records live</span>
|
|
96
|
+
</div>
|
|
88
97
|
</div>
|
|
89
|
-
<div style={{ display: '
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
<div style={{ display: 'grid', gap: 10 }}>
|
|
99
|
+
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
|
|
100
|
+
<button onClick={() => runAction('refresh', {}, 'ontology refresh')} disabled={runState === 'running'} className="badge badge-blue" style={{ border: 'none', cursor: 'pointer' }}>Refresh ontology frontier</button>
|
|
101
|
+
</div>
|
|
102
|
+
<SurfaceJumpLinks surface="ontology" variant="compact" title="Semantic handoffs" />
|
|
93
103
|
</div>
|
|
94
104
|
</div>
|
|
95
105
|
}
|
|
96
106
|
/>
|
|
97
107
|
|
|
108
|
+
<OperatorLoopTrail surface="ontology" />
|
|
109
|
+
|
|
98
110
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
|
|
99
111
|
<MetricCard label="Kernel concepts" value={dashboard.summary.kernelConcepts} sublabel={`${dashboard.kernel.relationFamilies.length} relation families`} tone="purple" icon="◎" />
|
|
100
112
|
<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
113
|
<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
114
|
<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
115
|
<MetricCard label="Deprecation-sensitive" value={dashboard.adoption.conceptsAtDeprecationRisk} sublabel="approved concepts with live consumers" tone={dashboard.adoption.conceptsAtDeprecationRisk > 0 ? 'yellow' : 'neutral'} icon="!" />
|
|
116
|
+
<MetricCard label="Semantic proof review" value={proof.summary.reviewOpen} sublabel={`${proof.summary.effective} effective • ${proof.summary.mixed} mixed`} tone={proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral'} icon="◇" />
|
|
104
117
|
<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
118
|
</div>
|
|
106
119
|
|
|
@@ -198,10 +211,13 @@ export default function OntologyClient({ initialDashboard }: { initialDashboard:
|
|
|
198
211
|
</div>
|
|
199
212
|
</div>
|
|
200
213
|
)) : (
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
214
|
+
<NextStepEmptyState
|
|
215
|
+
title="No ontology frontier concepts yet"
|
|
216
|
+
description="Run a frontier refresh after pressure motifs, project gaps, or topology review patterns accumulate. Frontier creation is the first step before any concept can become active semantic control."
|
|
217
|
+
command="helixevo ontology --refresh"
|
|
218
|
+
pageLink={{ label: 'Open Co-Evolution', href: '/coevolution', tone: 'purple' }}
|
|
219
|
+
guideLink={{ label: 'Guide · Semantic Control', anchor: 'semanticcontrol', tone: 'green' }}
|
|
220
|
+
/>
|
|
205
221
|
)}
|
|
206
222
|
</div>
|
|
207
223
|
</SectionFrame>
|
|
@@ -240,10 +256,13 @@ export default function OntologyClient({ initialDashboard }: { initialDashboard:
|
|
|
240
256
|
{concept.warning ? <div className="signal-text" style={{ marginTop: 10 }}>• {concept.warning}</div> : null}
|
|
241
257
|
</div>
|
|
242
258
|
)) : (
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
259
|
+
<NextStepEmptyState
|
|
260
|
+
title="No approved extensions yet"
|
|
261
|
+
description="Approved extensions appear after frontier concepts pass semantic review. Promotion is what turns reviewed ontology into live semantic adoption."
|
|
262
|
+
command="helixevo ontology --review <conceptId> --decision promote"
|
|
263
|
+
pageLink={{ label: 'Open Co-Evolution', href: '/coevolution', tone: 'purple' }}
|
|
264
|
+
guideLink={{ label: 'Guide · Semantic Control', anchor: 'semanticcontrol', tone: 'green' }}
|
|
265
|
+
/>
|
|
247
266
|
)}
|
|
248
267
|
</div>
|
|
249
268
|
</SectionFrame>
|
|
@@ -267,10 +286,13 @@ export default function OntologyClient({ initialDashboard }: { initialDashboard:
|
|
|
267
286
|
</div>
|
|
268
287
|
</div>
|
|
269
288
|
)) : (
|
|
270
|
-
<
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
289
|
+
<NextStepEmptyState
|
|
290
|
+
title="No ontology changes logged yet"
|
|
291
|
+
description="Refresh the frontier or review a concept to create the first native ontology events and start a visible semantic history instead of hidden drift."
|
|
292
|
+
command="helixevo ontology --refresh"
|
|
293
|
+
pageLink={{ label: 'Open Topology', href: '/topology', tone: 'yellow' }}
|
|
294
|
+
guideLink={{ label: 'Guide · Semantic Control', anchor: 'semanticcontrol', tone: 'green' }}
|
|
295
|
+
/>
|
|
274
296
|
)}
|
|
275
297
|
</div>
|
|
276
298
|
</SectionFrame>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import OntologyClient from './client'
|
|
2
2
|
import { loadOntologyControlSummary } from '@/lib/data'
|
|
3
|
+
import { loadProofDashboardSummary } from '@/lib/proof'
|
|
3
4
|
|
|
4
5
|
export const dynamic = 'force-dynamic'
|
|
5
6
|
|
|
6
7
|
export default function OntologyPage() {
|
|
7
8
|
const dashboard = loadOntologyControlSummary()
|
|
8
|
-
|
|
9
|
+
const proof = loadProofDashboardSummary()
|
|
10
|
+
return <OntologyClient initialDashboard={dashboard} proof={proof} />
|
|
9
11
|
}
|
package/dashboard/app/page.tsx
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import Link from 'next/link'
|
|
2
2
|
import { getDashboardSummary, getOntologyDashboardSummary, loadCoEvolutionSummary, loadFailures, loadFrontier, loadGraph, loadHistory, listProjects } from '@/lib/data'
|
|
3
|
+
import { loadProofDashboardSummary } from '@/lib/proof'
|
|
3
4
|
import { OverviewActions } from '@/components/overview-actions'
|
|
4
5
|
import { PageHero } from '@/components/page-hero'
|
|
5
6
|
import { MetricCard } from '@/components/metric-card'
|
|
6
7
|
import { SectionFrame } from '@/components/section-frame'
|
|
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'
|
|
7
11
|
|
|
8
12
|
export const dynamic = 'force-dynamic'
|
|
9
13
|
|
|
@@ -17,6 +21,7 @@ export default function Overview() {
|
|
|
17
21
|
const summary = getDashboardSummary()
|
|
18
22
|
const ontology = getOntologyDashboardSummary()
|
|
19
23
|
const coevolution = loadCoEvolutionSummary()
|
|
24
|
+
const proof = loadProofDashboardSummary()
|
|
20
25
|
const frontier = loadFrontier()
|
|
21
26
|
const history = loadHistory()
|
|
22
27
|
const graph = loadGraph()
|
|
@@ -39,6 +44,7 @@ export default function Overview() {
|
|
|
39
44
|
{ label: `${coevolution.pressureMotifs.promotionReady} promotion-ready motifs`, tone: coevolution.pressureMotifs.promotionReady > 0 ? 'purple' : 'neutral' },
|
|
40
45
|
{ label: `${coevolution.topologyReviews.open} topology reviews`, tone: coevolution.topologyReviews.open > 0 ? 'yellow' : 'green' },
|
|
41
46
|
{ label: `${coevolution.topologyExecution.prepared} prepared structural plans`, tone: coevolution.topologyExecution.prepared > 0 ? 'blue' : 'neutral' },
|
|
47
|
+
{ label: `${proof.summary.reviewOpen} proof reviews`, tone: proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral' },
|
|
42
48
|
{ label: `mode: ${coevolution.governance.activeMode.replace(/-/g, ' ')}`, tone: coevolution.governance.activeMode === 'transfer-focused' ? 'purple' : coevolution.governance.activeMode === 'project-critical' ? 'yellow' : 'blue' },
|
|
43
49
|
]}
|
|
44
50
|
actions={
|
|
@@ -57,14 +63,40 @@ export default function Overview() {
|
|
|
57
63
|
}
|
|
58
64
|
/>
|
|
59
65
|
|
|
66
|
+
<OperatorLoopTrail surface="overview" />
|
|
67
|
+
|
|
60
68
|
<div className="grid-5">
|
|
61
69
|
<MetricCard label="Total skills" value={summary.skills.total} sublabel={`${summary.skills.evolved} evolved • ${summary.skillTests} skill tests`} tone="purple" href="/network" icon="◆" />
|
|
62
70
|
<MetricCard label="Accepted proposals" value={summary.evolution.accepted} sublabel={`${summary.evolution.rejected} rejected`} tone="green" href="/evolution" icon="✓" />
|
|
63
71
|
<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="!" />
|
|
64
72
|
<MetricCard label="Discoveries" value={summary.buffer.discoveries} sublabel={`${summary.buffer.drafts} drafts in progress`} tone="blue" href="/research" icon="◎" />
|
|
65
73
|
<MetricCard label="Frontier candidates" value={frontier.programs.length} sublabel={`${summary.canaries} active canaries`} tone="neutral" href="/frontier" icon="▲" />
|
|
74
|
+
<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="◇" />
|
|
66
75
|
</div>
|
|
67
76
|
|
|
77
|
+
<SectionFrame
|
|
78
|
+
eyebrow={`Release spotlight · v${CURRENT_RELEASE_SPOTLIGHT.version}`}
|
|
79
|
+
title={CURRENT_RELEASE_SPOTLIGHT.title}
|
|
80
|
+
description={CURRENT_RELEASE_SPOTLIGHT.summary}
|
|
81
|
+
tone="blue"
|
|
82
|
+
>
|
|
83
|
+
<div className="grid-2" style={{ gap: 16 }}>
|
|
84
|
+
<div style={{ display: 'grid', gap: 10 }}>
|
|
85
|
+
{CURRENT_RELEASE_SPOTLIGHT.bullets.map((item) => (
|
|
86
|
+
<div key={item} className="signal-text">• {item}</div>
|
|
87
|
+
))}
|
|
88
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 4 }}>
|
|
89
|
+
{CURRENT_RELEASE_SPOTLIGHT.nextActions.map((action) => (
|
|
90
|
+
<Link key={action.href} href={action.href} className={`hero-chip hero-chip-${action.tone}`} style={{ textDecoration: 'none' }}>
|
|
91
|
+
{action.label}
|
|
92
|
+
</Link>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<SurfaceJumpLinks surface="overview" variant="panel" title="Jump into the updated loop" />
|
|
97
|
+
</div>
|
|
98
|
+
</SectionFrame>
|
|
99
|
+
|
|
68
100
|
<SectionFrame
|
|
69
101
|
eyebrow="Execution layer"
|
|
70
102
|
title="Quick actions"
|
|
@@ -187,8 +219,10 @@ export default function Overview() {
|
|
|
187
219
|
<span className="badge badge-gray">deprecation risk → {ontology.ontologyLoop.adoption.conceptsAtDeprecationRisk} concepts • {ontology.ontologyLoop.adoption.unusedExtensions} unused extensions</span>
|
|
188
220
|
<span className="badge badge-gray">topology → {ontology.topologyReviews.open} open • {ontology.topologyReviews.accepted} accepted • {ontology.topologyReviews.generatedFromManualReview} manual-route</span>
|
|
189
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>
|
|
190
223
|
<Link href="/ontology" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open ontology control</Link>
|
|
191
224
|
<Link href="/topology" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open topology control</Link>
|
|
225
|
+
<Link href="/proof" className="badge badge-blue" style={{ textDecoration: 'none' }}>Open proof control</Link>
|
|
192
226
|
<span className="badge badge-gray">governance: {ontology.governance.activeMode.replace(/-/g, ' ')} ({ontology.governance.source})</span>
|
|
193
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>
|
|
194
228
|
<span className="badge badge-gray">{ontology.enrichedSkillNodes} skills carry explicit brain metadata</span>
|
|
@@ -6,6 +6,9 @@ import { ConsolePanel } from '@/components/console-panel'
|
|
|
6
6
|
import { MetricCard } from '@/components/metric-card'
|
|
7
7
|
import { PageHero } from '@/components/page-hero'
|
|
8
8
|
import { SectionFrame } from '@/components/section-frame'
|
|
9
|
+
import { OperatorLoopTrail } from '@/components/operator-loop-trail'
|
|
10
|
+
import { SurfaceJumpLinks } from '@/components/surface-jump-links'
|
|
11
|
+
import { NextStepEmptyState } from '@/components/next-step-empty-state'
|
|
9
12
|
import type { ProjectProfile } from '@/lib/data'
|
|
10
13
|
|
|
11
14
|
type SetupState = 'idle' | 'analyzing' | 'done' | 'error' | 'cancelled'
|
|
@@ -364,18 +367,13 @@ export default function ProjectsClient({ profiles, skillCount, projectAnalysisTr
|
|
|
364
367
|
<div className="hero-note-title">Analyze → Match → Gap scan → Action</div>
|
|
365
368
|
<div className="hero-note-copy">Run a profile, review the skill fit, then send uncovered needs into Research or the Skill Network.</div>
|
|
366
369
|
</div>
|
|
367
|
-
<
|
|
368
|
-
<Link href="/network" style={buttonStyle('purple')}>
|
|
369
|
-
Open Skill Network
|
|
370
|
-
</Link>
|
|
371
|
-
<Link href="/research" style={buttonStyle('blue')}>
|
|
372
|
-
Open Research
|
|
373
|
-
</Link>
|
|
374
|
-
</div>
|
|
370
|
+
<SurfaceJumpLinks surface="projects" variant="compact" title="Observation handoffs" />
|
|
375
371
|
</div>
|
|
376
372
|
}
|
|
377
373
|
/>
|
|
378
374
|
|
|
375
|
+
<OperatorLoopTrail surface="projects" />
|
|
376
|
+
|
|
379
377
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16, marginBottom: 24 }}>
|
|
380
378
|
<MetricCard
|
|
381
379
|
label="Portfolio"
|
|
@@ -764,17 +762,13 @@ export default function ProjectsClient({ profiles, skillCount, projectAnalysisTr
|
|
|
764
762
|
tone="yellow"
|
|
765
763
|
>
|
|
766
764
|
{profiles.length === 0 ? (
|
|
767
|
-
<
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
<div className="empty-state-desc">
|
|
775
|
-
Start with a local folder or GitHub repository. The resulting profile will show matched skills, uncovered gaps, and recommended next steps.
|
|
776
|
-
</div>
|
|
777
|
-
</div>
|
|
765
|
+
<NextStepEmptyState
|
|
766
|
+
title="No analyzed projects yet"
|
|
767
|
+
description={`Start with a local folder or GitHub repository. The resulting profile will show matched skills, uncovered gaps, activation traces, and the next routed action against your ${skillCount} current skills.`}
|
|
768
|
+
command="helixevo project-setup <path>"
|
|
769
|
+
pageLink={{ label: 'Open Research', href: '/research', tone: 'blue' }}
|
|
770
|
+
guideLink={{ label: 'Guide · Quick Start', anchor: 'quickstart', tone: 'purple' }}
|
|
771
|
+
/>
|
|
778
772
|
) : (
|
|
779
773
|
<div style={{ display: 'grid', gap: 18 }}>
|
|
780
774
|
{profiles.map(profile => {
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { ConsolePanel } from '@/components/console-panel'
|
|
5
|
+
import { MetricCard } from '@/components/metric-card'
|
|
6
|
+
import { NextStepEmptyState } from '@/components/next-step-empty-state'
|
|
7
|
+
import { OperatorLoopTrail } from '@/components/operator-loop-trail'
|
|
8
|
+
import { PageHero } from '@/components/page-hero'
|
|
9
|
+
import { SectionFrame } from '@/components/section-frame'
|
|
10
|
+
import { SurfaceJumpLinks } from '@/components/surface-jump-links'
|
|
11
|
+
import type { ProofDashboardSummary, ProofOutcomeState, ProofReviewDecisionStatus, ProofTargetType } from '@/lib/proof'
|
|
12
|
+
|
|
13
|
+
type RunState = 'idle' | 'running' | 'success' | 'error'
|
|
14
|
+
|
|
15
|
+
function toneForOutcome(outcome: ProofOutcomeState): 'green' | 'yellow' | 'purple' | 'neutral' {
|
|
16
|
+
if (outcome === 'effective') return 'green'
|
|
17
|
+
if (outcome === 'regressed') return 'yellow'
|
|
18
|
+
if (outcome === 'mixed') return 'purple'
|
|
19
|
+
return 'neutral'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toneForTarget(targetType: ProofTargetType): 'blue' | 'green' | 'purple' | 'yellow' | 'neutral' {
|
|
23
|
+
if (targetType === 'pressure-intervention') return 'blue'
|
|
24
|
+
if (targetType === 'transfer-event') return 'green'
|
|
25
|
+
if (targetType === 'topology-execution') return 'yellow'
|
|
26
|
+
if (targetType === 'ontology-extension') return 'purple'
|
|
27
|
+
return 'neutral'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function labelForTarget(targetType: ProofTargetType) {
|
|
31
|
+
if (targetType === 'pressure-intervention') return 'Intervention'
|
|
32
|
+
if (targetType === 'transfer-event') return 'Transfer'
|
|
33
|
+
if (targetType === 'topology-execution') return 'Topology'
|
|
34
|
+
if (targetType === 'ontology-extension') return 'Ontology'
|
|
35
|
+
return 'Evolution'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function labelForOutcome(outcome: ProofOutcomeState) {
|
|
39
|
+
return outcome.replace(/-/g, ' ')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function formatDate(value: string | undefined) {
|
|
43
|
+
if (!value) return '—'
|
|
44
|
+
return new Date(value).toLocaleString()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function consoleTone(state: RunState): 'neutral' | 'green' | 'red' | 'yellow' {
|
|
48
|
+
if (state === 'success') return 'green'
|
|
49
|
+
if (state === 'error') return 'red'
|
|
50
|
+
if (state === 'running') return 'yellow'
|
|
51
|
+
return 'neutral'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default function ProofClient({ initialDashboard }: { initialDashboard: ProofDashboardSummary }) {
|
|
55
|
+
const [dashboard, setDashboard] = useState(initialDashboard)
|
|
56
|
+
const [runState, setRunState] = useState<RunState>('idle')
|
|
57
|
+
const [output, setOutput] = useState('')
|
|
58
|
+
const [pendingKey, setPendingKey] = useState<string | null>(null)
|
|
59
|
+
|
|
60
|
+
const runReview = async (recordId: string, decision: ProofReviewDecisionStatus, label: string) => {
|
|
61
|
+
setPendingKey(recordId)
|
|
62
|
+
setRunState('running')
|
|
63
|
+
setOutput('')
|
|
64
|
+
try {
|
|
65
|
+
const res = await fetch('/api/proof', {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
action: 'review',
|
|
70
|
+
recordId,
|
|
71
|
+
decision,
|
|
72
|
+
rationale: `${decision} proof record from dashboard proof control.`,
|
|
73
|
+
}),
|
|
74
|
+
})
|
|
75
|
+
const data = await res.json()
|
|
76
|
+
if (!res.ok || !data.success) {
|
|
77
|
+
setRunState('error')
|
|
78
|
+
setOutput(data.error ?? `${label} failed`)
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
setDashboard(data.dashboard)
|
|
82
|
+
setRunState('success')
|
|
83
|
+
setOutput(data.output || `${label} completed`)
|
|
84
|
+
} catch (err: unknown) {
|
|
85
|
+
setRunState('error')
|
|
86
|
+
setOutput(err instanceof Error ? err.message : `${label} failed`)
|
|
87
|
+
} finally {
|
|
88
|
+
setPendingKey(null)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div style={{ display: 'grid', gap: 22 }}>
|
|
94
|
+
<PageHero
|
|
95
|
+
eyebrow="Outcome attribution"
|
|
96
|
+
title="Proof Control"
|
|
97
|
+
description="Review whether interventions, transfers, topology changes, semantic adoption, and evolution effects actually appear to be working. Proof stays bounded and operator-governed rather than pretending to know more than the evidence supports."
|
|
98
|
+
chips={[
|
|
99
|
+
{ label: `${dashboard.summary.total} proof records`, tone: 'blue' },
|
|
100
|
+
{ label: `${dashboard.summary.effective} effective`, tone: dashboard.summary.effective > 0 ? 'green' : 'neutral' },
|
|
101
|
+
{ label: `${dashboard.summary.mixed} mixed`, tone: dashboard.summary.mixed > 0 ? 'purple' : 'neutral' },
|
|
102
|
+
{ label: `${dashboard.summary.regressed} regressed`, tone: dashboard.summary.regressed > 0 ? 'yellow' : 'neutral' },
|
|
103
|
+
{ label: `${dashboard.summary.reviewOpen} open review`, tone: dashboard.summary.reviewOpen > 0 ? 'yellow' : 'green' },
|
|
104
|
+
{ label: `${dashboard.summary.verified} verified`, tone: dashboard.summary.verified > 0 ? 'green' : 'neutral' },
|
|
105
|
+
]}
|
|
106
|
+
actions={
|
|
107
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
108
|
+
<div className="hero-note-card">
|
|
109
|
+
<div className="hero-note-label">Bounded proof layer</div>
|
|
110
|
+
<div className="hero-note-title">Derived first, reviewed explicitly</div>
|
|
111
|
+
<div className="hero-note-copy">The proof layer unifies evolution impact, semantic adoption, transfer, interventions, and topology execution without claiming direct causality when the evidence is only partial.</div>
|
|
112
|
+
</div>
|
|
113
|
+
<SurfaceJumpLinks surface="proof" variant="compact" title="Proof handoffs" />
|
|
114
|
+
</div>
|
|
115
|
+
}
|
|
116
|
+
/>
|
|
117
|
+
|
|
118
|
+
<OperatorLoopTrail surface="proof" />
|
|
119
|
+
|
|
120
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
|
|
121
|
+
<MetricCard label="Effective" value={dashboard.summary.effective} sublabel={`${dashboard.summary.verified} verified • ${dashboard.summary.reviewOpen} waiting for review`} tone={dashboard.summary.effective > 0 ? 'green' : 'neutral'} icon="✓" />
|
|
122
|
+
<MetricCard label="Mixed" value={dashboard.summary.mixed} sublabel="partial or conflicting downstream evidence" tone={dashboard.summary.mixed > 0 ? 'purple' : 'neutral'} icon="⇄" />
|
|
123
|
+
<MetricCard label="Regressed" value={dashboard.summary.regressed} sublabel={`${dashboard.summary.contested} contested • explicit failed or rolled-back cases`} tone={dashboard.summary.regressed > 0 ? 'yellow' : 'neutral'} icon="!" />
|
|
124
|
+
<MetricCard label="Measuring" value={dashboard.summary.measuring} sublabel={`${dashboard.summary.insufficientEvidence} insufficient-evidence`} tone={dashboard.summary.measuring > 0 ? 'blue' : 'neutral'} icon="◎" />
|
|
125
|
+
<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
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<SectionFrame
|
|
129
|
+
eyebrow="Coverage"
|
|
130
|
+
title="Proof now spans the live brain loop"
|
|
131
|
+
description="Milestone 10 stops prove from being only metrics. It unifies evidence review across response, transfer, topology, semantic adoption, and existing evolution measurement."
|
|
132
|
+
>
|
|
133
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12 }}>
|
|
134
|
+
{dashboard.byTargetType.length > 0 ? dashboard.byTargetType.map((entry) => (
|
|
135
|
+
<div key={entry.targetType} className="guide-dimension-card" style={{ borderLeftColor: `var(--${toneForTarget(entry.targetType) === 'neutral' ? 'text-muted' : toneForTarget(entry.targetType)})` }}>
|
|
136
|
+
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)' }}>{labelForTarget(entry.targetType)}</div>
|
|
137
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.7 }}>
|
|
138
|
+
{entry.total} total • {entry.effective} effective • {entry.mixed} mixed • {entry.regressed} regressed • {entry.measuring} measuring
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
)) : (
|
|
142
|
+
<NextStepEmptyState
|
|
143
|
+
title="No proof targets yet"
|
|
144
|
+
description="Create live interventions, semantic adoption, or topology execution first. Proof becomes useful when HelixEvo has real actions and downstream evidence to evaluate."
|
|
145
|
+
command="helixevo proof --status"
|
|
146
|
+
pageLink={{ label: 'Open Co-Evolution', href: '/coevolution', tone: 'purple' }}
|
|
147
|
+
guideLink={{ label: 'Guide · Closed-Loop Metrics', anchor: 'metrics', tone: 'blue' }}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
</SectionFrame>
|
|
152
|
+
|
|
153
|
+
<SectionFrame
|
|
154
|
+
eyebrow="Review queue"
|
|
155
|
+
title="Open proof review"
|
|
156
|
+
description="Derived proof stays operator-governed. Verify, defer, or contest proof records explicitly instead of treating heuristics as unquestionable truth."
|
|
157
|
+
>
|
|
158
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
159
|
+
{dashboard.reviewQueue.length > 0 ? dashboard.reviewQueue.map((record) => (
|
|
160
|
+
<div key={record.id} style={{ padding: '16px 18px', borderRadius: 18, border: '1px solid var(--border)', background: 'rgba(255,255,255,0.74)' }}>
|
|
161
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
|
162
|
+
<div style={{ display: 'grid', gap: 4, flex: 1 }}>
|
|
163
|
+
<div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)' }}>{record.title}</div>
|
|
164
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>{record.summary}</div>
|
|
165
|
+
</div>
|
|
166
|
+
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
|
167
|
+
<span className={`badge badge-${toneForTarget(record.targetType)}`}>{labelForTarget(record.targetType)}</span>
|
|
168
|
+
<span className={`badge badge-${toneForOutcome(record.outcomeState)}`}>{labelForOutcome(record.outcomeState)}</span>
|
|
169
|
+
<span className="badge badge-gray">{(record.confidence * 100).toFixed(0)}% confidence</span>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 12 }}>
|
|
174
|
+
<span className="badge badge-gray">created {formatDate(record.createdAt)}</span>
|
|
175
|
+
<span className="badge badge-gray">activity {formatDate(record.lastActivityAt)}</span>
|
|
176
|
+
{(record.relatedProjectIds ?? []).slice(0, 3).map((projectId) => <span key={`${record.id}-${projectId}`} className="badge badge-gray">{projectId}</span>)}
|
|
177
|
+
{(record.semanticConceptIds ?? []).slice(0, 2).map((conceptId) => <span key={`${record.id}-${conceptId}`} className="badge badge-green">{conceptId}</span>)}
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div style={{ display: 'grid', gap: 6, marginTop: 12 }}>
|
|
181
|
+
{record.reasons.slice(0, 3).map((reason, idx) => (
|
|
182
|
+
<div key={`${record.id}-${idx}`} className="signal-text">• {reason}</div>
|
|
183
|
+
))}
|
|
184
|
+
{record.recommendedAction ? <div className="signal-text">→ {record.recommendedAction}</div> : null}
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 14 }}>
|
|
188
|
+
<button onClick={() => runReview(record.id, 'verify', `verify ${record.title}`)} disabled={pendingKey === record.id} className="badge badge-green" style={{ border: 'none', cursor: 'pointer' }}>Verify</button>
|
|
189
|
+
<button onClick={() => runReview(record.id, 'defer', `defer ${record.title}`)} disabled={pendingKey === record.id} className="badge badge-purple" style={{ border: 'none', cursor: 'pointer' }}>Defer</button>
|
|
190
|
+
<button onClick={() => runReview(record.id, 'contest', `contest ${record.title}`)} disabled={pendingKey === record.id} className="badge badge-yellow" style={{ border: 'none', cursor: 'pointer' }}>Contest</button>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
)) : (
|
|
194
|
+
<NextStepEmptyState
|
|
195
|
+
title="No open proof reviews"
|
|
196
|
+
description="Either the proof layer has no records yet or every current record has already been verified, deferred, or contested."
|
|
197
|
+
command="helixevo proof --status --verbose"
|
|
198
|
+
pageLink={{ label: 'Open Ontology', href: '/ontology', tone: 'green' }}
|
|
199
|
+
guideLink={{ label: 'Guide · Closed-Loop Metrics', anchor: 'metrics', tone: 'blue' }}
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
</SectionFrame>
|
|
204
|
+
|
|
205
|
+
<div className="grid-2">
|
|
206
|
+
<SectionFrame
|
|
207
|
+
eyebrow="Verified"
|
|
208
|
+
title="Recent verified proof"
|
|
209
|
+
description="Operator-verified records become the most trustworthy proof layer inside the current bounded system."
|
|
210
|
+
>
|
|
211
|
+
<div style={{ display: 'grid', gap: 10 }}>
|
|
212
|
+
{dashboard.recentVerified.length > 0 ? dashboard.recentVerified.map((record) => (
|
|
213
|
+
<div key={record.id} style={{ padding: '14px 16px', borderRadius: 18, border: '1px solid var(--border)', background: 'rgba(255,255,255,0.72)' }}>
|
|
214
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
|
|
215
|
+
<div style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--text)' }}>{record.title}</div>
|
|
216
|
+
<span className="badge badge-green">verified</span>
|
|
217
|
+
</div>
|
|
218
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 6, lineHeight: 1.6 }}>{record.summary}</div>
|
|
219
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
|
|
220
|
+
<span className={`badge badge-${toneForOutcome(record.outcomeState)}`}>{labelForOutcome(record.outcomeState)}</span>
|
|
221
|
+
<span className="badge badge-gray">{labelForTarget(record.targetType)}</span>
|
|
222
|
+
<span className="badge badge-gray">{formatDate(record.latestReview?.decidedAt)}</span>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
)) : <div className="signal-text">No verified proof records yet.</div>}
|
|
226
|
+
</div>
|
|
227
|
+
</SectionFrame>
|
|
228
|
+
|
|
229
|
+
<SectionFrame
|
|
230
|
+
eyebrow="Contested"
|
|
231
|
+
title="Recent contested proof"
|
|
232
|
+
description="Contest records when the derived attribution is too strong, too weak, or simply not aligned with operator judgment."
|
|
233
|
+
>
|
|
234
|
+
<div style={{ display: 'grid', gap: 10 }}>
|
|
235
|
+
{dashboard.recentContested.length > 0 ? dashboard.recentContested.map((record) => (
|
|
236
|
+
<div key={record.id} style={{ padding: '14px 16px', borderRadius: 18, border: '1px solid var(--border)', background: 'rgba(255,255,255,0.72)' }}>
|
|
237
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
|
|
238
|
+
<div style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--text)' }}>{record.title}</div>
|
|
239
|
+
<span className="badge badge-yellow">contested</span>
|
|
240
|
+
</div>
|
|
241
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 6, lineHeight: 1.6 }}>{record.summary}</div>
|
|
242
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
|
|
243
|
+
<span className={`badge badge-${toneForOutcome(record.outcomeState)}`}>{labelForOutcome(record.outcomeState)}</span>
|
|
244
|
+
<span className="badge badge-gray">{labelForTarget(record.targetType)}</span>
|
|
245
|
+
<span className="badge badge-gray">{formatDate(record.latestReview?.decidedAt)}</span>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
)) : <div className="signal-text">No contested proof records yet.</div>}
|
|
249
|
+
</div>
|
|
250
|
+
</SectionFrame>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<SectionFrame
|
|
254
|
+
eyebrow="Ledger"
|
|
255
|
+
title="All proof records"
|
|
256
|
+
description="A unified view across intervention, transfer, structural, semantic, and evolution evidence."
|
|
257
|
+
>
|
|
258
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
259
|
+
{dashboard.records.length > 0 ? dashboard.records.map((record) => (
|
|
260
|
+
<div key={record.id} style={{ padding: '14px 16px', borderRadius: 18, border: '1px solid var(--border)', background: 'rgba(255,255,255,0.72)' }}>
|
|
261
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
|
262
|
+
<div style={{ display: 'grid', gap: 4, flex: 1 }}>
|
|
263
|
+
<div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)' }}>{record.title}</div>
|
|
264
|
+
<div style={{ fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>{record.summary}</div>
|
|
265
|
+
</div>
|
|
266
|
+
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
|
267
|
+
<span className={`badge badge-${toneForTarget(record.targetType)}`}>{labelForTarget(record.targetType)}</span>
|
|
268
|
+
<span className={`badge badge-${toneForOutcome(record.outcomeState)}`}>{labelForOutcome(record.outcomeState)}</span>
|
|
269
|
+
<span className="badge badge-gray">{record.reviewStatus}</span>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
{record.latestReview?.rationale ? <div className="signal-text" style={{ marginTop: 10 }}>review rationale · {record.latestReview.rationale}</div> : null}
|
|
273
|
+
</div>
|
|
274
|
+
)) : null}
|
|
275
|
+
</div>
|
|
276
|
+
</SectionFrame>
|
|
277
|
+
|
|
278
|
+
<ConsolePanel
|
|
279
|
+
title="Proof 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
|
+
? 'Recording proof review…\n\n' + (output || '')
|
|
286
|
+
: runState === 'success'
|
|
287
|
+
? (output || 'Proof review completed.')
|
|
288
|
+
: runState === 'error'
|
|
289
|
+
? (output || 'Proof review failed.')
|
|
290
|
+
: 'Use this surface to review derived proof records once HelixEvo has enough downstream evidence.'}
|
|
291
|
+
</pre>
|
|
292
|
+
</ConsolePanel>
|
|
293
|
+
</div>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import ProofClient from './client'
|
|
2
|
+
import { loadProofDashboardSummary } from '@/lib/proof'
|
|
3
|
+
|
|
4
|
+
export const dynamic = 'force-dynamic'
|
|
5
|
+
|
|
6
|
+
export default function ProofPage() {
|
|
7
|
+
const dashboard = loadProofDashboardSummary()
|
|
8
|
+
return <ProofClient initialDashboard={dashboard} />
|
|
9
|
+
}
|