zz_

app/demo/greenfield.ts

zzmq.dev@gmail.com/zz / app / demo / greenfield.ts @ 5e5720fb Download
Ladder Summary
  • file The module implements the “greenfield” test workflow for the zz protocol: it ensures a repository‑level lane exists (`ensureRepoLane`) and then drives a full claim → submit → merge cycle for a new increment (`submitIncrement`), committing the supplied files, recording the commit OID, and updating the repository’s ladder notes. Exported symbols are: - **`AUTHOR`** – constant describing the author identity. - **`ensureRepoLane(slug: string)`** – guarantees the repo is cloned and its `_repo` lane is created and approved. - **`IncrementInput`** – interface describing the data required to submit an increment (lane info, files, message, ladder‑building callback, optional origin). - **`submitIncrement(slug: string, input: IncrementInput)`** – performs the lane proposal, claim, changeset submission, commit creation, ladder construction, and merge request handling, returning the resulting commit OID.
// Driving real, forward-going development through the actual zz
// protocol -- scenario 1 of the greenfield test (me as the developer).
// Every increment: propose a Lane, claim it, submit a changeset (real
// file content as the ask's payload, committed via engine/changeset.ts
// with no working-tree file ever written), then merge the real Ladder
// content describing what just landed. No shortcuts around the
// protocol -- this is the same claim/submit/merge shape ladder-build.ts
// and ladder-replay-commits.ts already use, just authoring new content
// instead of describing history.
import { ensureCloned, resolveRepo } from "../cli/lib/repo.js";
import { registerLocalBackendFactory } from "../engine/backend.js";
import { createLocalBackend } from "../engine/localBackend.js";
import { ask, answer, mergeLaneLadder, proposeLane } from "../engine/lane.js";

registerLocalBackendFactory(createLocalBackend);
import { commitChangeset, type ChangesetFile } from "../engine/changeset.js";
import { readAnchor, writeLadder } from "../engine/ladderNotes.js";
import { readLane } from "../engine/store.js";
import type { Rung } from "../engine/ladder.js";
import { REPO_RUNG_PATH } from "../engine/ladder.js";
import type { LaneRef } from "../engine/types.js";

export const AUTHOR = { name: "zz-dev", email: "zz-dev@local" };
const COORDINATOR = "zz-coordinator";

export async function ensureRepoLane(slug: string) {
  const repo = resolveRepo(slug);
  ensureCloned(repo);
  let repoLane = readLane(repo.stateDir, REPO_RUNG_PATH);
  if (!repoLane) {
    repoLane = await proposeLane(repo.slug, { id: REPO_RUNG_PATH, repo: slug, name: slug, objective: `the ${slug} repository`, scope: ["**"], proposedBy: COORDINATOR, coordinators: [COORDINATOR] });
    const review = await ask(repo.slug, repoLane.id, { kind: "review_request", from: COORDINATOR });
    await answer(repo.slug, repoLane.id, review, { from: COORDINATOR, action: "grant" });
  }
  return { repo, repoLane };
}

export interface IncrementInput {
  laneId: string;
  name: string;
  objective: string;
  scope: string[];
  files: ChangesetFile[];
  message: string;
  /** Top-level rungs to land in the Ladder, built from the real commit oid (sources need the real sha). */
  buildLadderRungs: (commitOid: string) => Rung[];
  /** Overrides the default origin (this repo's own _repo Lane) -- set this to a Lane in a different repo when the work exists because of that upstream Lane, a real cross-repo dependency, not just this repo's own history. */
  origin?: LaneRef;
}

export async function submitIncrement(slug: string, input: IncrementInput): Promise<{ commitOid: string }> {
  const { repo, repoLane } = await ensureRepoLane(slug);
  const origin: LaneRef = input.origin ?? { repo: slug, lane: repoLane.id };

  const lane = await proposeLane(repo.slug, {
    id: input.laneId,
    repo: slug,
    name: input.name,
    objective: input.objective,
    scope: input.scope,
    origin,
    proposedBy: AUTHOR.name,
    coordinators: [AUTHOR.name],
  });

  const claim = await ask(repo.slug, lane.id, { kind: "claim_request", from: AUTHOR.name });
  await answer(repo.slug, lane.id, claim, { from: AUTHOR.name, action: "grant" });

  const submit = await ask(repo.slug, lane.id, { kind: "submit_changeset", from: AUTHOR.name, payload: { files: input.files, message: input.message } });
  const commitOid = await commitChangeset(repo.repoDir, { files: input.files, message: input.message, author: AUTHOR });
  await answer(repo.slug, lane.id, submit, { from: AUTHOR.name, action: "grant", payload: { commit: commitOid } });

  const rungs = input.buildLadderRungs(commitOid);
  const mergeRequest = await ask(repo.slug, lane.id, { kind: "merge_request", from: AUTHOR.name, payload: { ladder: rungs.map((r) => ({ path: r.path, kind: r.kind })) } });

  // _repo's own note doesn't exist until after the repo's first real
  // commit (it anchors to the root tree); writing it for the first
  // time means wrapping the new rungs as its children directly, the
  // same bootstrap ladder-build.ts does, rather than calling
  // appendRepoChildren on a note that doesn't exist yet.
  const repoAnchor = await readAnchor(repo.repoDir, repo.stateDir, REPO_RUNG_PATH);
  if (!repoAnchor) {
    const root: Rung = { path: REPO_RUNG_PATH, kind: "repo", summary: `${slug}, built from scratch through the zz protocol.`, sources: [], children: rungs };
    await writeLadder(repo.repoDir, repo.stateDir, root);
  } else {
    await mergeLaneLadder(repo.slug, rungs);
  }
  await answer(repo.slug, lane.id, mergeRequest, { from: AUTHOR.name, action: "grant" });

  return { commitOid };
}