Fork Horse repository, listed by its owner. Publisher snapshot.

packages/forkhorse-dashboard/src/contracts.ts

Snapshot at 34e3175

Containing directory

9,876 bytes · typescriptView raw
1/** FH-060 cumulative snapshots; absent fields preserve legacy state compatibility. */
2export interface Usage {
3  inputTokens: number;
4  outputTokens: number;
5  model?: string;
6  source: "reported" | "metered";
7  at: string;
8}
9export interface UsageSources {
10  reported: Usage | null;
11  metered: Usage | null;
12}
13export type TokenBudgetState = "unchecked" | "within" | "near" | "over";
14export interface BlockerResolution {
15  at: string;
16  by: "release" | "promote" | "owner";
17  reason?: string;
18}
19/** FH-048 public history. Missing fields on legacy records mean open/unknown. */
20export interface WireBlocker {
21  id: string;
22  packet: string;
23  claimingAgent: string;
24  pushingAgent: string;
25  repoName: string;
26  sha: string;
27  paths: string[];
28  resolved?: BlockerResolution | null;
29  at?: string | null;
30  pushingPacket?: string | null;
31}
32export interface BlockerView {
33  id: string;
34  agent: string;
35  packet: string;
36  paths: string[];
37  reason: string;
38  resolved: boolean;
39  resolution?: BlockerResolution | null;
40  claimingAgent?: string;
41  pushingAgent?: string;
42  sha?: string;
43  repoName?: string;
44  at?: string | null;
45  pushingPacket?: string | null;
46}
47
48/** View projection of the public FH-005 wire state; never includes credentials. */
49export interface ProjectState {
50  project: {
51    id: string;
52    repoName: string;
53    defaultBranch: string;
54    remote?: string;
55    events?: "pending" | "active";
56    settings?: { requireReceipt: boolean };
57  };
58  canonicalHead?: string;
59  recentActivity?: Activity[];
60  agents: { name: string; createdAt?: string | null }[];
61  forks: { agent: string; repoName: string; head: string; events?: "pending" | "active" }[];
62  packets: Packet[];
63  claims: {
64    packet: string;
65    agent: string;
66    repoName: string;
67    paths: string[];
68    active: boolean;
69    usage?: UsageSources;
70    createdAt?: string;
71  }[];
72  blockers: BlockerView[];
73  races?: Race[];
74  reviews: Review[];
75  ledger: {
76    id?: string;
77    before?: string;
78    review: string;
79    sha: string;
80    promotedAt: string;
81    receiptDigest?: string | null;
82    race?: string;
83  }[];
84  reconcilerAlerts: {
85    sha: string;
86    message: string;
87    id?: string;
88    repoName?: string;
89    ref?: string;
90    before?: string;
91    after?: string;
92  }[];
93  promotionPending?: LedgerEntry | null;
94}
95
96export interface Packet {
97  id: string;
98  title: string;
99  goal: string;
100  paths: string[];
101  acceptance: string[];
102  mode?: "standard" | "race";
103  tokenBudget?: number | null;
104  /** Absent legacy records are treated as null. */
105  archived?: null | { at: string; by: "owner" };
106}
107
108export interface Review {
109  id: string;
110  repoName: string;
111  agent: string;
112  packet: string;
113  sha: string;
114  receiptDigest: string | null;
115  requestedAt?: string;
116  approvedAt?: string | null;
117  base?: string;
118  status: "pending" | "approved" | "stale" | "promoted";
119  /** Raw core flag, independent of the derived display status. */
120  promoted?: boolean;
121  stale?: boolean;
122  returned?: null | { reason: string; at: string };
123  approval?: { sha: string; receiptDigest: string | null } | null;
124  receipt?: ReceiptSummary | null;
125  usageAtReview?: UsageSources;
126}
127
128/** FH-032 fixed public wire summary: logs belong only to the owner receipt endpoint. */
129export type ReceiptStatus = "running" | "passed" | "failed" | "infrastructure-error" | "timed-out";
130export interface ReceiptSummary {
131  status: ReceiptStatus;
132  trigger: "review-created" | "before-promote";
133  digest: string | null;
134  startedAt: string;
135  finishedAt: string | null;
136  durationMs: number | null;
137  checks: {
138    name: string;
139    tier: "full" | "quick";
140    runtime?: "container" | "dynamic-worker";
141    message?: string;
142    outcome: ReceiptStatus;
143    durationMs: number | null;
144  }[];
145}
146/** Stored runner receipt returned by the owner-only endpoint. No bearer token plumbing. */
147export interface OwnerReceipt {
148  digest: string;
149  receipt: {
150    commit: string;
151    checks: {
152      check: { name: string };
153      logs: { stdout: string; stderr: string; truncated: boolean };
154    }[];
155  };
156}
157
158/** FH-003 receive sequence is the cursor, not event time. */
159export interface Activity {
160  sequence: number;
161  namespace: string;
162  repoName: string;
163  agent: string | null;
164  ref: string;
165  before: string;
166  after: string;
167  changedPaths: string[];
168  changedPathCount?: number;
169  changedPathsTruncated?: boolean;
170  eventTimestamp: string;
171  recordedAt: string;
172}
173
174export interface ActivityPage {
175  activity: Activity[];
176  nextAfter: number;
177}
178export interface ProjectOption {
179  id: string;
180  label: string;
181}
182export interface Brand {
183  name: string;
184  tagline: string;
185}
186
187/** An HTTP implementation today; a host may supply its Cap'n Web ui capability later. */
188export interface DashboardTransport {
189  /** Local connection state, separate from the Core wire snapshot. */
190  live?: {
191    subscribe(listener: (paused: boolean) => void): () => void;
192    reconnect(): Promise<void>;
193  };
194  getReview?(project: string, review: string): Promise<ReviewDetail>;
195  getDiff?(project: string, review: string, path: string): Promise<ReviewDiff>;
196  returnReview?(
197    project: string,
198    review: string,
199    reason: string,
200  ): Promise<{ review: StateResponse["reviews"][number] }>;
201  archivePacket?(project: string, packet: string): Promise<{ packet: Packet }>;
202  unarchivePacket?(project: string, packet: string): Promise<{ packet: Packet }>;
203  resolveBlocker?(project: string, blocker: string, reason: string): Promise<{ blocker: WireBlocker }>;
204  getState(project: string): Promise<ProjectState>;
205  getActivity(project: string, after?: number): Promise<ActivityPage>;
206  approve(project: string, review: string): Promise<unknown>;
207  promote(project: string, review: string): Promise<{ sha: string }>;
208  /** Optional because the read-only agent host has no owner capabilities. */
209  getReceipt?(project: string, review: string): Promise<OwnerReceipt>;
210  setSettings?(
211    project: string,
212    settings: { requireReceipt: boolean },
213  ): Promise<{ settings: { requireReceipt: boolean } }>;
214  /** Optional invalidations only. The authoritative snapshot still comes from getState. */
215  subscribe?(project: string, invalidate: () => void): () => void;
216}
217
218/** FH-005 JSON snapshot. Only live claims appear; blockers retain resolved history. */
219export interface StateResponse {
220  project: {
221    id: string;
222    namespace: string;
223    repoName: string;
224    remote: string;
225    defaultBranch: string;
226    events?: "pending" | "active";
227    settings?: { requireReceipt: boolean };
228  };
229  agents: { name: string; createdAt?: string | null }[];
230  forks: {
231    agent: string;
232    repoName: string;
233    head: string;
234    remote: string;
235    createdAt: string;
236    events?: "pending" | "active";
237  }[];
238  packets: ProjectState["packets"];
239  claims: { packet: string; agent: string; repoName: string; paths: string[]; createdAt: string; usage?: UsageSources }[];
240  blockers: WireBlocker[];
241  races?: Race[];
242  reviews: {
243    approvedAt?: string | null;
244    usageAtReview?: UsageSources;
245    id: string;
246    packet: string;
247    agent: string;
248    repoName: string;
249    sha: string;
250    base: string;
251    receiptDigest: string | null;
252    receipt?: ReceiptSummary | null;
253    returned?: null | { reason: string; at: string };
254    approval: { sha: string; receiptDigest: string | null } | null;
255    stale: boolean;
256    promoted: boolean;
257    createdAt: string;
258  }[];
259  ledger: LedgerEntry[];
260  activity: Activity[];
261  reconcilerAlerts: {
262    id: string;
263    repoName: string;
264    ref: string;
265    before: string;
266    after: string;
267    reason: string;
268  }[];
269  promotionPending: LedgerEntry | null;
270}
271export interface LedgerEntry {
272  id: string;
273  review: string;
274  repoName: string;
275  ref: string;
276  before: string;
277  after: string;
278  receiptDigest: string | null;
279  recordedAt: string;
280  race?: string;
281}
282
283/** FH-039 fixed bounded detail contract. Public reads never contain logs or authentication credentials. */
284export interface ReviewDetail {
285  review: StateResponse["reviews"][number];
286  packet: ProjectState["packets"][number];
287  changes: {
288    base: string;
289    head: string;
290    fileCount: number;
291    truncated: boolean;
292    files: { path: string; status: "added" | "modified" | "deleted" }[];
293  };
294  commits: {
295    sha: string;
296    subject: string;
297    authorName: string;
298    packetTrailer: string | null;
299    agentTrailer: string | null;
300  }[];
301  commitsTruncated: boolean;
302  promotion: null | {
303    before: string;
304    after: string;
305    recordedAt: string;
306    receiptDigest: string | null;
307  };
308  blockers: StateResponse["blockers"];
309}
310export interface ReviewDiff {
311  path: string;
312  status: "added" | "modified" | "deleted";
313  binary: boolean;
314  patch: string;
315  truncated: boolean;
316}
317
318/** Optional FH-052/053 read projection; absent evidence is never synthesized. */
319export interface RaceEntrant {
320  usage?: UsageSources;
321  budget?: TokenBudgetState;
322  agent: string;
323  repoName: string;
324  claimedAt: string;
325  firstPushAt: string | null;
326  reviewRequestedAt: string | null;
327  receiptPassedAt: string | null;
328  approvedAt: string | null;
329  promotedAt: string | null;
330  place: number | null;
331  closedAt: string | null;
332  closedBy?: "release" | "owner" | "race-finished" | null;
333  promoteRejected?: { at: string; expected: string; found: string } | null;
334}
335/** FH-063 fixed public read seam; absent fields preserve races created before judges. */
336export interface RaceAdvice { ranking: string[]; rationale: string; at: string; }
337export interface Race {
338  judge?: string | null;
339  verdict?: RaceAdvice | null;
340  packet: string;
341  status: "open" | "finished";
342  tokenBudget?: number | null;
343  entrants: RaceEntrant[];
344  finishedAt?: string | null;
345  winner?: string | null;
346  /** Exact string reference to ledger[].id; never an array position. */
347  ledgerEntryId?: string | null;
348  places?: { agent: string; place: number }[];
349}
350