@@ -72,7 +72,9 @@ const KanbanCardSchema = z.object({
7272 phase : z . string ( ) . optional ( ) ,
7373 operator_status : z . string ( ) . optional ( ) ,
7474 operator_summary : z . string ( ) . nullable ( ) . optional ( ) ,
75+ workspace_target : z . string ( ) . nullable ( ) . optional ( ) ,
7576 workspace_path : z . string ( ) . nullable ( ) . optional ( ) ,
77+ repository_path : z . string ( ) . nullable ( ) . optional ( ) ,
7678 session_id : z . string ( ) . nullable ( ) . optional ( ) ,
7779 last_event : z . string ( ) . nullable ( ) . optional ( ) ,
7880 last_message : z . string ( ) . nullable ( ) . optional ( ) ,
@@ -259,6 +261,13 @@ type Card = {
259261 stagePosition ?: StagePosition ;
260262 scorePath ?: string ;
261263 scoreSummary ?: string ;
264+ workspacePath ?: string ;
265+ workspaceTarget ?: string ;
266+ repositoryPath ?: string ;
267+ verdictPath ?: string ;
268+ judgeVerdict ?: unknown ;
269+ refinerAttempt ?: number | null ;
270+ refinerMaxAttempts ?: number | null ;
262271 phaseHistory : PhaseHistoryEntry [ ] ;
263272 conversation : AgentConversationMessage [ ] ;
264273 intensity : number ;
@@ -269,6 +278,7 @@ type ManualMovementPayload = {
269278 title : string ;
270279 description ?: string ;
271280 identifier ?: string ;
281+ workspace_path ?: string ;
272282} ;
273283
274284const columns = [ 'Ready' , 'In Progress' , 'Human Review' , 'Retry' , 'Blocked' , 'Done' ] ;
@@ -846,6 +856,13 @@ function makeCardsFromKanban(kanban?: KanbanState): Card[] {
846856 stagePosition : profileStagePosition ,
847857 scorePath : stringField ( rawRecord , 'score_path' ) ,
848858 scoreSummary : stringField ( rawRecord , 'score_summary' ) ,
859+ workspacePath : stringField ( rawRecord , 'workspace_path' ) ,
860+ workspaceTarget : stringField ( rawRecord , 'workspace_target' ) ,
861+ repositoryPath : stringField ( rawRecord , 'repository_path' ) ,
862+ verdictPath : stringField ( rawRecord , 'verdict_path' ) ,
863+ judgeVerdict : rawRecord . judge_verdict ,
864+ refinerAttempt : typeof raw . refiner_attempt === 'number' ? raw . refiner_attempt : undefined ,
865+ refinerMaxAttempts : typeof raw . refiner_max_attempts === 'number' ? raw . refiner_max_attempts : undefined ,
849866 phaseHistory,
850867 conversation,
851868 intensity : intensityFor ( tokens , turns , priority ) ,
@@ -1585,6 +1602,7 @@ function App() {
15851602 onCreateMovement = { payload => createMovement . mutateAsync ( payload ) }
15861603 movementBusy = { createMovement . isPending }
15871604 movementError = { createMovement . error instanceof Error ? createMovement . error . message : undefined }
1605+ liveEvents = { orchestrationEvents . events }
15881606 />
15891607 ) }
15901608 { tab === 'agents' && (
@@ -1912,7 +1930,8 @@ function OrchestraFloor({
19121930 onAction,
19131931 onCreateMovement,
19141932 movementBusy,
1915- movementError
1933+ movementError,
1934+ liveEvents
19161935} : {
19171936 cards : Card [ ] ;
19181937 selectedCard ?: Card ;
@@ -1928,6 +1947,7 @@ function OrchestraFloor({
19281947 onCreateMovement : ( payload : ManualMovementPayload ) => Promise < unknown > ;
19291948 movementBusy : boolean ;
19301949 movementError ?: string ;
1950+ liveEvents : OrchestrationEvent [ ] ;
19311951} ) {
19321952 const counts = Object . fromEntries (
19331953 renderedStations . map ( station => [
@@ -1999,6 +2019,7 @@ function OrchestraFloor({
19992019 onCreateMovement = { onCreateMovement }
20002020 movementBusy = { movementBusy }
20012021 movementError = { movementError }
2022+ liveEvents = { liveEvents }
20022023 />
20032024 </ div >
20042025 ) ;
@@ -2159,7 +2180,8 @@ function ScoreConsole({
21592180 onAction,
21602181 onCreateMovement,
21612182 movementBusy,
2162- movementError
2183+ movementError,
2184+ liveEvents
21632185} : {
21642186 selectedCard ?: Card ;
21652187 debugPayload : string | null ;
@@ -2170,12 +2192,13 @@ function ScoreConsole({
21702192 onCreateMovement : ( payload : ManualMovementPayload ) => Promise < unknown > ;
21712193 movementBusy : boolean ;
21722194 movementError ?: string ;
2195+ liveEvents : OrchestrationEvent [ ] ;
21732196} ) {
2174- const [ scoreTab , setScoreTab ] = useState < 'score ' | 'events ' | 'tools ' > ( 'score ' ) ;
2197+ const [ scoreTab , setScoreTab ] = useState < 'plan ' | 'ledger ' | 'controls ' > ( 'plan ' ) ;
21752198 return (
21762199 < aside className = "scoreConsole pixelPanel" >
21772200 < div className = "consoleTabs" >
2178- { ( [ 'score ' , 'events ' , 'tools ' ] as const ) . map ( tab => (
2201+ { ( [ 'plan ' , 'ledger ' , 'controls ' ] as const ) . map ( tab => (
21792202 < button key = { tab } className = { scoreTab === tab ? 'scoreTabButton active' : 'scoreTabButton' } onClick = { ( ) => setScoreTab ( tab ) } > { tab . toUpperCase ( ) } </ button >
21802203 ) ) }
21812204 </ div >
@@ -2186,20 +2209,21 @@ function ScoreConsole({
21862209 < h2 > { selectedCard . identifier } </ h2 >
21872210 < h3 > { selectedCard . title } </ h3 >
21882211 < StatusPill status = { selectedCard . status } />
2189- { scoreTab === 'score ' && < MovementPanel card = { selectedCard } /> }
2190- { scoreTab === 'events ' && < MovementEvents card = { selectedCard } debugPayload = { debugPayload } /> }
2191- { scoreTab === 'tools ' && < MovementTools card = { selectedCard } busy = { busy } onDebug = { onDebug } onMove = { onMove } onAction = { onAction } /> }
2212+ { scoreTab === 'plan ' && < MovementPanel card = { selectedCard } /> }
2213+ { scoreTab === 'ledger ' && < MovementEvents card = { selectedCard } debugPayload = { debugPayload } liveEvents = { liveEvents } /> }
2214+ { scoreTab === 'controls ' && < MovementTools card = { selectedCard } busy = { busy } onDebug = { onDebug } onMove = { onMove } onAction = { onAction } /> }
21922215 </ div >
21932216 ) : (
21942217 < div className = "selectedScore empty" > < h2 > No movement selected</ h2 > < p > The orchestra is waiting for a movement.</ p > </ div >
21952218 ) }
2196- { debugPayload && scoreTab !== 'events ' && < pre className = "debugPanel" > { debugPayload } </ pre > }
2219+ { debugPayload && scoreTab !== 'ledger ' && < pre className = "debugPanel" > { debugPayload } </ pre > }
21972220 </ aside >
21982221 ) ;
21992222}
22002223
22012224function ManualMovementComposer ( { busy, error, onCreateMovement } : { busy : boolean ; error ?: string ; onCreateMovement : ( payload : ManualMovementPayload ) => Promise < unknown > } ) {
22022225 const [ title , setTitle ] = useState ( '' ) ;
2226+ const [ workspacePath , setWorkspacePath ] = useState ( ( ) => localStorage . getItem ( 'misconduct.lastMovementWorkspace' ) || '' ) ;
22032227 const [ description , setDescription ] = useState ( '' ) ;
22042228 const [ localError , setLocalError ] = useState < string | null > ( null ) ;
22052229 const ready = title . trim ( ) . length > 0 ;
@@ -2213,9 +2237,12 @@ function ManualMovementComposer({ busy, error, onCreateMovement }: { busy: boole
22132237
22142238 try {
22152239 setLocalError ( null ) ;
2240+ const target = workspacePath . trim ( ) ;
2241+ if ( target ) localStorage . setItem ( 'misconduct.lastMovementWorkspace' , target ) ;
22162242 await onCreateMovement ( {
22172243 title : title . trim ( ) ,
2218- description : description . trim ( )
2244+ description : description . trim ( ) ,
2245+ workspace_path : target || undefined
22192246 } ) ;
22202247 setTitle ( '' ) ;
22212248 setDescription ( '' ) ;
@@ -2227,49 +2254,91 @@ function ManualMovementComposer({ busy, error, onCreateMovement }: { busy: boole
22272254 return (
22282255 < form className = "movementComposer" onSubmit = { submit } >
22292256 < div className = "movementComposerHeader" >
2230- < p className = "eyebrow" > Conductor intake</ p >
2231- < button className = "button primary compact" disabled = { busy || ! ready } type = "submit" > < PlayCircle size = { 14 } /> Conduct</ button >
2257+ < p className = "eyebrow" > Movement intake</ p >
2258+ < button className = "button primary compact" disabled = { busy || ! ready } type = "submit" > < PlayCircle size = { 14 } /> Conduct in repo </ button >
22322259 </ div >
22332260 < label >
22342261 Movement title
2235- < input value = { title } onChange = { event => setTitle ( event . target . value ) } placeholder = "Add a ready-to-conduct score " />
2262+ < input value = { title } onChange = { event => setTitle ( event . target . value ) } placeholder = "Implement a focused change " />
22362263 </ label >
22372264 < label >
2238- Score brief
2239- < textarea value = { description } onChange = { event => setDescription ( event . target . value ) } placeholder = "Objective, constraints, repository context" />
2265+ Target directory / repo
2266+ < input value = { workspacePath } onChange = { event => setWorkspacePath ( event . target . value ) } placeholder = "/Users/you/project or blank for managed workspace" />
2267+ </ label >
2268+ < label >
2269+ Baton notes
2270+ < textarea value = { description } onChange = { event => setDescription ( event . target . value ) } placeholder = "Objective, constraints, files, commands, and evidence expected" />
22402271 </ label >
22412272 { ( localError || error ) && < p className = "formError" > { localError || error } </ p > }
22422273 </ form >
22432274 ) ;
22442275}
22452276
22462277function MovementPanel ( { card } : { card : Card } ) {
2278+ const workspace = card . workspacePath || card . workspaceTarget || card . repositoryPath ;
22472279 return (
22482280 < >
22492281 < div className = "scoreSheet" >
22502282 < div className = "staffLines" > < i /> < i /> < i /> < i /> < i /> </ div >
22512283 < div className = "scoreNotes" style = { { '--movement-intensity' : `${ card . intensity } %` } as React . CSSProperties } > ♪ ♫ ♩ ♬</ div >
22522284 </ div >
2285+ < CueChain card = { card } />
22532286 < dl className = "definitionList compact" >
2287+ < div > < dt > Workspace</ dt > < dd > { workspace ? < code > { workspace } </ code > : 'managed movement workspace' } </ dd > </ div >
22542288 < div > < dt > Section</ dt > < dd > { card . section } </ dd > </ div >
22552289 < div > < dt > Instrument</ dt > < dd > { card . instrumentName } </ dd > </ div >
22562290 < div > < dt > Agent</ dt > < dd > { card . agent } </ dd > </ div >
22572291 < div > < dt > Backend ID</ dt > < dd > < code > { card . backendId } </ code > </ dd > </ div >
22582292 </ dl >
22592293 { ( card . scoreSummary || card . scorePath ) && (
22602294 < div className = "scoreArtifact" >
2261- < b > Conductor score </ b >
2295+ < b > Conductor plan artifact </ b >
22622296 { card . scoreSummary && < p > { card . scoreSummary } </ p > }
22632297 { card . scorePath && < code > { card . scorePath } </ code > }
22642298 </ div >
22652299 ) }
2300+ { card . verdictPath && (
2301+ < div className = "scoreArtifact judgeArtifact" >
2302+ < b > Judge verdict artifact</ b >
2303+ < code > { card . verdictPath } </ code >
2304+ </ div >
2305+ ) }
22662306 < PhaseTrace entries = { card . phaseHistory } />
22672307 < AgentConversation messages = { card . conversation } activeAgent = { card . agent } />
22682308 < p > { card . message || card . retry || 'Waiting for the next orchestration cue.' } </ p >
22692309 </ >
22702310 ) ;
22712311}
22722312
2313+ function CueChain ( { card } : { card : Card } ) {
2314+ const phase = ( card . phase || card . stage || '' ) . toLowerCase ( ) ;
2315+ const completed = new Set ( card . phaseHistory . filter ( entry => entry . status === 'completed' ) . map ( entry => String ( entry . phase || '' ) . toLowerCase ( ) ) ) ;
2316+ const cueState = ( name : string ) => {
2317+ if ( completed . has ( name ) ) return 'complete' ;
2318+ if ( phase === name ) return 'active' ;
2319+ return 'waiting' ;
2320+ } ;
2321+ const refinerLabel = card . refinerMaxAttempts ? `${ card . refinerAttempt || 0 } /${ card . refinerMaxAttempts } ` : undefined ;
2322+ const cues = [
2323+ { id : 'conductor' , label : 'Conductor' , detail : 'plans movement' , artifact : card . scorePath , state : cueState ( 'conductor' ) } ,
2324+ { id : 'build' , label : 'Builder' , detail : 'edits workspace' , artifact : card . workspacePath || card . workspaceTarget || card . repositoryPath , state : cueState ( 'build' ) } ,
2325+ { id : 'judge' , label : 'Judge' , detail : 'checks evidence' , artifact : card . verdictPath , state : cueState ( 'judge' ) } ,
2326+ { id : 'refiner' , label : 'Refiner' , detail : refinerLabel ? `fixes findings ${ refinerLabel } ` : 'fixes findings' , artifact : card . judgeVerdict ? 'judge verdict loaded' : undefined , state : cueState ( 'refiner' ) }
2327+ ] ;
2328+
2329+ return (
2330+ < div className = "cueChain" aria-label = "Movement cue chain" >
2331+ { cues . map ( cue => (
2332+ < div key = { cue . id } className = { `cueStep ${ cue . state } ` } >
2333+ < b > { cue . label } </ b >
2334+ < span > { cue . detail } </ span >
2335+ < small > { cue . artifact || 'awaiting artifact' } </ small >
2336+ </ div >
2337+ ) ) }
2338+ </ div >
2339+ ) ;
2340+ }
2341+
22732342function PhaseTrace ( { entries } : { entries : PhaseHistoryEntry [ ] } ) {
22742343 if ( ! entries . length ) return null ;
22752344
@@ -2308,19 +2377,32 @@ function AgentConversation({ messages, activeAgent }: { messages: AgentConversat
23082377 ) ;
23092378}
23102379
2311- function MovementEvents ( { card, debugPayload } : { card : Card ; debugPayload : string | null } ) {
2380+ function MovementEvents ( { card, debugPayload, liveEvents } : { card : Card ; debugPayload : string | null ; liveEvents : OrchestrationEvent [ ] } ) {
2381+ const movementEvents = liveEvents
2382+ . filter ( event => {
2383+ const ids = [ event . issue_id , event . issue_identifier ] . filter ( Boolean ) ;
2384+ return ids . includes ( card . backendId ) || ids . includes ( card . identifier ) ;
2385+ } )
2386+ . slice ( - 18 )
2387+ . reverse ( ) ;
23122388 const events = [
23132389 [ 'Movement' , card . movement ] ,
23142390 [ 'Status' , card . status ] ,
23152391 [ 'Turns' , String ( card . turns ) ] ,
23162392 [ 'Notes' , card . tokens . toLocaleString ( ) ] ,
2393+ [ 'Workspace' , card . workspacePath || card . workspaceTarget || card . repositoryPath || 'managed movement workspace' ] ,
23172394 [ 'Score path' , card . scorePath || 'none' ] ,
2395+ [ 'Verdict path' , card . verdictPath || 'none' ] ,
23182396 [ 'Retry due' , card . retry || 'none' ] ,
23192397 [ 'Last message' , card . message || 'none reported' ]
23202398 ] ;
23212399 return (
23222400 < div className = "movementTimeline" >
23232401 { events . map ( ( [ label , value ] ) => < div className = "timelineRow" key = { label } > < b > { label } </ b > < span > { value } </ span > </ div > ) }
2402+ < div className = "liveMovementLedger" >
2403+ < h3 > Live pit ledger</ h3 >
2404+ { movementEvents . length ? movementEvents . map ( event => < EventRow event = { event } key = { event . id } /> ) : < p className = "subtle" > No live events for this movement yet.</ p > }
2405+ </ div >
23242406 { debugPayload && < pre className = "debugPanel inline" > { debugPayload } </ pre > }
23252407 </ div >
23262408 ) ;
@@ -2774,7 +2856,13 @@ function WorkflowPanel({
27742856 </ div >
27752857
27762858 < aside className = "workflowInspector pixelPanel" >
2777- < div className = "panelHeader" > < div > < p className = "eyebrow" > Rehearsal Desk</ p > < h2 > Judge / Refiner / Location</ h2 > </ div > </ div >
2859+ < div className = "panelHeader" > < div > < p className = "eyebrow" > Rehearsal Desk</ p > < h2 > Activation / Location</ h2 > </ div > </ div >
2860+ < div className = "workflowMap" >
2861+ < div > < b > 1. Compose</ b > < span > WORKFLOW.md defines the orchestra, roles, and defaults.</ span > </ div >
2862+ < div > < b > 2. Rehearse</ b > < span > Readiness checks verify provider, auth, roster, and workspace.</ span > </ div >
2863+ < div > < b > 3. Conduct</ b > < span > A movement targets a repo or managed workspace and starts the phase chain.</ span > </ div >
2864+ < div > < b > 4. Observe</ b > < span > Conductor, Builder, Judge, and Refiner publish artifacts and ledger events.</ span > </ div >
2865+ </ div >
27782866 < div className = "targetPreview" >
27792867 < b > Backend-managed root</ b >
27802868 < code > { filesQuery . data ?. root || ( enabled ? 'loading…' : 'waiting for desktop backend' ) } </ code >
@@ -2807,8 +2895,8 @@ function WorkflowPanel({
28072895 </ div >
28082896
28092897 < div className = "resultPanel" >
2810- < h3 > Judge / Refiner </ h3 >
2811- { review ? < pre > { JSON . stringify ( review , null , 2 ) } </ pre > : < p className = "subtle" > No review yet.</ p > }
2898+ < h3 > Workflow validation judge </ h3 >
2899+ { review ? < pre > { JSON . stringify ( review , null , 2 ) } </ pre > : < p className = "subtle" > No validation review yet.</ p > }
28122900 </ div >
28132901
28142902 < div className = "resultPanel" >
@@ -3016,7 +3104,7 @@ function SafetyPanel() {
30163104 < section className = "doc pixelPanel" >
30173105 < h2 > < ShieldAlert size = { 20 } /> Safety Posture</ h2 >
30183106 < ul className = "checkList" >
3019- < li > Agent working directories are constrained to per-issue workspaces .</ li >
3107+ < li > Agent working directories are explicit: a movement target repo or a managed movement workspace .</ li >
30203108 < li > Workspace keys allow only alphanumeric, dot, underscore, and dash characters.</ li >
30213109 < li > Secrets use environment indirection and are not emitted in public config JSON.</ li >
30223110 < li > Hooks time out to prevent orchestration stalls.</ li >
0 commit comments