Split contracts.ts by domain: plan / graph / chat / jobs

Purely mechanical: src/core/contracts/{plan,graph,chat,jobs}.ts carry the
sections verbatim (graph = nodes + ledgers + extraction findings; jobs =
the async ack-and-steer protocol + tool results + demo bridge), and
contracts.ts becomes a re-exporting barrel so every existing import path
(@/src/core and ./contracts.ts) is unchanged.

Wave-2 lane ownership follows the split: the graph stream owns
contracts/graph.ts, the teacher stream owns contracts/jobs.ts — same-file
contract collisions between streams are structurally impossible now;
cross-domain schema changes go through master.
This commit is contained in:
amosblomqvist
2026-09-05 21:20:38 +03:00
parent d8324ca9bb
commit 8a9497bf59
5 changed files with 550 additions and 524 deletions
+61
View File
@@ -0,0 +1,61 @@
/**
* Plan-tree contracts — the session-scoped dependency tree
*
* Part of the domain-split contracts (src/core/contracts/): each domain file
* is owned outright by its stream — cross-domain schema changes go through
* master. Pure types + protocol docs + tiny pure helpers; type-only imports
* of "ai" (safe everywhere: server, client, tests). See docs/vision.md.
*/
// ─── Plan tree (vision §2 "formal plan tree") ────────────────────────────────
// A session-scoped dependency tree authored by the teacher via plan tools.
// One substrate with the global graph: tree nodes fold into graph nodes.
export interface PlanNode {
/** Opaque, agent-chosen slug, stable across edits: "chain-rule-intuition". */
id: string;
/** Short label for the tree panel. */
title: string;
/** 1-2 sentences: what this node covers. */
summary?: string;
}
export interface PlanEdge {
/** Prerequisite node id. */
from: string;
/** Node that depends on `from`. */
to: string;
}
export interface PlanTree {
nodes: PlanNode[];
edges: PlanEdge[];
currentNodeId: string | null;
doneNodeIds: string[];
/** Increments on every mutation — lets the UI animate/react to changes. */
version: number;
}
/** Derived, never stored: status of a node for the panel (vision §2). */
export type PlanNodeStatus = "done" | "current" | "upcoming";
export function planNodeStatus(tree: PlanTree, nodeId: string): PlanNodeStatus {
if (tree.currentNodeId === nodeId) return "current";
if (tree.doneNodeIds.includes(nodeId)) return "done";
return "upcoming";
}
/**
* Plan tools (W1 implements; server-side WITH execute, mutate the chat's
* stored tree, and return the full updated tree so the panel can render the
* latest state straight from the stream). Every tool returns PlanToolResult.
* Tool names: plan_init, plan_add_node, plan_update_node, plan_remove_node,
* plan_add_edge, plan_remove_edge, plan_set_current, plan_mark_done.
* The tree panel derives its display from the LATEST plan tool output in the
* transcript (live during streaming) — no separate state channel.
*/
export interface PlanToolResult {
ok: boolean;
error?: string;
tree: PlanTree;
}