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

View File

@@ -78,7 +78,8 @@ const { values, positionals } = parseArgs({
const usage = `Usage: bun run smoke:zopu [--run] [--report <path>] [--timeout-ms <ms>]
Without --run, the command only probes the local web/Convex/Flue/AgentOS and CPA contracts.
Without --run, the command probes the control-plane (web/Convex/Flue/AgentOS/CPA)
and the Orb execution-plane (Docker/OpenCode/Gitea/repository) contracts.
With --run, it creates one issue in the explicit smoke project, drives the worker, and sends the result to the GLM planner/reviewer.
Required environment:
@@ -93,6 +94,13 @@ Optional environment:
ZOPU_SMOKE_FEATURE_REQUEST override the tiny feature request
ZOPU_SMOKE_WEB_URL default: SITE_URL or http://localhost:5173
ZOPU_SMOKE_FLUE_URL default: VITE_FLUE_URL or http://localhost:3583
Orb execution-plane environment (probed and reported as BLOCKED when absent):
GITEA_URL self-hosted Gitea base URL
GITEA_TOKEN Gitea API token for PR creation
ZOPU_SMOKE_REPOSITORY_PATH owner/repo slug for the PR-creation probe
ORB_DOCKER_IMAGE optional Docker image override
ORB_DOCKER_WORKSPACE optional Orb workspace root (default /tmp/orb-*)
`;
if (values.help || positionals.length > 0) {
@@ -580,6 +588,243 @@ const probeGateway = async (input: {
}
};
// ---------------------------------------------------------------------------
// Orb-path preflight probes — Docker, OpenCode, Git/Gitea, repository writable
//
// These probes check the Orb execution-plane dependencies that the
// OrbProjectManager relies on. They are intentionally separate from the
// control-plane/gateway probes: a missing Docker daemon or Gitea token
// reports as a BLOCKED preflight item, never a fake success.
// ---------------------------------------------------------------------------
const probeDocker = async (checks: SmokeCapabilityCheck[]): Promise<void> => {
try {
const proc = Bun.spawn(
["docker", "version", "--format", "{{.Server.Version}}"],
{ stderr: "pipe", stdout: "pipe" }
);
const stdout = await new Response(proc.stdout).text();
const exitCode = await proc.exited;
const version = stdout.trim();
if (exitCode === 0 && version.length > 0) {
addCheck(
checks,
"docker-daemon",
true,
`Docker server version ${version}`
);
} else {
addCheck(
checks,
"docker-daemon",
false,
"Docker daemon is not running; start Docker Desktop or dockerd"
);
}
} catch (error) {
addCheck(
checks,
"docker-daemon",
false,
`Docker CLI unavailable: ${errorMessage(error, [])}`
);
}
};
const probeOpenCodePackage = async (
checks: SmokeCapabilityCheck[]
): Promise<void> => {
try {
await import("@agentos-software/opencode");
addCheck(
checks,
"opencode-package",
true,
"@agentos-software/opencode is resolvable"
);
} catch {
addCheck(
checks,
"opencode-package",
false,
"@agentos-software/opencode is not installed; Orb AgentOS VM cannot link OpenCode"
);
}
};
const probeGitea = async (input: {
readonly checks: SmokeCapabilityCheck[];
readonly giteaToken: string | undefined;
readonly giteaUrl: string | undefined;
readonly repositoryPath: string | undefined;
readonly secrets: readonly string[];
readonly timeoutMs: number;
}): Promise<void> => {
const { checks, giteaToken, giteaUrl, repositoryPath, secrets, timeoutMs } =
input;
if (giteaUrl === undefined) {
addCheck(
checks,
"gitea-reachable",
false,
"GITEA_URL is not set; set it to the self-hosted Gitea base URL for the Orb Git lifecycle"
);
} else {
try {
const response = await fetch(
`${giteaUrl.replace(/\/$/u, "")}/api/v1/version`,
{
headers: giteaToken ? { Authorization: `token ${giteaToken}` } : {},
signal: withTimeout(timeoutMs),
}
);
addCheck(
checks,
"gitea-reachable",
response.ok,
response.ok
? "Gitea API version endpoint is reachable"
: `Gitea API returned HTTP ${response.status}`
);
} catch (error) {
addCheck(
checks,
"gitea-reachable",
false,
`Gitea API is unreachable: ${errorMessage(error, secrets)}`
);
}
}
if (giteaUrl === undefined || giteaToken === undefined) {
addCheck(
checks,
"gitea-creds-valid",
false,
giteaToken === undefined
? "GITEA_TOKEN is not set; the Orb Git lifecycle needs it to create pull requests"
: "Gitea URL is not configured; cannot validate token"
);
} else {
try {
const response = await fetch(
`${giteaUrl.replace(/\/$/u, "")}/api/v1/user`,
{
headers: { Authorization: `token ${giteaToken}` },
signal: withTimeout(timeoutMs),
}
);
addCheck(
checks,
"gitea-creds-valid",
response.ok,
response.ok
? "Gitea token is valid"
: `Gitea token returned HTTP ${response.status}`
);
} catch (error) {
addCheck(
checks,
"gitea-creds-valid",
false,
`Gitea credential probe failed: ${errorMessage(error, secrets)}`
);
}
}
if (
repositoryPath === undefined ||
giteaToken === undefined ||
giteaUrl === undefined
) {
addCheck(
checks,
"gitea-pr-creation",
false,
repositoryPath === undefined
? "ZOPU_SMOKE_REPOSITORY_PATH (owner/repo) is not set; cannot probe PR creation"
: "Gitea URL or token is missing; cannot probe PR creation"
);
} else {
try {
const path = repositoryPath
.split("/")
.map((segment) => encodeURIComponent(segment))
.join("/");
const response = await fetch(
`${giteaUrl.replace(/\/$/u, "")}/api/v1/repos/${path}`,
{
headers: { Authorization: `token ${giteaToken}` },
signal: withTimeout(timeoutMs),
}
);
addCheck(
checks,
"gitea-pr-creation",
response.ok,
response.ok
? `Repository ${repositoryPath} is accessible for PR creation`
: `Repository ${repositoryPath} returned HTTP ${response.status}`
);
} catch (error) {
addCheck(
checks,
"gitea-pr-creation",
false,
`Gitea repository probe failed: ${errorMessage(error, secrets)}`
);
}
}
};
const probeRepositoryWritable = async (
checks: SmokeCapabilityCheck[]
): Promise<void> => {
const { mkdir, rm, writeFile } = await import("node:fs/promises");
const workspace =
process.env.ORB_DOCKER_WORKSPACE ?? "/tmp/orb-smoke-workspace-probe";
try {
await mkdir(workspace, { recursive: true });
const probe = `${workspace}/.orb-write-probe`;
await writeFile(probe, "ok");
await rm(probe);
addCheck(
checks,
"repository-writable",
true,
`Orb workspace root ${workspace} is writable`
);
} catch (error) {
addCheck(
checks,
"repository-writable",
false,
`Orb workspace root is not writable: ${errorMessage(error, [])}`
);
}
};
const probeOrbCapabilities = async (input: {
readonly checks: SmokeCapabilityCheck[];
readonly env: SmokeEnv;
readonly secrets: readonly string[];
readonly timeoutMs: number;
}): Promise<void> => {
const { checks, env, secrets, timeoutMs } = input;
await probeDocker(checks);
await probeOpenCodePackage(checks);
await probeGitea({
checks,
giteaToken: env.giteaToken,
giteaUrl: env.giteaUrl,
repositoryPath: env.repositoryPath,
secrets,
timeoutMs,
});
await probeRepositoryWritable(checks);
};
const runSmoke = async (input: {
readonly baseReport: Omit<SmokeReport, "status">;
readonly convex: ConvexHttpClient;
@@ -772,6 +1017,12 @@ const main = async (): Promise<number> => {
timeoutMs,
});
await probeOrbCapabilities({
checks,
env,
secrets,
timeoutMs,
});
const initialLoop = initialSmokeLoopState();
let loop: SmokeLoopState;
try {