Intelligence

Artifacts

Browse the repository, read documents, and manage the governance folders. Source, runtime, and infrastructure are read-only.

Repository
README.md
CONSTITUTION_COMPLIANCE_AUDIT_V1.mdREADME.md
repositories/aaf-holdings/hq01/app/mission-control/[id]/assignments/[aid]/page.tsx
12.9 KB
import Link from "next/link";
import { notFound } from "next/navigation";
import { Layers, History as HistoryIcon, FileCode, HardHat } from "lucide-react";
import { PageHeader } from "@/components/layout/page-header";
import { Card, CardContent } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { MetaItem, FieldList } from "@/components/shared/field-list";
import { PriorityBadge } from "@/components/shared/status-badge";
import { AssignmentStatusBadge } from "@/components/missions/assignment-status-badge";
import { ReportList } from "@/components/missions/report-list";
import { AssetList } from "@/components/assets/asset-list";
import { WorkerInstantiate } from "@/components/workers/worker-instantiate";
import { AutoRefresh } from "@/components/sessions/auto-refresh";
import {
  getAssignmentView,
  collectAssignmentReports,
} from "@/lib/missions/assignments";
import { listReportsForAssignment } from "@/lib/missions/reports";
import { listAssetsForAssignment } from "@/lib/assets/ledger";
import { listTemplates } from "@/lib/workers/templates";
import { listInstancesForAssignment, instanceHistory } from "@/lib/workers/instances";
import { getMission, collectReports } from "@/lib/missions/manager";
import type { ExecChainEventType } from "@/lib/missions/types";

export const dynamic = "force-dynamic";

const EVENT_LABEL: Partial<Record<ExecChainEventType, string>> = {
  assignment_created: "Assignment created",
  execution_context_created: "Execution context created",
  dispatch_started: "Dispatch started",
  dispatch_completed: "Dispatch completed",
};

