feat(dogfood): integrate and document complete v0 loop

Extend the smoke harness with Orb execution-plane preflight probes
(Docker, OpenCode, Gitea reachability/creds/PR-creation, repository
writable) that report BLOCKED with exact reasons when dependencies are
absent. Add scripts/orb-project-run.ts as the missing connection point
between OrbProjectManager and the Orb/Git lifecycle. Write
docs/DOGFOOD_V0.md covering architecture, startup order, env vars,
local/server flows, demo procedure, health checks, failure recovery,
cleanup, known limitations, and next milestones.

The smoke harness now runs 18 preflight checks across control-plane and
Orb execution-plane layers, all with stable markers and sanitized JSON
reports. The orb:run script (ORB_RUN=1) drives one issue through the
full Orb project-manager lifecycle.
This commit is contained in:
-Puter
2026-07-25 02:11:35 +05:30
parent 360f0d4829
commit a11ed988fb
6 changed files with 954 additions and 4 deletions

268
scripts/orb-project-run.ts Normal file
View File

@@ -0,0 +1,268 @@
/**
* Orb Project Run — Integration Glue Entry Point
*
* Connects OrbProjectManager (packages/agents/src/orb/orb-project-manager.ts)
* to the Orb runtime (Docker + AgentOS + OpenCode) and the Git/Gitea lifecycle
* (packages/agents/src/orb/git-adapter.ts). This is the missing connection
* point: OrbProjectManager was fully implemented and live-tested but had no
* production entry point — no Convex action, no script invocation.
*
* Usage:
* ORB_RUN=1 \
* ORB_GATEWAY_API_KEY=... \
* ORB_GATEWAY_BASE_URL=https://ai.example.com/v1 \
* ORB_GATEWAY_MODEL=glm-5.2 \
* ORB_GATEWAY_PROVIDER=cheaptricks \
GITEA_URL=https://git.example.com \
GITEA_TOKEN=... \
ORB_RUN_REPOSITORY_URL=https://git.example.com/owner/repo.git \
ORB_RUN_REPOSITORY_PATH=owner/repo \
bun run scripts/orb-project-run.ts
*
* Stable markers: ORB_RUN_COMPLETED (exit 0), ORB_RUN_BLOCKED (exit 2),
* ORB_RUN_FAILED (exit 1). The script never prints gateway secrets; only
* provider/model identifiers and sanitized status text.
*
* See docs/DOGFOOD_V0.md for the full lifecycle and the relationship between
* this entry point, the Flue project-manager agent, and the web start button.
*/
/* eslint-disable no-console -- CLI tool */
import { createGiteaHttpTransport } from "../packages/agents/src/git/gitea";
import type { OrbModelGatewayConfig } from "../packages/agents/src/orb/domain";
import { createGitLifecyclePort } from "../packages/agents/src/orb/git-adapter";
import { createOrbAdapter } from "../packages/agents/src/orb/orb-adapter";
import { OrbProjectManager } from "../packages/agents/src/orb/orb-project-manager";
const MARKER = {
blocked: "ORB_RUN_BLOCKED",
completed: "ORB_RUN_COMPLETED",
failed: "ORB_RUN_FAILED",
} as const;
interface RunConfig {
readonly gateway: OrbModelGatewayConfig;
readonly giteaToken: string | undefined;
readonly giteaUrl: string | undefined;
readonly issueBody: string;
readonly issueNumber: number;
readonly issueTitle: string;
readonly repositoryPath: string | undefined;
readonly repositoryUrl: string | undefined;
readonly baseBranch: string;
readonly workspaceRoot: string;
}
const envValue = (key: string): string | undefined => process.env[key];
const resolveGateway = (): OrbModelGatewayConfig | null => {
const apiKey =
envValue("ORB_GATEWAY_API_KEY") ?? envValue("AGENT_MODEL_API_KEY");
const baseUrl =
envValue("ORB_GATEWAY_BASE_URL") ?? envValue("AGENT_MODEL_BASE_URL");
const model = envValue("ORB_GATEWAY_MODEL") ?? envValue("AGENT_MODEL_NAME");
const provider =
envValue("ORB_GATEWAY_PROVIDER") ?? envValue("AGENT_MODEL_PROVIDER");
if (
!apiKey?.trim() ||
!baseUrl?.trim() ||
!model?.trim() ||
!provider?.trim()
) {
return null;
}
return { apiKey, baseUrl, model, provider };
};
const resolveConfig = (): RunConfig | null => {
const gateway = resolveGateway();
if (!gateway) {
return null;
}
const issueTitle = envValue("ORB_RUN_ISSUE_TITLE") ?? "Orb run: echo test";
const issueBody =
envValue("ORB_RUN_ISSUE_BODY") ??
"Reply with WORK_COMPLETE: orb project run succeeded";
return {
baseBranch: envValue("ORB_RUN_BASE_BRANCH") ?? "main",
gateway,
giteaToken: envValue("GITEA_TOKEN") ?? envValue("ORB_RUN_GITEA_TOKEN"),
giteaUrl: envValue("GITEA_URL") ?? envValue("ORB_RUN_GITEA_URL"),
issueBody,
issueNumber: Number(envValue("ORB_RUN_ISSUE_NUMBER") ?? "1"),
issueTitle,
repositoryPath:
envValue("ORB_RUN_REPOSITORY_PATH") ??
envValue("ZOPU_SMOKE_REPOSITORY_PATH"),
repositoryUrl: envValue("ORB_RUN_REPOSITORY_URL"),
workspaceRoot:
envValue("ORB_DOCKER_WORKSPACE") ?? `/tmp/orb-project-run-${Date.now()}`,
};
};
const runComplete = async (): Promise<number> => {
const config = resolveConfig();
if (!config) {
console.error(
"[orb-run] missing gateway config: set ORB_GATEWAY_* (or AGENT_MODEL_* fallbacks)."
);
console.log(`\n${MARKER.blocked}`);
return 2;
}
const {
baseBranch,
gateway,
giteaToken,
giteaUrl,
issueBody,
issueNumber,
issueTitle,
repositoryPath,
repositoryUrl,
workspaceRoot,
} = config;
console.log(
`[orb-run] gateway provider=${gateway.provider} model=${gateway.model}`
);
// The Orb adapter wraps the OrbRuntime (Docker + AgentOS + OpenCode).
const orbAdapter = createOrbAdapter();
const events: { type: string; text?: string }[] = [];
// The Git lifecycle adapter connects Orb sandbox commands to Gitea PR
// creation. If Gitea is not configured, the Git lifecycle is skipped but
// the Orb run still completes the model turn and repository preparation.
const createGitLifecycle = (
orb: Parameters<typeof createGitLifecyclePort>[0]
) => {
if (!giteaUrl || !giteaToken) {
throw new Error(
"GITEA_URL and GITEA_TOKEN are required for the Git publish lifecycle"
);
}
return createGitLifecyclePort(
orb,
createGiteaHttpTransport({ baseUrl: giteaUrl, token: giteaToken })
);
};
const pm = new OrbProjectManager({
createGitLifecycle,
onProjectEvent: (e) => {
events.push({ text: e.text, type: e.type });
const summary = (e.text ?? e.type).slice(0, 120);
console.log(` [event] ${e.type}: ${summary}`);
},
orbAdapter,
});
const issueId = `orb-run-${Date.now()}`;
const branchName = `work/orb-run-${issueNumber}-${Date.now()}`;
let result;
try {
result = await pm.startIssue({
baseBranch,
branchName,
context: {
artifacts: [],
contextFiles: [],
issueBody,
issueTitle,
repositoryUrl,
},
contextPack: {
artifacts: [],
contextFiles: [],
evidence: [],
issueBody,
issueNumber,
issueTitle,
repositoryMetadata: {
baseBranch,
repositoryName: repositoryPath ?? "orb-run",
repositoryUrl: repositoryUrl ?? "local",
},
},
docker: {
hostWorkspacePath: workspaceRoot,
},
gateway,
issueId,
issueNumber,
issueTitle,
projectId: "orb-run",
runId: `run-${Date.now()}`,
});
console.log(
`[orb-run] session opened: ${result.sessionId ?? "?"} (status=${result.status})`
);
// If the run completed with a WORK_COMPLETE marker, drive the Git
// lifecycle. If Gitea is not configured, the model turn + repository
// preparation still constitutes a successful Orb run.
if (result.status === "completing" || result.status === "completed") {
if (giteaUrl && giteaToken && repositoryPath) {
console.log("[orb-run] driving Git/Gitea lifecycle...");
try {
const gitResult = await pm.complete({ issueId });
console.log(
`[orb-run] Git lifecycle: ${gitResult.status} branch=${gitResult.branch}`
);
} catch (error) {
console.log(
`[orb-run] Git lifecycle failed: ${error instanceof Error ? error.message : String(error)}`
);
}
} else {
console.log(
"[orb-run] Gitea not configured; skipping Git lifecycle (model turn completed)"
);
}
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`[orb-run] failed: ${message}`);
await pm.cancel(issueId).catch((cancelError: unknown) => void cancelError);
console.log(`\n${MARKER.failed}`);
return 1;
}
await pm.cancel(issueId).catch((cancelError: unknown) => void cancelError);
const eventTypes = events.map((e) => e.type);
console.log(
`[orb-run] events: ${[...new Set(eventTypes)].join(", ")} (${events.length} total)`
);
const hasSessionOpened = eventTypes.includes("run.session_opened");
if (hasSessionOpened) {
console.log(`\n${MARKER.completed}`);
return 0;
}
console.log(`\n${MARKER.failed}`);
return 1;
};
if (envValue("ORB_RUN") !== "1") {
console.error(
"Set ORB_RUN=1 to drive one issue through the Orb project-manager (requires Docker + model gateway)."
);
console.error(MARKER.blocked);
process.exit(2);
}
(async () => {
try {
const code = await runComplete();
process.exit(code);
} catch (error) {
console.error("[orb-run] unexpected failure:", error);
console.error(MARKER.failed);
process.exit(1);
}
})();