11'use client' ;
22
3- import { useEffect , useMemo , useState } from 'react' ;
3+ import { useCallback , useEffect , useMemo , useState } from 'react' ;
44
55import BitcodeMetricGrid from '@/components/base/bitcode/execution/BitcodeMetricGrid' ;
66
@@ -21,6 +21,12 @@ import {
2121 type TerminalDepositedSourceRevision ,
2222 type TerminalDepositReadWorkbench as TerminalDepositReadWorkbenchState ,
2323} from './terminal-deposit-read-workbench' ;
24+ import {
25+ buildTerminalFitPipelineHarnessRequest ,
26+ streamTerminalFitPipelineHarness ,
27+ summarizeTerminalFitPipelineHarnessEvent ,
28+ type TerminalFitPipelineHarnessEvent ,
29+ } from './terminal-pipeline-harness-client' ;
2430import { useTerminalShellBridge } from './terminal-shell-bridge' ;
2531import { jumpToShellSection } from './terminal-shell-reading' ;
2632
@@ -37,20 +43,28 @@ type ReadFitProgressState = 'draft' | 'measured' | 'admitted' | 'fit-recorded';
3743interface TerminalDepositReadWorkbenchProps {
3844 repositoryContext ?: TerminalRepositoryContextState | null ;
3945 depositedSourceRevision ?: TerminalDepositedSourceRevision | null ;
46+ admittedReadActivityId ?: string | null ;
4047 onRecordActivity ?: ( draft : TerminalActivityRecordDraft ) => Promise < unknown > ;
48+ onHarnessCompleted ?: ( ) => Promise < unknown > | unknown ;
4149 showDemonstrationWorkbench ?: boolean ;
4250}
4351
4452export default function TerminalDepositReadWorkbench ( {
4553 repositoryContext = null ,
4654 depositedSourceRevision = null ,
55+ admittedReadActivityId = null ,
4756 onRecordActivity,
57+ onHarnessCompleted,
4858 showDemonstrationWorkbench = true ,
4959} : TerminalDepositReadWorkbenchProps ) {
5060 const { snapshot } = useTerminalShellBridge ( ) ;
5161 const [ recordingKey , setRecordingKey ] = useState < 'deposit' | 'read' | 'read-admission' | 'fit' | null > ( null ) ;
5262 const [ recordMessage , setRecordMessage ] = useState < string | null > ( null ) ;
5363 const [ readFitProgress , setReadFitProgress ] = useState < ReadFitProgressState > ( 'draft' ) ;
64+ const [ recordedAdmittedReadActivityId , setRecordedAdmittedReadActivityId ] = useState < string | null > ( null ) ;
65+ const [ harnessState , setHarnessState ] = useState < 'idle' | 'running' | 'completed' | 'failed' > ( 'idle' ) ;
66+ const [ harnessMessage , setHarnessMessage ] = useState < string | null > ( null ) ;
67+ const [ harnessEvents , setHarnessEvents ] = useState < TerminalFitPipelineHarnessEvent [ ] > ( [ ] ) ;
5468 const workbenchSnapshot = useMemo ( ( ) => {
5569 if ( showDemonstrationWorkbench ) return snapshot ;
5670 return buildLiveTerminalDepositReadWorkbenchSnapshot ( repositoryContext , depositedSourceRevision ) ;
@@ -63,8 +77,17 @@ export default function TerminalDepositReadWorkbench({
6377
6478 useEffect ( ( ) => {
6579 setReadFitProgress ( 'draft' ) ;
80+ setRecordedAdmittedReadActivityId ( null ) ;
81+ setHarnessState ( 'idle' ) ;
82+ setHarnessMessage ( null ) ;
83+ setHarnessEvents ( [ ] ) ;
6684 } , [ scenarioKey ] ) ;
6785
86+ useEffect ( ( ) => {
87+ if ( ! admittedReadActivityId ) return ;
88+ setReadFitProgress ( ( currentProgress ) => ( currentProgress === 'draft' ? 'admitted' : currentProgress ) ) ;
89+ } , [ admittedReadActivityId ] ) ;
90+
6891 const selectedEntryChips = useMemo ( ( ) => {
6992 if ( ! workbench ?. deposit . selectedEntries . length ) return [ ] ;
7093 return workbench . deposit . selectedEntries . slice ( 0 , 6 ) . map ( ( entry ) => entry . label ) ;
@@ -83,6 +106,34 @@ export default function TerminalDepositReadWorkbench({
83106 : readFitProgress === 'fit-recorded'
84107 ? 'Fit result recorded'
85108 : 'Record fit result posture' ;
109+ const liveFitActionLabel =
110+ harnessState === 'running'
111+ ? 'Running live fit...'
112+ : harnessState === 'completed'
113+ ? 'Run live fit again'
114+ : 'Run live AssetPack fit' ;
115+ const harnessReadActivityId = recordedAdmittedReadActivityId || admittedReadActivityId ;
116+ const harnessRequestState = useMemo (
117+ ( ) =>
118+ buildTerminalFitPipelineHarnessRequest ( {
119+ workbench,
120+ repositoryContext,
121+ depositedSourceRevision,
122+ readActivityId : harnessReadActivityId ,
123+ } ) ,
124+ [ depositedSourceRevision , harnessReadActivityId , repositoryContext , workbench ] ,
125+ ) ;
126+ const canRunLiveFit =
127+ ! showDemonstrationWorkbench &&
128+ recordingKey === null &&
129+ harnessState !== 'running' &&
130+ harnessRequestState . ready ;
131+
132+ const recordActivityId = ( value : unknown ) => {
133+ if ( ! value || typeof value !== 'object' ) return null ;
134+ const id = ( value as { id ?: unknown } ) . id ;
135+ return typeof id === 'string' && id . trim ( ) ? id : null ;
136+ } ;
86137
87138 const handleRecord = async ( kind : 'deposit' | 'read' | 'fit' ) => {
88139 if ( ! workbench || ! onRecordActivity ) return ;
@@ -141,7 +192,8 @@ export default function TerminalDepositReadWorkbench({
141192 setRecordMessage ( null ) ;
142193
143194 try {
144- await onRecordActivity ( buildTerminalReadAdmissionDraft ( workbench ) ) ;
195+ const recorded = await onRecordActivity ( buildTerminalReadAdmissionDraft ( workbench ) ) ;
196+ setRecordedAdmittedReadActivityId ( recordActivityId ( recorded ) ) ;
145197 setReadFitProgress ( 'admitted' ) ;
146198 setRecordMessage (
147199 'Measured Read admitted for fit search. Next run or record the fit result posture as worthy_fit, no_worthy_fit, or blocked_readiness evidence.' ,
@@ -153,6 +205,33 @@ export default function TerminalDepositReadWorkbench({
153205 }
154206 } ;
155207
208+ const handleRunLiveFit = useCallback ( async ( ) => {
209+ if ( ! harnessRequestState . ready ) {
210+ setHarnessState ( 'failed' ) ;
211+ setHarnessMessage ( `Live fit cannot start yet: missing ${ harnessRequestState . missing . join ( ', ' ) } .` ) ;
212+ return ;
213+ }
214+
215+ setHarnessState ( 'running' ) ;
216+ setHarnessMessage ( 'Starting live AssetPack fit harness...' ) ;
217+ setHarnessEvents ( [ ] ) ;
218+
219+ try {
220+ await streamTerminalFitPipelineHarness ( harnessRequestState . request , {
221+ onEvent : ( event ) => {
222+ setHarnessEvents ( ( currentEvents ) => [ ...currentEvents . slice ( - 7 ) , event ] ) ;
223+ setHarnessMessage ( summarizeTerminalFitPipelineHarnessEvent ( event ) ) ;
224+ } ,
225+ } ) ;
226+ setHarnessState ( 'completed' ) ;
227+ setRecordMessage ( 'Live AssetPack fit harness completed. Refreshing activity and telemetry readback.' ) ;
228+ await onHarnessCompleted ?.( ) ;
229+ } catch ( error ) {
230+ setHarnessState ( 'failed' ) ;
231+ setHarnessMessage ( error instanceof Error ? error . message : 'Live AssetPack fit harness failed.' ) ;
232+ }
233+ } , [ harnessRequestState , onHarnessCompleted ] ) ;
234+
156235 if ( ! workbench ) {
157236 return (
158237 < TerminalWorkspaceCard
@@ -286,6 +365,18 @@ export default function TerminalDepositReadWorkbench({
286365 >
287366 Review fit result posture
288367 </ button >
368+ { ! showDemonstrationWorkbench ? (
369+ < button
370+ type = "button"
371+ disabled = { ! canRunLiveFit }
372+ onClick = { ( ) => {
373+ void handleRunLiveFit ( ) ;
374+ } }
375+ className = "rounded-[1.25rem] border border-sky-300/30 bg-sky-300/10 px-4 py-3 text-sm font-medium text-sky-100 transition hover:border-sky-200/50 hover:bg-sky-300/15 disabled:cursor-not-allowed disabled:opacity-55"
376+ >
377+ { liveFitActionLabel }
378+ </ button >
379+ ) : null }
289380 < button
290381 type = "button"
291382 disabled = { recordingKey !== null || readFitProgress !== 'admitted' }
@@ -297,6 +388,21 @@ export default function TerminalDepositReadWorkbench({
297388 { fitResultActionLabel }
298389 </ button >
299390 </ div >
391+ { ! showDemonstrationWorkbench && ( harnessMessage || ! harnessRequestState . ready || harnessEvents . length > 0 ) ? (
392+ < div className = "mt-4 rounded-[1.1rem] border border-white/8 bg-black/20 px-4 py-4 text-sm leading-6 text-neutral-300" >
393+ < p className = "font-medium text-neutral-100" >
394+ { harnessMessage ||
395+ `Live fit waiting for ${ harnessRequestState . ready ? 'stream events' : harnessRequestState . missing . join ( ', ' ) } .` }
396+ </ p >
397+ { harnessEvents . length ? (
398+ < ul className = "mt-3 space-y-1 text-xs text-neutral-400" >
399+ { harnessEvents . slice ( - 4 ) . map ( ( event , index ) => (
400+ < li key = { `${ event . event } -${ index } ` } > { summarizeTerminalFitPipelineHarnessEvent ( event ) } </ li >
401+ ) ) }
402+ </ ul >
403+ ) : null }
404+ </ div >
405+ ) : null }
300406 </ section >
301407
302408 < article
0 commit comments