mirror of
https://github.com/himanshudongre/smriti.git
synced 2026-08-28 05:14:59 +00:00
Surface active work claims and branch disposition in LineagePage
This commit is contained in:
parent
5f5e15c7aa
commit
8d01e1e866
4 changed files with 115 additions and 19 deletions
|
|
@ -91,6 +91,7 @@ class SessionNode(BaseModel):
|
|||
branch_name: str
|
||||
forked_from_checkpoint_id: Optional[uuid.UUID]
|
||||
seeded_commit_id: Optional[uuid.UUID]
|
||||
branch_disposition: str = "active"
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
|
|
|||
|
|
@ -357,6 +357,12 @@ export async function getLineage(spaceId: string): Promise<import('../types').Li
|
|||
return requestV5<import('../types').LineageResponse>(`/lineage/spaces/${spaceId}`);
|
||||
}
|
||||
|
||||
export async function getSpaceState(spaceId: string): Promise<import('../types').SpaceStateResponse> {
|
||||
const resp = await fetch(`${API_BASE}/api/v4/chat/spaces/${spaceId}/state`);
|
||||
if (!resp.ok) throw new Error(`Failed to fetch space state: ${resp.status}`);
|
||||
return resp.json();
|
||||
}
|
||||
|
||||
export async function compareCheckpoints(
|
||||
aId: string,
|
||||
bId: string,
|
||||
|
|
|
|||
|
|
@ -17,14 +17,15 @@
|
|||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { getLineage, compareCheckpoints, forkSession } from '../api/client';
|
||||
import { getLineage, getSpaceState, compareCheckpoints, forkSession } from '../api/client';
|
||||
import type {
|
||||
ActiveClaimSummary,
|
||||
CheckpointNode,
|
||||
SessionNode,
|
||||
LineageResponse,
|
||||
CompareResponse,
|
||||
} from '../types';
|
||||
import { GitBranch, GitCommit, Loader2, X, ArrowLeft } from 'lucide-react';
|
||||
import { GitBranch, GitCommit, Loader2, X, ArrowLeft, Zap } from 'lucide-react';
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -407,6 +408,7 @@ function BranchSection({
|
|||
onNavigate,
|
||||
sessionRef,
|
||||
isMain,
|
||||
disposition,
|
||||
}: {
|
||||
branchName: string;
|
||||
checkpoints: CheckpointNode[];
|
||||
|
|
@ -418,6 +420,7 @@ function BranchSection({
|
|||
onNavigate: (sessionId: string) => void;
|
||||
sessionRef: React.RefObject<HTMLDivElement | null>;
|
||||
isMain: boolean;
|
||||
disposition?: string;
|
||||
}) {
|
||||
const mostRecentCheckpoint = checkpoints[checkpoints.length - 1];
|
||||
const activeSession = sessions.find(s => s.id === currentSessionId) ?? sessions[0] ?? null;
|
||||
|
|
@ -441,6 +444,17 @@ function BranchSection({
|
|||
forked from <code className="text-blue-400 font-mono">{forkSourceId.slice(0, 7)}</code>
|
||||
</span>
|
||||
)}
|
||||
{!isMain && disposition && disposition !== 'active' && (
|
||||
<span className={`text-[9px] font-medium px-1.5 py-0.5 rounded-full border ${
|
||||
disposition === 'integrated'
|
||||
? 'text-green-400 border-green-500/30 bg-green-900/20'
|
||||
: disposition === 'abandoned'
|
||||
? 'text-red-400 border-red-500/30 bg-red-900/20'
|
||||
: 'text-gray-400 border-gray-600 bg-gray-800/30'
|
||||
}`}>
|
||||
{disposition}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{checkpoints.length > 0 ? (
|
||||
|
|
@ -562,6 +576,7 @@ export function LineagePage() {
|
|||
const currentSessionId = searchParams.get('session');
|
||||
|
||||
const [lineage, setLineage] = useState<LineageResponse | null>(null);
|
||||
const [activeClaims, setActiveClaims] = useState<ActiveClaimSummary[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [err, setErr] = useState('');
|
||||
|
||||
|
|
@ -577,8 +592,14 @@ export function LineagePage() {
|
|||
|
||||
useEffect(() => {
|
||||
if (!spaceId) return;
|
||||
getLineage(spaceId)
|
||||
.then(setLineage)
|
||||
Promise.all([
|
||||
getLineage(spaceId),
|
||||
getSpaceState(spaceId).catch(() => null),
|
||||
])
|
||||
.then(([lin, state]) => {
|
||||
setLineage(lin);
|
||||
setActiveClaims(state?.active_claims ?? []);
|
||||
})
|
||||
.catch(e => setErr(e.message || 'Failed to load lineage'))
|
||||
.finally(() => setLoading(false));
|
||||
}, [spaceId]);
|
||||
|
|
@ -682,6 +703,41 @@ export function LineagePage() {
|
|||
|
||||
{lineage && !loading && (
|
||||
<div className="space-y-10">
|
||||
{/* Active work claims */}
|
||||
{activeClaims.length > 0 && (
|
||||
<section>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Zap className="w-4 h-4 text-amber-400" />
|
||||
<h2 className="text-sm font-semibold text-white">Active work</h2>
|
||||
<span className="text-[10px] text-gray-600">{activeClaims.length} claim{activeClaims.length !== 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{activeClaims.map(claim => (
|
||||
<div
|
||||
key={claim.id}
|
||||
className="rounded-lg border border-amber-500/30 bg-amber-900/10 px-4 py-3"
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-[10px] text-amber-400 font-mono border border-amber-500/30 px-1.5 py-px rounded">
|
||||
{claim.agent}
|
||||
</span>
|
||||
<span className="text-[10px] text-gray-500 border border-gray-700 px-1.5 py-px rounded">
|
||||
{claim.intent_type}
|
||||
</span>
|
||||
<span className="text-[10px] text-gray-600">
|
||||
on <code className="text-gray-500">{claim.branch_name}</code>
|
||||
{claim.base_commit_hash && (
|
||||
<> from <code className="text-blue-400">{claim.base_commit_hash}</code></>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-300">{claim.scope}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Main branch */}
|
||||
<BranchSection
|
||||
branchName="main"
|
||||
|
|
@ -697,21 +753,33 @@ export function LineagePage() {
|
|||
/>
|
||||
|
||||
{/* Fork branches */}
|
||||
{forkBranches.map(branch => (
|
||||
<BranchSection
|
||||
key={branch}
|
||||
branchName={branch}
|
||||
checkpoints={checkpointsByBranch.get(branch) ?? []}
|
||||
sessions={sessionsByBranch.get(branch) ?? []}
|
||||
currentSessionId={currentSessionId}
|
||||
selected={selected}
|
||||
onSelect={handleSelect}
|
||||
onFork={setForkTarget}
|
||||
onNavigate={id => navigate(`/sessions/${id}`)}
|
||||
sessionRef={currentSessionRef}
|
||||
isMain={false}
|
||||
/>
|
||||
))}
|
||||
{forkBranches.map(branch => {
|
||||
// Derive disposition from sessions on this branch.
|
||||
// If any session is "active", the branch is active.
|
||||
// Otherwise use the most recent session's disposition.
|
||||
const branchSessions = sessionsByBranch.get(branch) ?? [];
|
||||
const hasActive = branchSessions.some(s => (s.branch_disposition || 'active') === 'active');
|
||||
const disposition = hasActive
|
||||
? 'active'
|
||||
: (branchSessions[0]?.branch_disposition || 'active');
|
||||
|
||||
return (
|
||||
<BranchSection
|
||||
key={branch}
|
||||
branchName={branch}
|
||||
checkpoints={checkpointsByBranch.get(branch) ?? []}
|
||||
sessions={branchSessions}
|
||||
currentSessionId={currentSessionId}
|
||||
selected={selected}
|
||||
onSelect={handleSelect}
|
||||
onFork={setForkTarget}
|
||||
onNavigate={id => navigate(`/sessions/${id}`)}
|
||||
sessionRef={currentSessionRef}
|
||||
isMain={false}
|
||||
disposition={disposition}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -208,9 +208,30 @@ export interface SessionNode {
|
|||
branch_name: string;
|
||||
forked_from_checkpoint_id: string | null;
|
||||
seeded_commit_id: string | null;
|
||||
branch_disposition: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ActiveClaimSummary {
|
||||
id: string;
|
||||
agent: string;
|
||||
branch_name: string;
|
||||
scope: string;
|
||||
intent_type: string;
|
||||
claimed_at: string;
|
||||
expires_at: string;
|
||||
base_commit_hash: string | null;
|
||||
}
|
||||
|
||||
export interface SpaceStateResponse {
|
||||
space: { id: string; name: string; description: string | null };
|
||||
head: any;
|
||||
commit: any;
|
||||
active_branches: any[];
|
||||
active_claims: ActiveClaimSummary[];
|
||||
divergence: any;
|
||||
}
|
||||
|
||||
export interface LineageResponse {
|
||||
space_id: string;
|
||||
checkpoints: CheckpointNode[];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue