jacquardSnapshot

← snapshot

7205 bytes
/** Wire types mirroring jac-serve's DTOs (crates/jac-serve/src/dto.rs). */

export type ProvenanceLabel = "human" | "agent" | "mixed";

export interface ActorView {
  kind: "human" | "agent" | "unknown";
  id: string;
  display_name?: string;
}

export interface SnapshotDto {
  id: string;
  short: string;
  tree: string;
  parents: string[];
  author: ActorView;
  provenance: ProvenanceLabel;
  message: string;
  at: number;
}

export interface RefDto {
  name: string;
  head: string;
}

export interface RepoSummary {
  slug: string;
  org: { id: string; name: string };
  default_ref: string;
  refs: number;
  decisions: { unsettled: number; settled: number };
  head: SnapshotDto | null;
  created_at: number;
}

export interface RepoDetail {
  slug: string;
  org: { id: string; name: string };
  default_ref: string;
  refs: RefDto[];
  humans: ActorView[];
  agents: ActorView[];
  decisions: { unsettled: number; settled: number };
  object_count: number;
  created_at: number;
}

export interface AttestationDto {
  id: string;
  decision: string;
  attestor: ActorView;
  statement: string;
  at: number;
}

export interface DecisionDto {
  id: string;
  short: string;
  title: string;
  rationale: string;
  families: string[];
  proposed_by: ActorView;
  scope: string[];
  at: number;
  state: "unsettled" | "settled";
  attestation?: AttestationDto;
}

export type VerdictDto =
  | {
      verdict: "admitted";
      decisions_checked: number;
      ungoverned: boolean;
      ref_moved: boolean;
      into_head?: string;
    }
  | {
      verdict: "blocked";
      reason: string;
      unsettled: string[];
      ref_moved: boolean;
    };

export interface PromotePreview {
  verdict: VerdictDto;
  changed: string[];
  fast_forward: boolean;
}

export interface FileEntry {
  path: string;
  blob: string;
  size: number;
}

export interface SnapshotDetail {
  snapshot: SnapshotDto;
  files: FileEntry[];
  changed_from_parent: string[];
}

export interface TreeEntryDto {
  name: string;
  kind: "blob" | "tree";
  id: string;
  size?: number;
}

export interface BlobDto {
  id: string;
  size: number;
  content: string | null;
  binary: boolean;
}

export interface PublicationRow {
  org: string;
  kind: "content" | "decision";
  at: number;
}

export interface IntroductionRow {
  token: string;
  parties: [string, string];
  at: number;
}

export interface SketchMatchRow {
  publication: string;
  org: string;
  similarity_permille: number;
  similarity_percent: string;
}

export interface ApiErrorBody {
  error: { code: string; message: string };
}

/** The init request — the whole ceremony in one POST. */
export interface InitRepoRequest {
  name: string;
  founder: { display_name: string };
  default_ref?: string;
  agents?: { model: string }[];
  initial_commit?: {
    message: string;
    files: { path: string; content: string }[];
    provenance?: ProvenanceLabel;
  };
  founding_decision?: {
    title: string;
    rationale: string;
    families?: string[];
    scope?: string[];
    attestation?: { statement: string };
  };
}

export interface InitRepoResponse {
  slug: string;
  org: { id: string; name: string };
  founder: ActorView;
  agents: ActorView[];
  default_ref: string;
  head: SnapshotDto | null;
  founding_decision: DecisionDto | null;
  created_at: number;
  honesty: string;
}

export const RATIONALE_FAMILIES: { label: string; hint: string }[] = [
  { label: "alternatives", hint: "Alternatives considered and what ruled them out." },
  { label: "constraints", hint: "The constraint that forced this shape." },
  {
    label: "missed-abstraction",
    hint: "An abstraction within reach that was skipped — deliberately or not.",
  },
  { label: "deferred-work", hint: "Known debt: what another week would have changed." },
  {
    label: "confidence-risk",
    hint: "The part the author is least sure will hold up.",
  },
];

/** What a reviewer meant by speaking up. */
export type RemarkKind = "suggestion" | "question" | "note";

/** What they were looking at when they said it. */
export interface RemarkAnchorDto {
  kind: "decision" | "file" | "snapshot" | "gate";
  id: string;
  /** The exact span they had selected, when they had one. */
  quote: string | null;
}

/**
 * Something a reviewer said that should be acted on.
 *
 * Not an engine object: a remark is conversation about the work, not an
 * attestable claim about it. The path to something durable is to draft it
 * into a decision, which a human then attests.
 */
export interface RemarkDto {
  id: string;
  anchor: RemarkAnchorDto;
  /** The reviewer's own words, verbatim. */
  body: string;
  kind: RemarkKind;
  by: { id: string; display_name: string | null };
  at: number;
  state: "open" | "drafted" | "declined";
  outcome: { decision?: string; because?: string } | null;
}


/**
 * What an import found, and — more to the point — what it had to guess.
 *
 * GitHub records who pushed, which is not whose hands made the work, and a PR
 * description, which is a summary rather than a rationale anyone signed. The
 * report names both gaps rather than papering over them.
 */
export interface ImportReport {
  slug: string;
  source: {
    owner: string;
    repo: string;
    pull_request: number;
    base: string;
    head: string;
    author: string;
  };
  imported: {
    files: number;
    changed_on_github: number;
    truncated: boolean;
    skipped: string[];
  };
  decision: string;
  inference: {
    subject: string;
    provenance: ProvenanceLabel;
    /** The rule that fired, in words a reader can argue with. */
    because: string;
    /** True when the label is a working assumption, not evidence. */
    assumed: boolean;
  };
  /** What a person still has to answer. */
  gaps: { kind: string; ask: string; why: string }[];
  honesty: string;
}


/** One repo's tally inside a shelf. */
export interface ShelfRepo {
  slug: string;
  source: string;
  snapshots: number;
  provenance: { human: number; agent: number; mixed: number; assumed: number };
}

/** What a batch import left on disk. */
export interface ShelfIndex {
  kind: string;
  root: string;
  imported: number;
  failed: number;
  failures: { repo: string; why: string }[];
  provenance: {
    human: number;
    agent: number;
    mixed: number;
    /** Snapshots labelled human with no evidence either way. */
    assumed: number;
    note: string;
  };
  repos: ShelfRepo[];
}


/** One thing waiting on a person. */
export interface AwaitingItem {
  kind: "decision" | "remark";
  /** 0 highest. A decision outranks a remark because nothing else clears it. */
  rank: number;
  repo: string;
  id: string;
  short: string;
  title: string;
  at: number;
  /** What would actually clear it, in plain words. */
  asks: string;
  href: string;
  proposed_by?: ActorView;
  scope?: string[];
  remark_kind?: string;
  quote?: string | null;
}

/** The inbox, across every hosted repo. */
export interface Awaiting {
  items: AwaitingItem[];
  counts: { decisions: number; remarks: number; total: number };
  repos: {
    slug: string;
    org: string;
    unsettled: number;
    settled: number;
    open_remarks: number;
  }[];
  honesty: string;
}