Reshape harness flows like fork maintenance
Some checks failed
check / check (push) Failing after 26s

This commit is contained in:
matamune 2026-05-18 22:34:53 +00:00
parent 08404f1f0b
commit 8b1e31df1a
Signed by: matamune
GPG key ID: 3BB8E7D3B968A324
26 changed files with 1089 additions and 469 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ coverage
/.codex/workspace/local/
/.codex/workspace/actions/
/.codex/pack-backups/
/.codex/flow-artifacts/
data/
apps/*/data/
docs/out/

View file

@ -240,7 +240,7 @@ async function handleRun(positionals: string[], context: CliContext): Promise<nu
}
if (target === "harness") {
const eventFile = flagValue(context.parsed, "event") ??
path.join(context.workspaceRoot, "flows/patch-moi-harness/fixtures/upstream-release-v0.1.3.json");
path.join(context.workspaceRoot, "flows/patch-moi-harness-fork/fixtures/upstream-release-v0.1.3.json");
const event = await readFlowEvent(eventFile, context.workspaceRoot);
return await runEvent(event, context);
}
@ -732,7 +732,7 @@ function findWorkspaceRoot(cwd: string): string {
while (true) {
if (
existsSync(path.join(current, ".codex/workspace.toml")) ||
existsSync(path.join(current, "flows/patch-moi-harness/flow.toml"))
existsSync(path.join(current, "flows/patch-moi-harness-fork/flow.toml"))
) {
return current;
}

View file

@ -10,25 +10,43 @@ import {
const workspaceRoot = path.resolve(import.meta.dir, "../../..");
const fixturePath = path.resolve(
workspaceRoot,
process.argv[2] ?? "flows/patch-moi-harness/fixtures/upstream-release-v0.1.3.json",
process.argv[2] ?? "flows/patch-moi-harness-fork/fixtures/upstream-release-v0.1.3.json",
);
const event = JSON.parse(await readFile(fixturePath, "utf8")) as FlowEvent<Record<string, unknown>>;
const flows = await discoverFlows({ cwd: workspaceRoot });
const matches = await matchingSteps(flows, event);
const match = matches.find((entry) => entry.flow.manifest.name === "patch-moi-harness");
const harnessMatches = matches.filter((entry) => entry.flow.manifest.name.startsWith("patch-moi-harness-"));
if (!match) {
throw new Error(`No patch-moi-harness flow step matched ${event.type} from ${fixturePath}`);
if (harnessMatches.length === 0) {
throw new Error(`No patch-moi-harness-* flow steps matched ${event.type} from ${fixturePath}`);
}
const result = await runFlowStep({
flow: match.flow,
step: match.step,
const results = [];
for (const match of harnessMatches) {
results.push({
flow: match.flow.manifest.name,
step: match.step.name,
result: await runFlowStep({
flow: match.flow,
step: match.step,
event,
}),
});
}
console.log(JSON.stringify({
status: aggregateStatus(results.map((entry) => entry.result.status)),
event,
});
results,
}, null, 2));
console.log(JSON.stringify(result, null, 2));
if (["blocked", "failed", "needs_intervention"].includes(result.status)) {
if (results.some((entry) => ["blocked", "failed", "needs_intervention"].includes(entry.result.status))) {
process.exit(1);
}
function aggregateStatus(statuses: string[]): string {
for (const status of ["failed", "blocked", "needs_intervention", "changed"]) {
if (statuses.includes(status)) return status;
}
return statuses.includes("completed") ? "completed" : "skipped";
}

View file

@ -1,4 +1,4 @@
import { readFile } from "node:fs/promises";
import { access, readdir, readFile } from "node:fs/promises";
import path from "node:path";
import { describe, expect, test } from "bun:test";
import {
@ -12,24 +12,50 @@ const workspaceRoot = path.resolve(import.meta.dir, "../../..");
const harnessFork = path.join(workspaceRoot, "harness/fork");
describe("patch.moi harness flow", () => {
test("matches the release fixture and verifies the current fork", async () => {
test("matches the release fixture like the Codex fork release fanout", async () => {
const event = JSON.parse(await readFile(
path.join(workspaceRoot, "flows/patch-moi-harness/fixtures/upstream-release-v0.1.3.json"),
path.join(workspaceRoot, "flows/patch-moi-harness-fork/fixtures/upstream-release-v0.1.3.json"),
"utf8",
)) as FlowEvent<Record<string, unknown>>;
const flows = await discoverFlows({ cwd: workspaceRoot });
const matches = await matchingSteps(flows, event);
const match = matches.find((entry) => entry.flow.manifest.name === "patch-moi-harness");
const harnessMatches = matches
.filter((entry) => entry.flow.manifest.name.startsWith("patch-moi-harness-"))
.map(({ flow, step }) => `${flow.manifest.name}/${step.name}`);
expect(match).toBeDefined();
if (!match) {
return;
}
expect(harnessMatches).toEqual([
"patch-moi-harness-bindings/generate-bindings",
"patch-moi-harness-fork/release-cycle",
]);
});
test("release fixture regenerates bindings and rebuilds the patch-branch fork", async () => {
const event = JSON.parse(await readFile(
path.join(workspaceRoot, "flows/patch-moi-harness-fork/fixtures/upstream-release-v0.1.3.json"),
"utf8",
)) as FlowEvent<Record<string, unknown>>;
const flows = await discoverFlows({ cwd: workspaceRoot });
const matches = await matchingSteps(flows, event);
const bindingMatch = matches.find((entry) => entry.flow.manifest.name === "patch-moi-harness-bindings");
const forkMatch = matches.find((entry) => entry.flow.manifest.name === "patch-moi-harness-fork");
expect(bindingMatch).toBeDefined();
expect(forkMatch).toBeDefined();
if (!bindingMatch || !forkMatch) return;
const beforeHead = await git(["rev-parse", "HEAD"]);
const result = await runFlowStep({
flow: match.flow,
step: match.step,
const bindings = await runFlowStep({
flow: bindingMatch.flow,
step: bindingMatch.step,
event,
env: {
CODEX_FLOW_FETCH: "0",
CODEX_FLOW_PUSH: "0",
},
});
const fork = await runFlowStep({
flow: forkMatch.flow,
step: forkMatch.step,
event,
env: {
CODEX_FLOW_FETCH: "0",
@ -38,9 +64,18 @@ describe("patch.moi harness flow", () => {
});
const afterHead = await git(["rev-parse", "HEAD"]);
expect(result.status).toBe("completed");
expect(result.message).toContain("package checks passed");
expect(result.artifacts?.candidateRefs).toMatchObject([
expect(["changed", "completed"]).toContain(bindings.status);
expect(typeof bindings.artifacts?.artifactPath).toBe("string");
await access(String(bindings.artifacts?.artifactPath));
expect(fork.status).toBe("completed");
expect(fork.message).toContain("Harness fork already matches");
expect(fork.artifacts?.applied).toMatchObject([
{ name: "patch/010-maintained-greeting" },
{ name: "patch/020-shout-mode" },
{ name: "patch/030-package-identity" },
]);
expect(fork.artifacts?.candidateRefs).toMatchObject([
{
kind: "branch",
repo: "matamune-peezy/patch-moi-harness",
@ -54,6 +89,72 @@ describe("patch.moi harness flow", () => {
expect(await git(["status", "--porcelain=v1"])).toBe("");
});
test("matches and runs the harness main branch update path", async () => {
const event = JSON.parse(await readFile(
path.join(workspaceRoot, "flows/patch-moi-harness-fork/fixtures/upstream-main-v0.1.3.json"),
"utf8",
)) as FlowEvent<Record<string, unknown>>;
const flows = await discoverFlows({ cwd: workspaceRoot });
const matches = await matchingSteps(flows, event);
expect(matches.map(({ flow, step }) => `${flow.manifest.name}/${step.name}`)).toContain(
"patch-moi-harness-fork/main-branch-update",
);
const match = matches.find((entry) => entry.flow.manifest.name === "patch-moi-harness-fork");
if (!match) return;
const beforeHead = await git(["rev-parse", "HEAD"]);
const result = await runFlowStep({
flow: match.flow,
step: match.step,
event,
env: {
CODEX_FLOW_FETCH: "0",
CODEX_FLOW_PUSH: "0",
},
});
expect(result.status).toBe("completed");
expect(result.artifacts?.upstreamBranch).toBe("upstream");
expect(await git(["rev-parse", "HEAD"])).toBe(beforeHead);
expect(await git(["status", "--porcelain=v1"])).toBe("");
});
test("matches and runs the downstream harness fork package artifact path", async () => {
const event = JSON.parse(await readFile(
path.join(workspaceRoot, "flows/patch-moi-harness-flows-fork/fixtures/downstream-fork-release-v0.1.3-fork.0.json"),
"utf8",
)) as FlowEvent<Record<string, unknown>>;
const flows = await discoverFlows({ cwd: workspaceRoot });
const matches = await matchingSteps(flows, event);
expect(matches.map(({ flow, step }) => `${flow.manifest.name}/${step.name}`)).toContain(
"patch-moi-harness-flows-fork/release-fork",
);
const match = matches.find((entry) => entry.flow.manifest.name === "patch-moi-harness-flows-fork");
if (!match) return;
const result = await runFlowStep({
flow: match.flow,
step: match.step,
event,
env: {
CODEX_FLOW_FETCH: "0",
CODEX_FLOW_PUSH: "0",
CODEX_FLOW_PUBLISH: "0",
},
});
const tarballs = await readdir(path.join(workspaceRoot, ".codex/flow-artifacts/patch-moi-harness-flows-fork-release"));
expect(result.status).toBe("changed");
expect(result.artifacts?.candidateRefs).toMatchObject([
{
kind: "artifact",
repo: "matamune-peezy/patch-moi-harness",
pushed: false,
},
]);
expect(tarballs.some((entry) => entry.endsWith(".tgz"))).toBe(true);
});
test("matches installed Codex release flows without executing release work", async () => {
const flows = await discoverFlows({ cwd: workspaceRoot });
const matches = await matchingSteps(flows, {

View file

@ -44,7 +44,7 @@ The current service has these pieces:
| feed poller | reads configured upstream feeds and emits normalized signals |
| JSONL store | writes feed events, flow events, workspace dispatches, and maintenance attempts under `DATA_DIR` |
| workspace backend adapter | dispatches locally when no backend URL is set, or calls a configured Codex workspace backend |
| harness flow | exercises real fork maintenance through `flows/patch-moi-harness` |
| harness flows | exercise Codex-shaped fork maintenance through `flows/patch-moi-harness-*` |
| repo workspace config | exposes manual operator tasks through `codex-flows workspace doctor|tick|run` |
Those pieces are intentionally narrow. The service coordinates and records; the

View file

@ -112,7 +112,8 @@ DATA_DIR=./data FEED_SOURCES_PATH=./feed-sources.json bun run --filter @peezy.te
- `apps/patch`: Patch service, feed poller, JSONL store, admin API, Discord
output, and workspace backend adapter.
- `flows/patch-moi-harness`: executable maintenance flow for the harness repos.
- `flows/patch-moi-harness-*`: source harness flows that mirror the Codex
fork release, main-update, and downstream-release surfaces.
- `.codex/flows`: installed external flow capabilities, currently the Codex
release maintenance flows from the neighboring `../codex-flows` pack.
- `harness`: upstream and maintained fork repositories used for rehearsal.

View file

@ -6,8 +6,14 @@ description: Use the patch.moi harness repos to rehearse an upstream release and
# Run the harness maintenance flow
This tutorial runs the smallest real patch.moi maintenance loop. The upstream
repo is `harness/upstream`. The maintained fork is `harness/fork`. The flow
package is `flows/patch-moi-harness`.
repo is `harness/upstream`. The maintained fork is `harness/fork`. The source
flow packages mirror the Codex fork structure:
- `flows/patch-moi-harness-bindings` handles upstream release metadata.
- `flows/patch-moi-harness-fork` rebuilds the maintained fork from
`upstream` plus ordered `patch/*` branches.
- `flows/patch-moi-harness-flows-fork` prepares a downstream fork package
artifact.
There are two local operator paths:
@ -39,9 +45,11 @@ bun run harness:flow
```
The fixture event is `v0.1.3`, which the current fork already contains. The
flow should skip rebase work, run `npm test` and `npm run pack:dry-run` in the
fork, report `candidateRefs` for the maintained fork branch, and leave the fork
checkout unchanged.
release event fans out to the bindings flow and the fork flow. The fork flow
seeds the local `upstream` and `patch/*` branches when needed, verifies that
`main` already equals `upstream + patches`, runs `npm test` and
`npm run pack:dry-run`, reports `candidateRefs` for the maintained fork branch,
and leaves the fork checkout unchanged.
## 3. Run the fixture through workspace autonomy
@ -107,9 +115,10 @@ bun run harness:flow <event-file>
```
Use an event file whose `id`, `occurredAt`, `receivedAt`, and `payload.tag`
identify the new upstream tag. The flow rebases `harness/fork` onto that tag,
verifies the fork package, reports the local candidate branch, and keeps pushes
off.
identify the new upstream tag. The fork flow updates the local `upstream`
branch to that tag, rebuilds `main` by cherry-picking ordered `patch/*` branch
tips, verifies the fork package, reports the local candidate branch, and keeps
pushes off.
## 6. Push only after review
@ -119,7 +128,7 @@ When the local result is the maintained fork state you want:
CODEX_FLOW_PUSH=1 bun run harness:flow <event-file>
```
That pushes the rebased fork branch to the configured `origin` and `jojo`
That pushes the rebuilt fork branch to the configured `origin` and `jojo`
remotes with `--force-with-lease` and reports those pushed branch refs as
candidate refs. Public npm publishing remains a separate trusted-publishing
release path.

View file

@ -0,0 +1,124 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
type FlowContext = {
flow: {
config?: Record<string, unknown>;
event: {
id: string;
type: string;
payload?: Record<string, unknown>;
};
};
};
type CommandResult = {
code: number;
stdout: string;
stderr: string;
};
const context = JSON.parse(await Bun.stdin.text()) as FlowContext;
const config = context.flow.config ?? {};
const payload = context.flow.event.payload ?? {};
function finish(value: Record<string, unknown>): never {
process.stdout.write(`FLOW_RESULT ${JSON.stringify(value)}\n`);
process.exit(0);
}
try {
const expectedRepo = stringConfig("expected_repo", "peezy-tech/patch-moi-harness");
const repo = stringValue(payload.repo);
const tag = stringValue(payload.tag) || shortTag(stringValue(payload.ref) ?? "");
if (repo !== expectedRepo) {
finish({ status: "skipped", message: `Harness bindings ignore ${repo}.` });
}
if (!tag) {
finish({ status: "failed", message: "upstream.release requires payload.tag." });
}
const workspaceRoot = process.cwd();
const upstreamRepo = path.resolve(workspaceRoot, stringConfig("upstream_repo", "harness/upstream"));
const artifactDir = path.resolve(workspaceRoot, stringConfig("artifact_dir", ".codex/flow-artifacts/patch-moi-harness-bindings"));
const packageJsonPath = path.join(upstreamRepo, "package.json");
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8")) as {
name?: string;
version?: string;
exports?: unknown;
bin?: unknown;
};
const releaseSha = (await runChecked(["git", "rev-parse", "--verify", `${tag}^{commit}`], upstreamRepo)).stdout.trim();
const bindings = {
generatedBy: "patch-moi-harness-bindings",
eventId: context.flow.event.id,
repo,
tag,
releaseSha,
packageName: packageJson.name,
version: packageJson.version,
exports: packageJson.exports ?? null,
bin: packageJson.bin ?? null,
};
const artifactJson = `${JSON.stringify(bindings, null, 2)}\n`;
const artifactPath = path.join(artifactDir, "bindings.json");
await mkdir(artifactDir, { recursive: true });
const previous = await readFile(artifactPath, "utf8").catch(() => "");
await writeFile(artifactPath, artifactJson, "utf8");
const changed = previous !== artifactJson;
finish({
status: changed ? "changed" : "completed",
message: changed
? `Regenerated harness bindings for ${repo}@${tag}.`
: `Harness bindings already current for ${repo}@${tag}.`,
artifacts: {
repo,
tag,
releaseSha,
artifactPath,
candidateRefs: [{
kind: "artifact",
repo: expectedRepo,
ref: artifactPath,
sha: releaseSha,
pushed: false,
}],
},
});
} catch (error) {
finish({
status: "failed",
message: error instanceof Error ? error.message : String(error),
});
}
async function runChecked(command: string[], cwd: string): Promise<CommandResult> {
const proc = Bun.spawn(command, {
cwd,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, code] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
if (code !== 0) {
throw new Error(`${command.join(" ")} failed with exit ${code}:\n${stderr || stdout}`);
}
return { code, stdout, stderr };
}
function stringConfig(name: string, fallback: string): string {
const value = config[name];
return typeof value === "string" && value.trim() ? value : fallback;
}
function stringValue(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
function shortTag(value: string): string {
return value.replace(/^refs\/tags\//, "");
}

View file

@ -4,10 +4,8 @@
"source": "patch",
"receivedAt": "2026-05-16T00:00:00.000Z",
"payload": {
"provider": "github",
"repo": "peezy-tech/patch-moi-harness",
"tag": "v0.1.3",
"url": "https://github.com/peezy-tech/patch-moi-harness/releases/tag/v0.1.3",
"publishedAt": "2026-05-16T00:00:00.000Z"
"url": "https://github.com/peezy-tech/patch-moi-harness/releases/tag/v0.1.3"
}
}

View file

@ -0,0 +1,19 @@
name = "patch-moi-harness-bindings"
version = 1
description = "Regenerate a local harness bindings artifact from upstream harness releases."
[config]
expected_repo = "peezy-tech/patch-moi-harness"
upstream_repo = "harness/upstream"
artifact_dir = ".codex/flow-artifacts/patch-moi-harness-bindings"
[[steps]]
name = "generate-bindings"
runner = "bun"
script = "exec/generate-bindings.ts"
cwd = "../.."
timeout_ms = 300000
[steps.trigger]
type = "upstream.release"
schema = "schemas/upstream-release.schema.json"

View file

@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["repo", "tag"],
"properties": {
"repo": {
"type": "string",
"enum": ["peezy-tech/patch-moi-harness"]
},
"tag": {
"type": "string",
"minLength": 1
},
"url": {
"type": "string"
}
},
"additionalProperties": true
}

View file

@ -0,0 +1,178 @@
import { existsSync } from "node:fs";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import path from "node:path";
type FlowContext = {
flow: {
config?: Record<string, unknown>;
event: {
id: string;
payload?: Record<string, unknown>;
};
};
};
type CommandResult = {
code: number;
stdout: string;
stderr: string;
};
const context = JSON.parse(await Bun.stdin.text()) as FlowContext;
const config = context.flow.config ?? {};
const payload = context.flow.event.payload ?? {};
function finish(value: Record<string, unknown>): never {
process.stdout.write(`FLOW_RESULT ${JSON.stringify(value)}\n`);
process.exit(0);
}
try {
const packageName = stringValue(payload.packageName);
const version = stringValue(payload.version);
if (!packageName || !version) {
finish({ status: "failed", message: "downstream.release requires packageName and version." });
}
const acceptedPackages = stringArrayConfig("accepted_packages", [
"@peezy.tech/patch-moi-harness",
"@peezy.tech/patch-moi-harness-fork",
]);
if (!acceptedPackages.includes(packageName)) {
finish({ status: "skipped", message: `Harness fork release ignores ${packageName}.` });
}
const workspaceRoot = process.cwd();
const forkRepo = path.resolve(workspaceRoot, stringConfig("fork_repo", "harness/fork"));
const forkRepoFullName = stringConfig("fork_repo_full_name", "matamune-peezy/patch-moi-harness");
const sourceBranch = stringConfig("source_branch", "main");
const worktreeDir = path.resolve(workspaceRoot, stringConfig("worktree_dir", ".codex/flow-artifacts/patch-moi-harness-flows-fork-worktree"));
const artifactDir = path.resolve(workspaceRoot, stringConfig("artifact_dir", ".codex/flow-artifacts/patch-moi-harness-flows-fork-release"));
await requireCleanRepo(forkRepo);
const sourceSha = (await runChecked("resolve harness fork branch", ["git", "rev-parse", "--verify", `${sourceBranch}^{commit}`], forkRepo)).stdout.trim();
await prepareWorktree(forkRepo, worktreeDir, sourceBranch);
const packageJsonPath = path.join(worktreeDir, "package.json");
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8")) as Record<string, unknown>;
const baseVersion = typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
const forkVersion = forkPackageVersion(baseVersion, version);
packageJson.version = forkVersion;
packageJson.description = `Fork release artifact prepared from ${packageName}@${version}.`;
await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");
await runChecked("install harness fork package", ["npm", "install", "--package-lock-only"], worktreeDir);
await runChecked("test harness fork package", ["npm", "test"], worktreeDir);
await rm(artifactDir, { recursive: true, force: true });
await mkdir(artifactDir, { recursive: true });
const pack = await runChecked("pack harness fork package", ["npm", "pack", "--pack-destination", artifactDir], worktreeDir);
const tarball = pack.stdout.trim().split(/\r?\n/).filter(Boolean).at(-1);
const tarballPath = tarball ? path.join(artifactDir, tarball) : undefined;
const candidateSha = (await runChecked("read harness fork release candidate", ["git", "rev-parse", "HEAD"], worktreeDir)).stdout.trim();
finish({
status: "changed",
message: `Prepared harness fork package ${forkVersion} from ${packageName}@${version}.`,
artifacts: {
eventId: context.flow.event.id,
sourcePackage: packageName,
sourceVersion: version,
forkRepo,
forkRepoFullName,
sourceBranch,
sourceSha,
baseVersion,
forkVersion,
worktreeDir,
tarballPath,
pushed: false,
published: false,
candidateRefs: [{
kind: "artifact",
repo: forkRepoFullName,
ref: tarballPath ?? artifactDir,
sha: candidateSha,
pushed: false,
}],
},
});
} catch (error) {
finish({
status: "failed",
message: error instanceof Error ? error.message : String(error),
});
}
async function requireCleanRepo(repoRoot: string): Promise<void> {
const status = await runChecked("read harness fork status", ["git", "status", "--porcelain=v1"], repoRoot);
if (status.stdout.trim()) {
finish({
status: "blocked",
message: "Harness fork checkout has local changes before release artifact preparation.",
artifacts: { status: status.stdout },
});
}
}
async function prepareWorktree(repoRoot: string, worktreeDir: string, sourceBranch: string): Promise<void> {
if (existsSync(worktreeDir)) {
await run("remove old harness fork release worktree", ["git", "worktree", "remove", "--force", worktreeDir], repoRoot);
await rm(worktreeDir, { recursive: true, force: true });
}
await run("prune harness fork worktrees", ["git", "worktree", "prune"], repoRoot);
await runChecked("create harness fork release worktree", ["git", "worktree", "add", "--detach", worktreeDir, sourceBranch], repoRoot);
}
async function runChecked(label: string, command: string[], cwd: string): Promise<CommandResult> {
const result = await run(label, command, cwd);
if (result.code !== 0) {
throw new Error(`${label} failed with exit ${result.code}:\n${result.stderr || result.stdout}`);
}
return result;
}
async function run(label: string, command: string[], cwd: string): Promise<CommandResult> {
process.stderr.write(`+ ${label}: ${command.join(" ")}\n`);
const proc = Bun.spawn(command, {
cwd,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, code] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
if (stdout) process.stderr.write(stdout);
if (stderr) process.stderr.write(stderr);
return { code, stdout, stderr };
}
function forkPackageVersion(baseVersion: string, sourceVersion: string): string {
return `${baseVersion}-harness.${sanitizePrerelease(sourceVersion)}`;
}
function sanitizePrerelease(value: string): string {
return value
.replace(/^v/, "")
.replace(/[^0-9A-Za-z]+/g, ".")
.split(".")
.filter(Boolean)
.join(".") || "0";
}
function stringConfig(name: string, fallback: string): string {
const value = config[name];
return typeof value === "string" && value.trim() ? value : fallback;
}
function stringArrayConfig(name: string, fallback: string[]): string[] {
const value = config[name];
if (!Array.isArray(value)) return fallback;
const entries = value.filter((entry): entry is string => typeof entry === "string" && entry.trim());
return entries.length > 0 ? entries : fallback;
}
function stringValue(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}

View file

@ -0,0 +1,11 @@
{
"id": "patch:downstream.release:@peezy.tech/patch-moi-harness-fork:0.1.3-fork.0",
"type": "downstream.release",
"source": "patch",
"receivedAt": "2026-05-16T00:00:00.000Z",
"payload": {
"packageName": "@peezy.tech/patch-moi-harness-fork",
"version": "0.1.3-fork.0",
"repo": "matamune-peezy/patch-moi-harness"
}
}

View file

@ -0,0 +1,28 @@
name = "patch-moi-harness-flows-fork"
version = 1
description = "Prepare a local harness fork package artifact from downstream harness package releases."
[config]
fork_repo = "harness/fork"
fork_repo_full_name = "matamune-peezy/patch-moi-harness"
source_branch = "main"
worktree_dir = ".codex/flow-artifacts/patch-moi-harness-flows-fork-worktree"
artifact_dir = ".codex/flow-artifacts/patch-moi-harness-flows-fork-release"
fetch = false
push = false
publish = false
accepted_packages = [
"@peezy.tech/patch-moi-harness",
"@peezy.tech/patch-moi-harness-fork",
]
[[steps]]
name = "release-fork"
runner = "bun"
script = "exec/release-fork.ts"
cwd = "../.."
timeout_ms = 600000
[steps.trigger]
type = "downstream.release"
schema = "schemas/downstream-release.schema.json"

View file

@ -0,0 +1,22 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["packageName", "version"],
"properties": {
"packageName": {
"type": "string",
"enum": [
"@peezy.tech/patch-moi-harness",
"@peezy.tech/patch-moi-harness-fork"
]
},
"version": {
"type": "string",
"minLength": 1
},
"repo": {
"type": "string"
}
},
"additionalProperties": true
}

View file

@ -0,0 +1,375 @@
import path from "node:path";
type FlowContext = {
flow: {
config?: Record<string, unknown>;
event: {
id: string;
type: string;
payload?: Record<string, unknown>;
};
};
};
type CommandResult = {
label: string;
cmd: string[];
cwd: string;
code: number;
stdout: string;
stderr: string;
};
type PatchBranch = {
name: string;
sha: string;
subject: string;
};
const context = JSON.parse(await Bun.stdin.text()) as FlowContext;
const config = context.flow.config ?? {};
const payload = context.flow.event.payload ?? {};
const commands: CommandResult[] = [];
const workspaceRoot = process.cwd();
const forkRepo = path.resolve(workspaceRoot, stringConfig("fork_repo", "harness/fork"));
const forkRepoFullName = stringConfig("fork_repo_full_name", "matamune-peezy/patch-moi-harness");
const targetBranch = stringConfig("target_branch", "main");
const upstreamBranch = stringConfig("upstream_branch", "upstream");
const patchPrefix = stringConfig("patch_prefix", "patch/");
const upstreamRemote = stringConfig("upstream_remote", "upstream");
const upstreamRepoUrl = stringConfig("upstream_repo_url", "https://github.com/peezy-tech/patch-moi-harness.git");
const verifyCommands = stringArrayConfig("verify_commands", ["npm test", "npm run pack:dry-run"]);
const pushRemotes = stringArrayConfig("push_remotes", ["origin", "jojo"]);
function finish(value: Record<string, unknown>): never {
process.stdout.write(`FLOW_RESULT ${JSON.stringify(value)}\n`);
process.exit(0);
}
try {
const expectedRepo = stringConfig("expected_repo", "peezy-tech/patch-moi-harness");
const repo = stringValue(payload.repo);
if (repo !== expectedRepo) {
finish({ status: "skipped", message: `Harness fork flow ignores ${repo}.` });
}
await requireGitRepo();
await ensureUpstreamRemote();
if (enabled("fetch", true)) {
await run("fetch upstream refs", ["git", "fetch", upstreamRemote, "--tags", "--prune"]);
}
await requireNoRebaseOrCherryPickInProgress();
await requireCleanWorktree("before harness patch rebuild");
const base = await resolveBase();
await run("update local upstream branch", ["git", "branch", "-f", upstreamBranch, base.sha]);
await ensureSeedPatchBranches();
const currentSha = (await runChecked("read harness checkout head", ["git", "rev-parse", "HEAD"])).stdout.trim();
const targetExists = await branchExists(targetBranch);
const beforeSha = targetExists ? await resolveCommit(targetBranch) : currentSha;
const beforeTree = beforeSha ? await resolveTree(beforeSha) : "";
const patchBranches = await listPatchBranches();
if (patchBranches.length === 0) {
finish({
status: "blocked",
message: `Harness fork has no ${patchPrefix} branches.`,
artifacts: baseArtifacts(base),
});
}
await run("switch to upstream rebuild base", ["git", "switch", "--detach", base.sha]);
const applied: PatchBranch[] = [];
for (const patchBranch of patchBranches) {
const pick = await run(`apply ${patchBranch.name}`, ["git", "cherry-pick", patchBranch.sha], { allowFailure: true });
if (pick.code !== 0) {
const status = await run("patch rebuild conflict status", ["git", "status", "--short", "--branch"], { allowFailure: true });
const unmerged = await run("unmerged files", ["git", "diff", "--name-only", "--diff-filter=U"], { allowFailure: true });
finish({
status: "needs_intervention",
message: `Harness patch rebuild stopped while applying ${patchBranch.name}.`,
artifacts: {
...baseArtifacts(base),
beforeSha,
applied,
failedPatch: patchBranch,
statusOutput: status.stdout,
unmergedFiles: lines(unmerged.stdout),
commands: commandArtifacts(),
},
});
}
applied.push(patchBranch);
}
const candidateSha = (await runChecked("read rebuilt harness head", ["git", "rev-parse", "HEAD"])).stdout.trim();
const candidateTree = await resolveTree(candidateSha);
const changed = !beforeSha || candidateTree !== beforeTree;
if (changed) {
await run("update maintained harness branch", ["git", "branch", "-f", targetBranch, candidateSha]);
} else if (!targetExists) {
await run("seed maintained harness branch", ["git", "branch", "-f", targetBranch, beforeSha]);
}
await run("switch maintained harness branch", ["git", "switch", targetBranch]);
for (const command of verifyCommands) {
const result = await run(`verify: ${command}`, ["bash", "-lc", command], { allowFailure: true });
if (result.code !== 0) {
finish({
status: "needs_intervention",
message: `Harness verification failed: ${command}.`,
artifacts: {
...baseArtifacts(base),
beforeSha,
candidateSha,
applied,
failedCommand: command,
commands: commandArtifacts(),
},
});
}
}
await requireCleanWorktree("after harness verification");
const afterSha = (await runChecked("read maintained harness head", ["git", "rev-parse", "HEAD"])).stdout.trim();
if (enabled("push", false)) {
for (const remote of pushRemotes) {
await run(`push ${remote}/${targetBranch}`, [
"git",
"push",
"--force-with-lease",
remote,
`HEAD:${targetBranch}`,
]);
}
}
finish({
status: changed ? "changed" : "completed",
message: changed
? `Harness fork rebuilt ${targetBranch} from ${base.label} plus ${patchBranches.length} patches.`
: `Harness fork already matches ${base.label} plus ${patchBranches.length} patches.`,
artifacts: {
...baseArtifacts(base),
eventId: context.flow.event.id,
forkRepo,
forkRepoFullName,
targetBranch,
upstreamBranch,
patchPrefix,
beforeSha,
afterSha,
applied,
checks: verifyCommands.map((name) => ({ name, status: "passed" })),
candidateRefs: candidateRefsFor(afterSha),
commands: commandArtifacts(),
},
});
} catch (error) {
finish({
status: "failed",
message: error instanceof Error ? error.message : String(error),
artifacts: { commands: commandArtifacts() },
});
}
async function requireGitRepo(): Promise<void> {
await runChecked("verify harness fork checkout", ["git", "rev-parse", "--show-toplevel"]);
}
async function ensureUpstreamRemote(): Promise<void> {
const current = await run("read upstream remote", ["git", "remote", "get-url", upstreamRemote], { allowFailure: true });
if (current.code === 0) return;
if (!upstreamRepoUrl) {
finish({ status: "blocked", message: `Missing ${upstreamRemote} remote and no upstream_repo_url is configured.` });
}
await run("add upstream remote", ["git", "remote", "add", upstreamRemote, upstreamRepoUrl]);
}
async function requireNoRebaseOrCherryPickInProgress(): Promise<void> {
const state = await run(
"check existing replay state",
["bash", "-lc", 'test -d "$(git rev-parse --git-path rebase-merge)" -o -d "$(git rev-parse --git-path rebase-apply)" -o -f "$(git rev-parse --git-path CHERRY_PICK_HEAD)"'],
{ allowFailure: true },
);
if (state.code === 0) {
finish({
status: "blocked",
message: "A rebase or cherry-pick is already in progress in the harness fork checkout.",
artifacts: { forkRepo, commands: commandArtifacts() },
});
}
}
async function requireCleanWorktree(stage: string): Promise<void> {
const status = await run(`dirty worktree check ${stage}`, ["git", "status", "--porcelain=v1"]);
if (status.stdout.trim()) {
finish({
status: "blocked",
message: `Harness fork checkout has local changes ${stage}.`,
artifacts: {
dirtyStatus: status.stdout,
forkRepo,
commands: commandArtifacts(),
},
});
}
}
async function resolveBase(): Promise<{ kind: "release" | "branch"; label: string; sha: string }> {
if (context.flow.event.type === "upstream.release") {
const tag = stringValue(payload.tag) || shortTag(stringValue(payload.ref) ?? "");
if (!tag) {
finish({ status: "failed", message: "upstream.release requires payload.tag." });
}
return { kind: "release", label: tag, sha: await resolveCommit(`refs/tags/${tag}`).catch(() => resolveCommit(tag)) };
}
if (context.flow.event.type === "upstream.branch_update") {
const ref = stringValue(payload.ref) ?? "refs/heads/main";
const sha = stringValue(payload.sha);
if (!sha) {
finish({ status: "failed", message: "upstream.branch_update requires payload.sha." });
}
return { kind: "branch", label: `${ref}@${sha}`, sha: await resolveCommit(sha) };
}
finish({ status: "skipped", message: `Unsupported harness fork event ${context.flow.event.type}.` });
}
async function ensureSeedPatchBranches(): Promise<void> {
const seeds = stringArrayConfig("seed_patch_refs", []);
for (const seed of seeds) {
const [name, ref] = seed.split("=");
if (!name?.startsWith(patchPrefix) || !ref) {
throw new Error(`Invalid seed_patch_refs entry: ${seed}`);
}
const exists = await branchExists(name);
if (exists) continue;
await resolveCommit(ref);
await run(`seed ${name}`, ["git", "branch", "-f", name, ref]);
}
}
async function listPatchBranches(): Promise<PatchBranch[]> {
const refsPath = `refs/heads/${patchPrefix.replace(/\/+$/, "")}`;
const result = await run("list harness patch branches", [
"git",
"for-each-ref",
"--format=%(refname:short)%09%(objectname)%09%(contents:subject)",
refsPath,
], { allowFailure: true });
if (result.code !== 0 || !result.stdout.trim()) return [];
return result.stdout.trim().split(/\r?\n/).map((line) => {
const [name = "", sha = "", subject = ""] = line.split("\t");
return { name, sha, subject };
}).filter((branch) => branch.name.startsWith(patchPrefix)).sort((left, right) => left.name.localeCompare(right.name));
}
async function branchExists(branch: string): Promise<boolean> {
return (await run(`check branch ${branch}`, ["git", "show-ref", "--verify", "--quiet", `refs/heads/${branch}`], { allowFailure: true })).code === 0;
}
async function resolveCommit(ref: string): Promise<string> {
return (await runChecked(`resolve ${ref}`, ["git", "rev-parse", "--verify", `${ref}^{commit}`])).stdout.trim();
}
async function resolveTree(ref: string): Promise<string> {
return (await runChecked(`resolve tree ${ref}`, ["git", "rev-parse", "--verify", `${ref}^{tree}`])).stdout.trim();
}
async function runChecked(label: string, cmd: string[]): Promise<CommandResult> {
const result = await run(label, cmd);
if (result.code !== 0) {
throw new Error(`${label} failed with exit ${result.code}:\n${result.stderr || result.stdout}`);
}
return result;
}
async function run(label: string, cmd: string[], options: { allowFailure?: boolean } = {}): Promise<CommandResult> {
process.stderr.write(`+ ${label}: ${cmd.join(" ")}\n`);
const child = Bun.spawn(cmd, {
cwd: forkRepo,
env: process.env,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, code] = await Promise.all([
new Response(child.stdout).text(),
new Response(child.stderr).text(),
child.exited,
]);
if (stdout) process.stderr.write(stdout);
if (stderr) process.stderr.write(stderr);
const result = { label, cmd, cwd: forkRepo, code, stdout, stderr };
commands.push(result);
if (code !== 0 && !options.allowFailure) {
throw new Error(`${label} failed with exit ${code}:\n${stderr || stdout}`);
}
return result;
}
function baseArtifacts(base: { kind: string; label: string; sha: string }): Record<string, unknown> {
return {
upstreamKind: base.kind,
upstreamLabel: base.label,
upstreamSha: base.sha,
};
}
function candidateRefsFor(sha: string): Array<Record<string, unknown>> {
const pushed = enabled("push", false);
const remotes = pushed ? pushRemotes : ["local"];
return remotes.map((remote) => ({
kind: "branch",
repo: forkRepoFullName,
remote,
ref: `refs/heads/${targetBranch}`,
sha,
pushed,
}));
}
function commandArtifacts(): Array<Record<string, unknown>> {
return commands.map((command) => ({
...command,
stdout: truncate(command.stdout),
stderr: truncate(command.stderr),
}));
}
function enabled(name: string, fallback: boolean): boolean {
const envValue = process.env[`CODEX_FLOW_${name.toUpperCase()}`];
if (envValue !== undefined) {
return ["1", "true", "yes", "on"].includes(envValue.trim().toLowerCase());
}
const value = config[name];
return typeof value === "boolean" ? value : fallback;
}
function stringConfig(name: string, fallback: string): string {
const value = config[name];
return typeof value === "string" && value.trim() ? value : fallback;
}
function stringArrayConfig(name: string, fallback: string[]): string[] {
const value = config[name];
if (!Array.isArray(value)) return fallback;
const entries = value.filter((entry): entry is string => typeof entry === "string" && entry.trim());
return entries.length > 0 ? entries : fallback;
}
function stringValue(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
function lines(value: string): string[] {
return value.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
}
function shortTag(value: string): string {
return value.replace(/^refs\/tags\//, "");
}
function truncate(value: string, max = 4000): string {
return value.length <= max ? value : `${value.slice(0, max)}\n...[truncated ${value.length - max} chars]`;
}

View file

@ -0,0 +1,11 @@
{
"id": "patch:harness-main:772bdd62c6031302b57b175e428110fa51671861:upstream.branch_update",
"type": "upstream.branch_update",
"source": "patch",
"receivedAt": "2026-05-16T00:00:00.000Z",
"payload": {
"repo": "peezy-tech/patch-moi-harness",
"ref": "refs/heads/main",
"sha": "772bdd62c6031302b57b175e428110fa51671861"
}
}

View file

@ -0,0 +1,11 @@
{
"id": "patch:harness:v0.1.3:upstream.release",
"type": "upstream.release",
"source": "patch",
"receivedAt": "2026-05-16T00:00:00.000Z",
"payload": {
"repo": "peezy-tech/patch-moi-harness",
"tag": "v0.1.3",
"url": "https://github.com/peezy-tech/patch-moi-harness/releases/tag/v0.1.3"
}
}

View file

@ -0,0 +1,44 @@
name = "patch-moi-harness-fork"
version = 1
description = "Maintain the harness fork patch workspace from upstream releases and main updates."
[config]
expected_repo = "peezy-tech/patch-moi-harness"
fork_repo = "harness/fork"
fork_repo_full_name = "matamune-peezy/patch-moi-harness"
target_branch = "main"
upstream_branch = "upstream"
patch_prefix = "patch/"
upstream_remote = "upstream"
upstream_repo_url = "https://github.com/peezy-tech/patch-moi-harness.git"
fetch = true
push = false
push_remotes = ["origin", "jojo"]
verify_commands = ["npm test", "npm run pack:dry-run"]
seed_patch_refs = [
"patch/010-maintained-greeting=e8e54070d4465b509b07d9d718ed693a75b6f870",
"patch/020-shout-mode=5a06a998390b0b835adcad134f8055a2c2eb5b02",
"patch/030-package-identity=9fd9d8c31a004059e16f769453d75afba29959be",
]
[[steps]]
name = "release-cycle"
runner = "bun"
script = "exec/update-fork.ts"
cwd = "../.."
timeout_ms = 600000
[steps.trigger]
type = "upstream.release"
schema = "schemas/upstream-release.schema.json"
[[steps]]
name = "main-branch-update"
runner = "bun"
script = "exec/update-fork.ts"
cwd = "../.."
timeout_ms = 600000
[steps.trigger]
type = "upstream.branch_update"
schema = "schemas/upstream-branch-update.schema.json"

View file

@ -0,0 +1,20 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["repo", "ref", "sha"],
"properties": {
"repo": {
"type": "string",
"enum": ["peezy-tech/patch-moi-harness"]
},
"ref": {
"type": "string",
"enum": ["refs/heads/main"]
},
"sha": {
"type": "string",
"pattern": "^[0-9a-fA-F]{7,40}$"
}
},
"additionalProperties": true
}

View file

@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["repo", "tag"],
"properties": {
"repo": {
"type": "string",
"enum": ["peezy-tech/patch-moi-harness"]
},
"tag": {
"type": "string",
"minLength": 1
},
"url": {
"type": "string"
}
},
"additionalProperties": true
}

View file

@ -1,42 +0,0 @@
# patch-moi-harness Flow
This flow is the first executable patch.moi maintenance harness. It consumes a
generic `upstream.release` event for `peezy-tech/patch-moi-harness`, then uses
Git state in `harness/fork` to keep the maintained fork on top of the upstream
release tag.
The default behavior is local and reviewable:
- fetch the configured upstream remote
- switch to the maintained fork branch
- rebase onto the release tag when the tag is not already an ancestor
- run the configured package checks
- emit candidate branch refs in the `FLOW_RESULT` artifacts
- leave pushes disabled unless `CODEX_FLOW_PUSH=1` is set
Useful overrides:
```bash
CODEX_FLOW_FETCH=0 bun run harness:flow
CODEX_FLOW_PUSH=1 bun run harness:flow
```
The fixture event is `fixtures/upstream-release-v0.1.3.json`. It should be a
no-op rebase against the current harness fork while still verifying the package
surface and reporting the local maintained branch as the candidate ref.
The repo also exposes an experimental workspace-owned flow smoke task:
```bash
cd ../codex-flows
bun run workspace:backend --cwd /home/peezy/meta-workspace/patch.moi
```
```bash
CODEX_WORKSPACE_BACKEND_WS_URL=ws://127.0.0.1:3586 \
CODEX_FLOW_FETCH=0 CODEX_FLOW_PUSH=0 \
bun run workspace:run:harness-flow
```
That task requires a running Codex workspace backend and writes backend
event/run state, not patch.moi `DATA_DIR` maintenance-attempt state.

View file

@ -1,336 +0,0 @@
import path from "node:path";
import { defineBunFlow } from "@peezy.tech/codex-flows/flow-runtime/bun";
import type { FlowResult, FlowResultStatus, FlowRunContext } from "@peezy.tech/codex-flows/flow-runtime";
type HarnessFlowContext = FlowRunContext & {
flow: FlowRunContext["flow"] & {
event: FlowRunContext["flow"]["event"] & {
payload: Record<string, unknown>;
};
};
};
type CommandResult = {
label: string;
cmd: string[];
cwd: string;
exitCode: number | null;
stdout: string;
stderr: string;
};
const expectedRepo = "peezy-tech/patch-moi-harness";
let context: HarnessFlowContext;
let config: Record<string, unknown>;
let commands: CommandResult[];
let releaseTag: string;
let repo: string;
let forkRepo: string;
let forkRepoFullName: string;
let targetBranch: string;
let upstreamRemote: string;
let upstreamRepoUrl: string;
let verifyCommands: string[];
let pushRemotes: string[];
class FlowFinished extends Error {
constructor(readonly result: FlowResult) {
super(result.message ?? result.status);
}
}
export default defineBunFlow(async (flowContext: FlowRunContext): Promise<FlowResult> => {
context = flowContext as HarnessFlowContext;
config = context.flow.config ?? {};
commands = [];
const workspaceRoot = process.cwd();
const payload = context.flow.event.payload ?? {};
releaseTag = tagFromPayload(payload);
repo = stringValue(payload.repo, "payload.repo");
forkRepo = path.resolve(workspaceRoot, stringConfig("fork_repo", "harness/fork"));
forkRepoFullName = stringConfig("fork_repo_full_name", "matamune-peezy/patch-moi-harness");
targetBranch = stringConfig("target_branch", "main");
upstreamRemote = stringConfig("upstream_remote", "upstream");
upstreamRepoUrl = stringConfig("upstream_repo_url", "https://github.com/peezy-tech/patch-moi-harness.git");
verifyCommands = stringArrayConfig("verify_commands", ["npm test", "npm run pack:dry-run"]);
pushRemotes = stringArrayConfig("push_remotes", ["origin", "jojo"]);
try {
if (repo !== expectedRepo) {
finish("skipped", `Harness flow ignores ${repo}.`, { repo, expectedRepo });
}
if (!releaseTag) {
finish("failed", "Release payload is missing tag or refs/tags ref.");
}
const repoCheck = await run("verify harness fork checkout", ["git", "rev-parse", "--show-toplevel"]);
if (repoCheck.exitCode !== 0) {
finish("blocked", `Harness fork checkout is not available at ${forkRepo}.`, { forkRepo });
}
await ensureUpstreamRemote();
if (enabled("fetch", true)) {
await run("fetch upstream release refs", ["git", "fetch", upstreamRemote, "--tags", "--prune"]);
}
await requireNoRebaseInProgress();
await requireCleanWorktree("before harness fork rebase");
const branch = (await run("read current branch", ["git", "branch", "--show-current"])).stdout.trim();
if (branch !== targetBranch) {
await run("switch maintained fork branch", ["git", "switch", targetBranch]);
}
const beforeSha = (await run("read fork head before rebase", ["git", "rev-parse", "HEAD"])).stdout.trim();
const releaseSha = await resolveReleaseCommit(releaseTag);
const alreadyContainsRelease = (await run(
"check release ancestor",
["git", "merge-base", "--is-ancestor", releaseSha, "HEAD"],
{ allowFailure: true },
)).exitCode === 0;
if (!alreadyContainsRelease) {
const rebase = await run(
"rebase harness fork onto upstream release",
["git", "rebase", releaseSha],
{ allowFailure: true },
);
if (rebase.exitCode !== 0) {
const context = await collectRebaseContext(rebase, beforeSha);
finish("needs_intervention", `Harness fork rebase stopped on ${releaseTag}.`, context);
}
}
for (const command of verifyCommands) {
const result = await run(`verify: ${command}`, ["bash", "-lc", command], { allowFailure: true });
if (result.exitCode !== 0) {
finish("needs_intervention", `Harness verification failed: ${command}.`, {
failedCommand: command,
});
}
}
await requireCleanWorktree("after harness fork verification");
const afterSha = (await run("read fork head after rebase", ["git", "rev-parse", "HEAD"])).stdout.trim();
if (enabled("push", false)) {
for (const remote of pushRemotes) {
await run(`push ${remote}/${targetBranch}`, [
"git",
"push",
"--force-with-lease",
remote,
`HEAD:${targetBranch}`,
]);
}
}
finish(beforeSha === afterSha ? "completed" : "changed", harnessMessage(beforeSha, afterSha), {
eventId: context.flow.event.id,
repo,
forkRepo,
forkRepoFullName,
targetBranch,
releaseTag,
releaseSha,
beforeSha,
afterSha,
pushed: enabled("push", false),
checks: verifyCommands.map((command) => ({ name: command, status: "passed" })),
candidateRefs: candidateRefsFor(afterSha),
});
} catch (error) {
if (error instanceof FlowFinished) {
return error.result;
}
return buildResult("failed", error instanceof Error ? error.message : String(error));
}
});
async function ensureUpstreamRemote(): Promise<void> {
const current = await run("read upstream remote", ["git", "remote", "get-url", upstreamRemote], {
allowFailure: true,
});
if (current.exitCode === 0) {
return;
}
if (!upstreamRepoUrl) {
finish("blocked", `Missing ${upstreamRemote} remote and no upstream_repo_url is configured.`);
}
await run("add upstream remote", ["git", "remote", "add", upstreamRemote, upstreamRepoUrl]);
}
async function requireNoRebaseInProgress(): Promise<void> {
const state = await run(
"check existing rebase state",
["bash", "-lc", 'test -d "$(git rev-parse --git-path rebase-merge)" -o -d "$(git rev-parse --git-path rebase-apply)"'],
{ allowFailure: true },
);
if (state.exitCode === 0) {
finish("blocked", "A rebase is already in progress in the harness fork checkout.", {
forkRepo,
});
}
}
async function requireCleanWorktree(stage: string): Promise<void> {
const status = await run(`dirty worktree check ${stage}`, ["git", "status", "--porcelain=v1"]);
if (status.stdout.trim()) {
finish("blocked", `Harness fork checkout has local changes ${stage}.`, {
dirtyStatus: status.stdout,
forkRepo,
});
}
}
async function resolveReleaseCommit(tag: string): Promise<string> {
const candidates = tag.startsWith("refs/")
? [`${tag}^{commit}`, `${shortTag(tag)}^{commit}`]
: [`refs/tags/${tag}^{commit}`, `${tag}^{commit}`];
for (const candidate of candidates) {
const result = await run(`resolve release ref ${candidate}`, ["git", "rev-parse", "--verify", candidate], {
allowFailure: true,
});
if (result.exitCode === 0 && result.stdout.trim()) {
return result.stdout.trim();
}
}
finish("blocked", `Could not resolve upstream release tag ${tag}.`, {
tag,
forkRepo,
});
}
async function collectRebaseContext(rebase: CommandResult, beforeSha: string): Promise<Record<string, unknown>> {
const status = await run("rebase conflict status", ["git", "status", "--short", "--branch"], {
allowFailure: true,
});
const unmerged = await run("unmerged files", ["git", "diff", "--name-only", "--diff-filter=U"], {
allowFailure: true,
});
const currentPatch = await run("current rebase patch", ["git", "rebase", "--show-current-patch"], {
allowFailure: true,
});
return {
beforeSha,
rebaseOutput: truncate(rebase.stderr || rebase.stdout, 8000),
statusOutput: status.stdout,
unmergedFiles: unmerged.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean),
currentPatch: truncate(currentPatch.stdout || currentPatch.stderr, 12000),
};
}
async function run(
label: string,
cmd: string[],
options: { allowFailure?: boolean } = {},
): Promise<CommandResult> {
const child = Bun.spawn(cmd, {
cwd: forkRepo,
env: process.env,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(child.stdout).text(),
new Response(child.stderr).text(),
child.exited,
]);
const result = { label, cmd, cwd: forkRepo, exitCode, stdout, stderr };
commands.push(result);
if (exitCode !== 0 && !options.allowFailure) {
throw new Error(`${label} failed with exit ${exitCode}:\n${stderr || stdout}`);
}
return result;
}
function finish(status: FlowResultStatus, message: string, artifacts: Record<string, unknown> = {}): never {
throw new FlowFinished(buildResult(status, message, artifacts));
}
function buildResult(status: FlowResultStatus, message: string, artifacts: Record<string, unknown> = {}): FlowResult {
const commandArtifacts = commands.map((command) => ({
...command,
stdout: truncate(command.stdout),
stderr: truncate(command.stderr),
}));
return {
status,
message,
artifacts: {
...artifacts,
commands: commandArtifacts,
},
};
}
function harnessMessage(beforeSha: string, afterSha: string): string {
if (beforeSha === afterSha) {
return `Harness fork already contains ${releaseTag}; package checks passed.`;
}
return `Harness fork rebased onto ${releaseTag}; package checks passed.`;
}
function candidateRefsFor(sha: string): Array<Record<string, unknown>> {
const pushed = enabled("push", false);
const remotes = pushed ? pushRemotes : ["local"];
return remotes.map((remote) => ({
kind: "branch",
repo: forkRepoFullName,
remote,
ref: `refs/heads/${targetBranch}`,
sha,
pushed,
}));
}
function enabled(name: string, fallback: boolean): boolean {
const envValue = process.env[`CODEX_FLOW_${name.toUpperCase()}`];
if (envValue !== undefined) {
return ["1", "true", "yes", "on"].includes(envValue.trim().toLowerCase());
}
const value = config[name];
return typeof value === "boolean" ? value : fallback;
}
function stringConfig(name: string, fallback: string): string {
const value = config[name];
return typeof value === "string" && value.trim() ? value : fallback;
}
function stringArrayConfig(name: string, fallback: string[]): string[] {
const value = config[name];
if (!Array.isArray(value)) {
return fallback;
}
const entries = value.filter((entry): entry is string => typeof entry === "string" && entry.trim());
return entries.length > 0 ? entries : fallback;
}
function stringValue(value: unknown, name: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new Error(`${name} must be a non-empty string`);
}
return value;
}
function tagFromPayload(value: Record<string, unknown>): string {
if (typeof value.tag === "string" && value.tag.trim()) {
return value.tag.trim();
}
if (typeof value.ref === "string" && value.ref.startsWith("refs/tags/")) {
return value.ref.trim();
}
return "";
}
function shortTag(value: string): string {
return value.replace(/^refs\/tags\//, "");
}
function truncate(value: string, max = 4000): string {
return value.length <= max ? value : `${value.slice(0, max)}\n...[truncated ${value.length - max} chars]`;
}

View file

@ -1,25 +0,0 @@
name = "patch-moi-harness"
version = 1
description = "Rebase the maintained patch.moi harness fork onto an upstream harness release."
[config]
fork_repo = "harness/fork"
fork_repo_full_name = "matamune-peezy/patch-moi-harness"
target_branch = "main"
upstream_remote = "upstream"
upstream_repo_url = "https://github.com/peezy-tech/patch-moi-harness.git"
fetch = true
push = false
push_remotes = ["origin", "jojo"]
verify_commands = ["npm test", "npm run pack:dry-run"]
[[steps]]
name = "rebase-fork"
runner = "bun"
script = "exec/rebase-fork.ts"
cwd = "../.."
timeout_ms = 600000
[steps.trigger]
type = "upstream.release"
schema = "schemas/upstream-release.schema.json"

View file

@ -1,15 +0,0 @@
{
"type": "object",
"required": ["repo", "tag"],
"properties": {
"provider": { "type": "string" },
"repo": {
"type": "string",
"enum": ["peezy-tech/patch-moi-harness"]
},
"tag": { "type": "string", "minLength": 1 },
"ref": { "type": "string" },
"url": { "type": "string" },
"publishedAt": { "type": "string" }
}
}

View file

@ -34,8 +34,13 @@ git -C harness/fork fetch jojo
## Branch Model
- `harness/upstream` `main`: upstream package history and upstream releases.
- `harness/fork` `upstream/main`: fetched copy of upstream `main`.
- `harness/fork` `main`: maintained fork with patch commits applied.
- `harness/fork` `upstream`: local branch that follows the selected upstream
release tag or main commit for a maintenance run.
- `harness/fork` `main`: maintained fork rebuilt from `upstream` plus ordered
`patch/*` branches.
- `harness/fork` `patch/*`: one logical fork patch per branch tip. The current
seeds are `patch/010-maintained-greeting`, `patch/020-shout-mode`, and
`patch/030-package-identity`.
- `harness/fork` `jojo/main`: service-style maintained fork remote.
The upstream npm package is `@peezy.tech/patch-moi-harness`. It publishes from
@ -84,7 +89,7 @@ git push jojo main
Expected result: the GitHub fork and jojo maintained branch both move ahead of
the last upstream commit.
## Scenario: Rebase Fork Onto Upstream
## Scenario: Rebuild Fork Onto Upstream
Use this when upstream has released and the maintained fork needs to carry its
patches forward.
@ -92,25 +97,30 @@ patches forward.
```bash
cd harness/fork
git fetch upstream
git fetch origin
git branch -f upstream upstream/main
git checkout --detach upstream
for patch in $(git for-each-ref --format='%(refname:short)' refs/heads/patch | sort); do
git cherry-pick "$patch"
done
git branch -f main HEAD
git checkout main
git rebase upstream/main
npm test
git push --force-with-lease origin main
git push --force-with-lease jojo main
```
Expected result: fork `main` is still patched, but its base is the latest
upstream release.
upstream release or main commit.
The same maintenance path is executable through the first patch.moi harness
flow:
The same maintenance path is executable through the patch.moi harness flows:
```bash
CODEX_FLOW_FETCH=0 CODEX_FLOW_PUSH=0 bun run harness:flow
```
That direct command is local-mode execution. The repo-native workspace autonomy
That direct command is local-mode execution. The default upstream release
fixture fans out to `patch-moi-harness-bindings/generate-bindings` and
`patch-moi-harness-fork/release-cycle`. The repo-native workspace autonomy
surface runs the same harness through a manual command task:
```bash
@ -125,7 +135,26 @@ flow event to the configured workspace backend.
The default fixture targets `v0.1.3`, which should verify the current fork
without changing it and report `candidateRefs` for the maintained fork branch.
For a new upstream tag, run the same command with an event file whose
`payload.tag` names that tag.
`payload.tag` names that tag. For upstream main movement, use the
`flows/patch-moi-harness-fork/fixtures/upstream-main-v0.1.3.json` event shape
with the new main SHA.
## Scenario: Downstream Release Artifact
Use this when a downstream package release should prepare a local fork artifact
without pushing or publishing:
```bash
bun run patch.moi -- run event \
--file flows/patch-moi-harness-flows-fork/fixtures/downstream-fork-release-v0.1.3-fork.0.json \
--allow-local \
--json
```
Expected result: `patch-moi-harness-flows-fork/release-fork` creates a
flow-owned worktree under `.codex/flow-artifacts`, runs the fork package tests,
and writes an npm tarball under
`.codex/flow-artifacts/patch-moi-harness-flows-fork-release`.
## Scenario: Fork Release