export default function AssignmentDetailPage({
  params,
}: {
  params: { id: string; aid: string };
}) {
  // Reconcile completed sessions into reports + assignment status, then read.
  collectReports(params.id);
  collectAssignmentReports(params.id);

  const view = getAssignmentView(params.id, params.aid);
  if (!view) notFound();
  const { assignment, execution_context: ctx, history } = view;
  const mission = getMission(params.id);
  const reports = listReportsForAssignment(params.id, params.aid);
  const assets = listAssetsForAssignment(params.aid);
  const templates = listTemplates().map((t) => ({ id: t.id, name: t.name }));
  const workers = listInstancesForAssignment(params.aid);
  const currentWorker = assignment.worker_instance_id
    ? workers.find((w) => w.id === assignment.worker_instance_id) ?? null
    : null;
  const currentWorkerHistory = currentWorker ? instanceHistory(currentWorker.id) : [];

  return (
    <div>
      <AutoRefresh intervalMs={5000} />
      <PageHeader
        eyebrow={`${assignment.mission_id} · ${assignment.work_order_id} · ${assignment.id}`}
        title={`Assignment ${assignment.id.replace("ASSIGNMENT-", "A-")}`}
        description={`Delegated to ${assignment.executive}.`}
        back={{
          label: mission ? mission.title : "Mission",
          href: `/mission-control/${assignment.mission_id}`,
        }}
        actions={
          <div className="flex flex-col items-end gap-2">
            <div className="flex items-center gap-2">
              <PriorityBadge priority={assignment.priority} />
              <AssignmentStatusBadge status={assignment.status} />
            </div>
            <WorkerInstantiate
              missionId={assignment.mission_id}
              aid={assignment.id}
              templates={templates}
              status={assignment.status}
            />
          </div>
        }
      />

      <div className="grid grid-cols-1 gap-8 lg:grid-cols-3">
        <div className="space-y-6 lg:col-span-2">
          <Card>
            <CardContent className="p-6">
              <div className="mb-4 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.1em] text-muted-foreground">
                <Layers className="h-3.5 w-3.5" /> Execution Context
                <span className="font-normal lowercase tracking-normal text-muted-foreground/70">
                  (immutable)
                </span>
              </div>
              {!ctx ? (
                <p className="text-sm text-muted-foreground">No execution context.</p>
              ) : (
                <div className="grid grid-cols-1 gap-x-6 gap-y-3 sm:grid-cols-2">
                  <Ctx label="Organization">{ctx.organization}</Ctx>
                  <Ctx label="Company">{ctx.company ?? "—"}</Ctx>
                  <Ctx label="Product">{ctx.product}</Ctx>
                  <Ctx label="Repository" mono>{ctx.repository}</Ctx>
                  <Ctx label="Mission" mono>{ctx.mission_id}</Ctx>
                  <Ctx label="Objective" mono>{ctx.objective_id}</Ctx>
                  <Ctx label="Work Order" mono>{ctx.work_order_id}</Ctx>
                  <Ctx label="Assignment" mono>{ctx.assignment_id}</Ctx>
                  <Ctx label="Executive" mono>{ctx.executive}</Ctx>
                  <Ctx label="Workspace" mono full>{ctx.workspace}</Ctx>
                  <Ctx label="Runtime Root" mono full>{ctx.runtime_root}</Ctx>
                  <Ctx label="Report Location" mono full>{ctx.report_location}</Ctx>
                  <Ctx label="Allowed Paths" mono full>{ctx.allowed_paths.join(", ")}</Ctx>
                  <Ctx label="Expected Outputs" full>{ctx.expected_outputs.join(", ")}</Ctx>
                </div>
              )}
              {ctx && ctx.success_criteria.length > 0 && (
                <div className="mt-5">
                  <FieldList label="Success Criteria" items={ctx.success_criteria} />
                </div>
              )}
            </CardContent>
          </Card>

          <Card>
            <CardContent className="p-6">
              <div className="mb-3 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.1em] text-muted-foreground">
                <HardHat className="h-3.5 w-3.5" /> Workers ({workers.length})
              </div>
              {currentWorker && (
                <div className="mb-3 rounded-md border border-border p-3">
                  <div className="flex flex-wrap items-center gap-2 text-[13px]">
                    <span className="font-mono text-[11px] text-muted-foreground">
                      {currentWorker.id.replace("WORKER-", "W-")}
                    </span>
                    <Link href={`/workers/${currentWorker.template_id}`} className="font-medium text-foreground hover:underline">
                      {currentWorker.template_id} v{currentWorker.version}
                    </Link>
                    <span className="rounded-full bg-secondary px-2 py-0.5 text-[10px] font-medium text-foreground/70">
                      {currentWorker.status}
                    </span>
                    <span className="font-mono text-[11px] text-muted-foreground">{currentWorker.model}</span>
                  </div>
                  <div className="mt-1 text-[11px] text-muted-foreground">
                    {currentWorker.session_id && (
                      <Link href={`/sessions/${currentWorker.session_id}`} className="underline underline-offset-2">session</Link>
                    )}
                    {currentWorker.report_id && <> · report {currentWorker.report_id.replace("REPORT-", "R-")}</>}
                  </div>
                  <ol className="mt-2 space-y-0.5 border-l border-border pl-3">
                    {currentWorkerHistory.map((e, i) => (
                      <li key={i} className="text-[11px] text-muted-foreground">
                        <span className="font-medium text-foreground/70">{e.type.replace(/_/g, " ")}</span>
                        {e.detail ? ` — ${e.detail}` : ""} · {new Date(e.at).toLocaleString()}
                      </li>
                    ))}
                  </ol>
                </div>
              )}
              {workers.length === 0 ? (
                <p className="text-sm text-muted-foreground">
                  No worker instantiated yet. Instantiate one above; it runs and
                  terminates — no worker persists.
                </p>
              ) : (
                <ul className="divide-y divide-border text-[12px]">
                  {workers.filter((w) => w.id !== currentWorker?.id).map((w) => (
                    <li key={w.id} className="flex items-center gap-2 py-1.5">
                      <span className="font-mono text-[11px] text-muted-foreground">{w.id.replace("WORKER-", "W-")}</span>
                      <span className="flex-1">{w.template_id}</span>
                      <span className="text-muted-foreground">{w.status}</span>
                    </li>
                  ))}
                </ul>
              )}
            </CardContent>
          </Card>

          <Card>
            <CardContent className="p-6">
              <div className="mb-3 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.1em] text-muted-foreground">
                <FileCode className="h-3.5 w-3.5" /> Reports ({reports.length})
              </div>
              <ReportList reports={reports} emptyText="No reports yet for this assignment." />
            </CardContent>
          </Card>

          <Card>
            <CardContent className="p-6">
              <div className="mb-3 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.1em] text-muted-foreground">
                <Layers className="h-3.5 w-3.5" /> Produced Assets ({assets.length})
              </div>
              <AssetList assets={assets} showMission={false} emptyText="No assets registered from this assignment." />
            </CardContent>
          </Card>

          <Card>
            <CardContent className="p-6">
              <div className="mb-4 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.1em] text-muted-foreground">
                <HistoryIcon className="h-3.5 w-3.5" /> History
              </div>
              <ol className="space-y-3">
                {history.map((e, i) => (
                  <li key={i} className="flex gap-3">
                    <div className="mt-1 h-1.5 w-1.5 shrink-0 rounded-full bg-accent" />
                    <div>
                      <div className="text-[13px] font-medium text-foreground">
                        {EVENT_LABEL[e.type] ?? e.type}
                        {e.detail && (
                          <span className="font-normal text-muted-foreground"> — {e.detail}</span>
                        )}
                      </div>
                      <div className="text-[11px] text-muted-foreground">
                        {new Date(e.at).toLocaleString()}
                        {e.session_id && (
                          <>
                            {" · "}
                            <Link
                              href={`/sessions/${e.session_id}`}
                              className="font-mono underline underline-offset-2"
                            >
                              session
                            </Link>
                          </>
                        )}
                      </div>
                    </div>
                  </li>
                ))}
              </ol>
            </CardContent>
          </Card>
        </div>

        <aside className="space-y-6">
          <Card>
            <CardContent className="space-y-5 p-6">
              <MetaItem label="Status">
                <AssignmentStatusBadge status={assignment.status} />
              </MetaItem>
              <Separator />
              <MetaItem label="Worker Template">
                {assignment.worker_template ?? "— (placeholder; no workers yet)"}
              </MetaItem>
              {assignment.session && (
                <>
                  <Separator />
                  <MetaItem label="Session">
                    <Link
                      href={`/sessions/${assignment.session.session_id}`}
                      className="inline-flex items-center gap-1.5 font-mono text-[12px] underline underline-offset-2"
                    >
                      <FileCode className="h-3.5 w-3.5" />
                      {assignment.session.session_id.slice(0, 22)}…
                    </Link>
                    <div className="mt-1 text-[11px] text-muted-foreground">
                      {assignment.session.status}
                    </div>
                  </MetaItem>
                </>
              )}
            </CardContent>
          </Card>

          <div className="font-mono text-[11px] text-muted-foreground">
            /srv/aaf/missions/{assignment.mission_id}/assignments/{assignment.id}/
          </div>
        </aside>
      </div>
    </div>
  );
}

function Ctx({
  label,
  children,
  mono,
  full,
}: {
  label: string;
  children: React.ReactNode;
  mono?: boolean;
  full?: boolean;
}) {
  return (
    <div className={full ? "sm:col-span-2" : ""}>
      <div className="text-[10px] font-medium uppercase tracking-[0.1em] text-muted-foreground">
        {label}
      </div>
      <div
        className={
          "mt-0.5 break-all text-[13px] text-foreground" + (mono ? " font-mono text-[12px]" : "")
        }
      >
        {children}
      </div>
    </div>
  );
}

root · /srv/aaf