app/engine/types.ts
zzmq.dev@gmail.com/zz / app / engine / types.ts
@ 671ebfb5
Download
Ladder Summary
- file This module defines the TypeScript data model that the engine uses to represent roles, lanes, asks, answers, insights, changesets, and related metadata—mirroring the canonical schema documented at zzbin.dev/data-format/. It exports a collection of `type` aliases and `interface`s (e.g., `Role`, `RoleGrant`, `AgentRoles`, `LaneStatus`, `Trigger`, `AskKind`, `AskEntry`, `AnswerEntry`, `InsightEntry`, `HistoryEntry`, `LaneRef`, `RelatedLaneEntry`, `Lane`, `Changeset`, and `Decision`). No functions or classes are exported from this file.
// Mirrors the schema documented at zzbin.dev/data-format/.
// Keep this file and that page in sync; the docs are the source of truth for shape.
export type Role = "owner" | "maintainer" | "committer" | "contributor" | "reader";
export interface RoleGrant {
role: Role;
scope: string;
granted_by: string;
at: string;
}
export interface AgentRoles {
agent: string;
grants: RoleGrant[];
}
export type LaneStatus =
| "proposed"
| "open"
| "claimed"
| "active"
| "merged"
| "closed"
| "vetoed"
| "stopped";
export interface Trigger {
kind: "manual";
proposed_by: string;
reason?: string;
}
export type AskKind =
| "claim_request"
// Gives up a claim held via an earlier claim_request, without
// answering the claimed ask itself -- it goes back to being
// claimable by anyone (including the same actor again later), unlike
// "deny" (terminal) or just walking away (claim stays held forever,
// invisible to claimableAsks). See engine/workqueue.ts's claimHolder,
// which treats the latest granted claim_request/claim_release event
// for a given target ask as authoritative.
| "claim_release"
| "review_request"
| "change_request"
| "coordinator_request"
| "merge_request"
| "rung_update_request"
| "ingest_repo"
| "analyze_changeset"
| "add_filechange"
| "summarize_changeset"
// A generic task with no dedicated handler: payload.description is
// prose for an agent to read like a prompt and figure out on its own,
// rather than a kind-specific typed payload.
| "work"
// Proposes a real code change, with the full content of every
// changed file as payload -- granting it is what actually commits
// it (engine/changeset.ts's commitChangeset), the same pattern as
// merge_request granting a Ladder write. No file ever needs to exist
// on disk before this: the content lives entirely in the payload.
| "submit_changeset"
// A Maintainer's review of a proposed changeset, before it may be
// merged -- is this what was actually asked for, does it stay in
// scope, does it do anything that wasn't requested. Separate from
// submit_changeset's own (coordinator-scope) grant, which is purely
// mechanical: 3-way merge, commit, Ladder writes. submit_changeset
// depends_on this, so it can't even be claimed before a Maintainer
// has granted it (see engine/lane.ts's ASK_SCOPE and the backstop
// check in answer()).
| "changeset_review"
// Posted when a submit_changeset is denied specifically because
// another change landed first and the proposal, based on a now-stale
// snapshot, would have dropped it -- a real concurrency conflict
// between two agents' work, not a bug in either one. Payload carries
// the original proposal (files/message/summary) and the conflict
// reason, so whoever claims this reconciles the two instead of
// starting over from the task description alone.
| "resolve_conflict";
export interface AskEntry {
id: string;
type: "ASK";
kind: AskKind;
from: string;
/** Kind-specific data: what's actually being asked for (a commit sha, a target ask id to claim, a Rung to merge, etc). */
payload?: unknown;
/** Ask ids (same lane) that must each have a "grant"/"complete" Answer before this one may be claimed. */
depends_on?: string[];
/** Higher = more urgent. A query-time sort hint (see engine/workqueue.ts), not a scheduling guarantee. */
priority?: number;
at: string;
}
export interface AnswerEntry {
id: string;
type: "ANSWER";
/** The AskEntry.id this answers. */
ask: string;
from: string;
action: "grant" | "deny" | "complete";
/** Kind-specific data: a grant id for a claim, a built Rung for a work item, etc. */
payload?: unknown;
at: string;
}
export interface InsightEntry {
type: "INSIGHT";
kind: "norm" | "trap";
text: string;
from: string;
at: string;
}
export type HistoryEntry = AskEntry | AnswerEntry | InsightEntry;
export interface LaneRef {
repo: string;
lane: string;
}
export interface RelatedLaneEntry extends LaneRef {
origin: LaneRef;
status: LaneStatus;
}
export interface Lane {
id: string;
repo: string;
origin: LaneRef | null;
name: string;
objective: string;
status: LaneStatus;
scope: string[];
trigger: Trigger;
coordinators: string[];
// Who may grant a changeset_review -- the Maintainer role from
// site/content/governance/index.md ("Reviews and decides;
// accountable for what merges"), distinct from coordinators (which
// governs the mechanical merge step: commit_changeset,
// ladder_write_rung). Defaults to coordinators when not set
// explicitly, so existing fully-autonomous flows keep working;
// narrow it (e.g. to a single human actor id) to require real human
// sign-off.
maintainers: string[];
budget?: { cap_usd: number; spent_usd: number };
history: HistoryEntry[];
related_lanes: RelatedLaneEntry[];
}
export interface Changeset {
id: string;
lane: string;
read_set: string[];
write_set: string[];
reviews: { agent: string; verdict: "approve" | "request_changes"; at: string }[];
merge_grant: {
issued_to: string;
issued_by: string;
single_use: boolean;
consumed_at: string | null;
} | null;
}
export interface Decision {
commit: string;
lane: string;
changeset: string;
merged_via: "occ_fast_path" | "human_review";
checks: string[];
}