Adopt codex-flows 0.3.5 surfaces
Some checks failed
check / check (push) Failing after 3s

This commit is contained in:
matamune 2026-05-18 00:07:15 +00:00
parent e7a85c5bcf
commit 81ea91e387
Signed by: matamune
GPG key ID: 3BB8E7D3B968A324
22 changed files with 197 additions and 170 deletions

View file

@ -7,12 +7,12 @@
"source": {
"input": "../codex-flows",
"type": "local",
"commit": "5be9b571578409a31af4693caac8c949c06fe388"
"commit": "a96a005617d18d95d6746efdbd6a1f7bcd70ca49"
},
"sourcePath": "flows/openai-codex-bindings",
"destinationPath": ".codex/flows/openai-codex-bindings",
"contentHash": "sha256:5fcb04e9525e94c24ca319fcefc99fd05ed973e9c640a5e27629e263f13ca968",
"installedAt": "2026-05-16T19:56:06.687Z"
"installedAt": "2026-05-18T00:04:17.788Z"
},
{
"name": "peezy-codex-fork",
@ -20,12 +20,12 @@
"source": {
"input": "../codex-flows",
"type": "local",
"commit": "5be9b571578409a31af4693caac8c949c06fe388"
"commit": "a96a005617d18d95d6746efdbd6a1f7bcd70ca49"
},
"sourcePath": "flows/peezy-codex-fork",
"destinationPath": ".codex/flows/peezy-codex-fork",
"contentHash": "sha256:521e7766cd1888fd54fc0c10a6d2a3a7f235d9a7c8aa2577b4f4681e3a8fdabe",
"installedAt": "2026-05-16T19:56:06.687Z"
"installedAt": "2026-05-18T00:04:17.788Z"
}
]
}

View file

@ -12,12 +12,21 @@ jobs:
runs-on: bun
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install
run: bun install --frozen-lockfile
- name: Typecheck
run: bun run check:types
- name: Test
run: bun run test
- name: Verify no-backend Actions dispatch
run: |
data_dir="$(mktemp -d)"
CODEX_WORKSPACE_MODE=actions \
CODEX_FLOW_FETCH=0 \
CODEX_FLOW_PUSH=0 \
bun run patch.moi -- run harness --workspace-root "$PWD" --data-dir "$data_dir" --json
- name: Build docs
run: bun run docs:build
- name: Build container

1
.gitignore vendored
View file

@ -4,6 +4,7 @@ coverage
*.log
.env
/.codex/workspace/local/
/.codex/workspace/actions/
data/
apps/*/data/
docs/out/

View file

@ -50,6 +50,7 @@ PATCH_FLOW_BACKEND_URL=
PATCH_FLOW_DISPATCH_URL=
PATCH_FLOW_DISPATCH_SECRET=
PATCH_ADMIN_TOKEN=
CODEX_WORKSPACE_MODE=
```
Discord notifications are off by default. Set `DISCORD_OUTPUT_ENABLED=true`
@ -75,6 +76,12 @@ configured workspace backend or local adapter, and record dispatch outcomes in
patch.moi-owned `DATA_DIR/maintenance-attempts.jsonl` entry that links the
upstream update to workspace run ids, final flow outcome, and candidate refs.
Patch can dispatch through a configured workspace backend, through the
codex-flows Actions/local surface with `CODEX_WORKSPACE_MODE=actions`, or
through synchronous local flow execution for development. The backend does not
need to be running in this checkout unless you are explicitly testing or
operating that surface.
The harness can also be run through the repo-native workspace task:
```bash

View file

@ -17,7 +17,7 @@
"check": "bun run check:types && bun run test"
},
"dependencies": {
"@peezy.tech/flow-runtime": "^0.4.0"
"@peezy.tech/codex-flows": "^0.3.5"
},
"devDependencies": {
"@types/bun": "catalog:",

View file

@ -3,7 +3,7 @@
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { discoverFlows, matchingSteps, type FlowEvent as RuntimeFlowEvent } from "@peezy.tech/flow-runtime";
import { discoverFlows, matchingSteps, type FlowEvent as RuntimeFlowEvent } from "@peezy.tech/codex-flows/flow-runtime";
import {
dispatchWorkspaceEventDetailed,
maintenanceAttemptForWorkspaceDispatch,
@ -462,11 +462,15 @@ async function appendFlowEventIfMissing(store: EventStore, event: FlowEvent): Pr
}
function assertCodexDispatchAllowed(context: CliContext): void {
if (flagBool(context.parsed, "allow-local") || workspaceBackendConfigured(context.env)) {
if (
flagBool(context.parsed, "allow-local") ||
workspaceBackendConfigured(context.env) ||
actionsLocalConfigured(context.env)
) {
return;
}
throw new UsageError(
"codex-release dispatch requires PATCH_WORKSPACE_BACKEND_URL or --allow-local; use --dry-run to verify matching without executing release work",
"codex-release dispatch requires PATCH_WORKSPACE_BACKEND_URL, CODEX_WORKSPACE_MODE=actions, or --allow-local; use --dry-run to verify matching without executing release work",
);
}
@ -478,6 +482,10 @@ function workspaceBackendConfigured(env: Record<string, string | undefined>): bo
);
}
function actionsLocalConfigured(env: Record<string, string | undefined>): boolean {
return env.CODEX_WORKSPACE_MODE === "actions" || env.GITHUB_ACTIONS === "true";
}
function workspaceConfig(context: CliContext): WorkspaceDispatchConfig {
return {
env: context.env,

View file

@ -2,7 +2,7 @@ import type {
FlowDispatchResult,
FlowReplayResult,
FlowRunView,
} from "@peezy.tech/flow-runtime/client";
} from "@peezy.tech/codex-flows/flow-runtime/client";
import type {
CandidateRefRecord,
FeedWorkspaceFlowTarget,
@ -143,7 +143,7 @@ export async function dispatchWorkspaceEventDetailed(
eventId: event.id,
eventType: event.type,
operation: "dispatch",
target: backend.mode === "local" ? "local" : "workspace-backend",
target: localTransport(backend.mode) ? "local" : "workspace-backend",
transport: backend.mode,
workspaceBackendUrl: backend.url,
url: backend.eventsUrl,
@ -161,7 +161,7 @@ export async function dispatchWorkspaceEventDetailed(
eventId: event.id,
eventType: event.type,
operation: "dispatch",
target: backend.mode === "local" ? "local" : "workspace-backend",
target: localTransport(backend.mode) ? "local" : "workspace-backend",
transport: backend.mode,
workspaceBackendUrl: backend.url,
url: backend.eventsUrl,
@ -210,7 +210,7 @@ export async function replayWorkspaceEventDetailed(
eventId: event.id,
eventType: event.type,
operation: "replay",
target: backend.mode === "local" ? "local" : "workspace-backend",
target: localTransport(backend.mode) ? "local" : "workspace-backend",
transport: backend.mode,
workspaceBackendUrl: backend.url,
url: backend.eventsUrl ? `${backend.eventsUrl}/${encodeURIComponent(event.id)}/replay` : undefined,
@ -228,7 +228,7 @@ export async function replayWorkspaceEventDetailed(
eventId: event.id,
eventType: event.type,
operation: "replay",
target: backend.mode === "local" ? "local" : "workspace-backend",
target: localTransport(backend.mode) ? "local" : "workspace-backend",
transport: backend.mode,
workspaceBackendUrl: backend.url,
url: backend.eventsUrl,
@ -372,6 +372,10 @@ function httpStatusFromError(error: unknown): number | undefined {
return match?.[1] ? Number(match[1]) : undefined;
}
function localTransport(value: string): boolean {
return value === "local" || value === "actions-local";
}
function stringValue(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value : undefined;
}

View file

@ -5,7 +5,7 @@ import {
matchingSteps,
runFlowStep,
type FlowEvent,
} from "@peezy.tech/flow-runtime";
} from "@peezy.tech/codex-flows/flow-runtime";
const workspaceRoot = path.resolve(import.meta.dir, "../../..");
const fixturePath = path.resolve(

View file

@ -83,7 +83,7 @@ export type FlowDispatchRecord = {
eventType: string;
operation?: "dispatch" | "replay";
target?: "local" | "workspace-backend";
transport?: "local" | "workspace-http" | "workspace-ws";
transport?: "local" | "actions-local" | "workspace-http" | "workspace-ws";
workspaceBackendUrl?: string;
url?: string;
status: "dispatched" | "failed" | "skipped";

View file

@ -11,14 +11,16 @@ import {
type FlowReplayResult,
type FlowRunList,
type FlowRunView,
} from "@peezy.tech/flow-runtime/client";
} from "@peezy.tech/codex-flows/flow-runtime/client";
import { createActionsLocalFlowClient } from "@peezy.tech/codex-flows/actions";
import { CodexWorkspaceBackendClient } from "@peezy.tech/codex-flows/workspace-backend";
import {
normalizeDispatchResult,
normalizeEvent,
normalizeEventList,
normalizeRun,
normalizeRunList,
} from "@peezy.tech/flow-runtime/backend-client";
} from "@peezy.tech/codex-flows/flow-runtime/backend-client";
import type { FeedWorkspaceFlowTarget, FlowEvent } from "./types";
export type WorkspaceBackendFetch = (url: string, init: RequestInit) => Promise<Response>;
@ -30,23 +32,12 @@ export type WorkspaceBackendConfig = {
};
export type PatchWorkspaceBackend = {
mode: "local" | "workspace-http" | "workspace-ws";
mode: "local" | "actions-local" | "workspace-http" | "workspace-ws";
url?: string;
eventsUrl?: string;
client: FlowClient;
};
type WorkspaceJsonRpcResponse = {
jsonrpc: "2.0";
id: string | number | null;
result?: unknown;
error?: {
code?: number;
message?: string;
data?: unknown;
};
};
export function createPatchWorkspaceBackend(
target: Partial<FeedWorkspaceFlowTarget> = {},
config: WorkspaceBackendConfig = {},
@ -74,11 +65,21 @@ export function createPatchWorkspaceBackend(
}),
};
}
const cwd = config.cwd ?? process.cwd();
if (actionsLocalRequested(env)) {
return {
mode: "actions-local",
client: createActionsLocalFlowClient({
workspaceRoot: cwd,
env,
}) as unknown as FlowClient,
};
}
return {
mode: "local",
client: createFlowClient({
mode: "local",
cwd: config.cwd ?? process.cwd(),
cwd,
env,
codex: {
command: env.CODEX_APP_SERVER_CODEX_COMMAND,
@ -136,6 +137,10 @@ function isWebSocketUrl(url: string): boolean {
return url.startsWith("ws://") || url.startsWith("wss://");
}
function actionsLocalRequested(env: Record<string, string | undefined>): boolean {
return env.CODEX_WORKSPACE_MODE === "actions" || env.GITHUB_ACTIONS === "true";
}
function patchFetch(fetchImpl: WorkspaceBackendFetch) {
return async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
return fetchImpl(String(input), init ?? {});
@ -190,104 +195,24 @@ class WorkspaceBackendWebSocketFlowClient implements FlowClient {
}
async #request(method: string, params?: unknown): Promise<unknown> {
return workspaceBackendWebSocketRequest(this.#url, method, params);
const client = new CodexWorkspaceBackendClient({
clientName: "patch-moi",
clientTitle: "patch.moi",
clientVersion: "0.1.0",
webSocketTransportOptions: {
url: this.#url,
requestTimeoutMs: 90_000,
},
});
try {
await client.connect();
return await client.workspaceRequest(method, params);
} finally {
client.close();
}
}
}
function workspaceBackendWebSocketRequest(url: string, method: string, params?: unknown): Promise<unknown> {
return new Promise((resolve, reject) => {
const socket = new WebSocket(url);
let nextId = 1;
let initId = 0;
let callId = 0;
let settled = false;
const timer = setTimeout(() => fail(new Error(`Workspace backend ${method} timed out`)), 90_000);
function requestId(): number {
const id = nextId;
nextId += 1;
return id;
}
function sendRequest(requestMethod: string, requestParams?: unknown): number {
const id = requestId();
socket.send(JSON.stringify({
jsonrpc: "2.0",
id,
method: requestMethod,
params: requestParams,
}));
return id;
}
function finish(value: unknown): void {
if (settled) {
return;
}
settled = true;
clearTimeout(timer);
socket.close();
resolve(value);
}
function fail(error: unknown): void {
if (settled) {
return;
}
settled = true;
clearTimeout(timer);
socket.close();
reject(error);
}
socket.addEventListener("open", () => {
initId = sendRequest("workspace.initialize", {
clientInfo: {
name: "patch-moi",
title: "patch.moi",
version: "0.1.0",
},
capabilities: {
appServerPassThrough: true,
},
});
});
socket.addEventListener("message", (event) => {
let response: WorkspaceJsonRpcResponse;
try {
response = JSON.parse(String(event.data)) as WorkspaceJsonRpcResponse;
} catch {
fail(new Error("Workspace backend returned invalid JSON-RPC"));
return;
}
if (response.id === undefined || response.id === null) {
return;
}
if (response.error) {
fail(new Error(response.error.message ?? `Workspace backend ${method} failed`));
return;
}
if (response.id === initId) {
callId = sendRequest(method, params);
return;
}
if (response.id === callId) {
finish(response.result);
}
});
socket.addEventListener("error", () => {
fail(new Error(`Workspace backend WebSocket request failed: ${method}`));
});
socket.addEventListener("close", () => {
if (!settled) {
fail(new Error(`Workspace backend WebSocket closed before ${method} completed`));
}
});
});
}
function record(value: unknown): Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
? value as Record<string, unknown>

View file

@ -69,7 +69,7 @@ describe("patch.moi CLI", () => {
workspaceRoot,
]);
expect(blocked.code).toBe(2);
expect(blocked.stderr).toContain("requires PATCH_WORKSPACE_BACKEND_URL or --allow-local");
expect(blocked.stderr).toContain("requires PATCH_WORKSPACE_BACKEND_URL, CODEX_WORKSPACE_MODE=actions, or --allow-local");
const dryRun = await invoke([
"run",

View file

@ -404,6 +404,41 @@ describe("feed watcher", () => {
});
});
test("workspace dispatch uses actions-local mode without a running backend", async () => {
const dataDir = await mkdtemp(join(tmpdir(), "patch-flow-actions-"));
await writeDemoFlow(dataDir);
const record = await dispatchWorkspaceEvent({
id: "patch:actions:demo",
type: "demo.event",
source: "patch",
receivedAt: "2026-05-15T00:00:00.000Z",
payload: { name: "Grace" },
}, {}, {
cwd: dataDir,
env: {
CODEX_WORKSPACE_MODE: "actions",
},
});
expect(record).toMatchObject({
eventId: "patch:actions:demo",
eventType: "demo.event",
status: "dispatched",
target: "local",
transport: "actions-local",
});
expect(JSON.parse(await readFile(join(dataDir, "local-flow-output.json"), "utf8"))).toEqual({
name: "Grace",
});
const state = JSON.parse(
await readFile(join(dataDir, ".codex/workspace/actions/flow-client/state.json"), "utf8"),
) as { events: Array<{ event: { id: string } }>; runs: Array<{ id: string }> };
expect(state.events.map((entry) => entry.event.id)).toContain("patch:actions:demo");
expect(state.runs.map((entry) => entry.id)).toEqual(record.runIds ?? []);
});
test("Patch upstream release helper creates deterministic product events", () => {
expect(patchUpstreamReleaseEvent({
repo: "openai/codex",

View file

@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import type { FlowRunView } from "@peezy.tech/flow-runtime/client";
import type { FlowRunView } from "@peezy.tech/codex-flows/flow-runtime/client";
import {
maintenanceAttemptForWorkspaceDispatch,
maintenanceAttemptWithWorkspaceRuns,

View file

@ -6,7 +6,7 @@ import {
matchingSteps,
runFlowStep,
type FlowEvent,
} from "@peezy.tech/flow-runtime";
} from "@peezy.tech/codex-flows/flow-runtime";
const workspaceRoot = path.resolve(import.meta.dir, "../../..");
const harnessFork = path.join(workspaceRoot, "harness/fork");

View file

@ -5,15 +5,17 @@
"": {
"name": "@peezy.tech/patch",
"devDependencies": {
"@peezy.tech/codex-flows": "^0.3.4",
"@peezy.tech/flow-runtime": "^0.4.0",
"@peezy.tech/codex-flows": "^0.3.5",
},
},
"apps/patch": {
"name": "@peezy.tech/patch",
"version": "0.1.0",
"bin": {
"patch.moi": "./src/cli.ts",
},
"dependencies": {
"@peezy.tech/flow-runtime": "^0.4.0",
"@peezy.tech/codex-flows": "^0.3.5",
},
"devDependencies": {
"@types/bun": "catalog:",
@ -91,9 +93,7 @@
"@oxc-project/types": ["@oxc-project/types@0.130.0", "", {}, "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q=="],
"@peezy.tech/codex-flows": ["@peezy.tech/codex-flows@0.3.4", "", { "bin": { "codex-flows": "dist/cli/index.js" } }, "sha512-ScggyelL6q9EKxqjrJhvyF7o6A7+naK4GkkB44aF5sSs06fANn1Uw/zN2Qmv3CuCFpSDCeg5I8fL+ro8PQGTFw=="],
"@peezy.tech/flow-runtime": ["@peezy.tech/flow-runtime@0.4.0", "", { "dependencies": { "@peezy.tech/codex-flows": "^0.3.1" } }, "sha512-HJ0witZkQ2l+tzB/bONB+hV4aPmjc0jOOoUu01gPHo2NFJ/qfJ8bMbUe7jdCvfvDCqStr6RZooS2FiF/OLGs6A=="],
"@peezy.tech/codex-flows": ["@peezy.tech/codex-flows@0.3.5", "", { "bin": { "codex-flows": "dist/cli/index.js", "codex-app": "dist/bin/codex-app.js", "codex-flow-runner": "dist/bin/codex-flow-runner.js", "codex-workspace-backend-local": "dist/bin/codex-workspace-backend-local.js" } }, "sha512-axKyRFn8H7MPM2VD6YEKu1LqnVvjiX9WjNRxnZNwsAf3n8kCofSiXP5p9l0RdKCB2ASex3AgdyboROkWc6zC6g=="],
"@peezy.tech/patch": ["@peezy.tech/patch@workspace:apps/patch"],
@ -873,8 +873,6 @@
"@apidevtools/json-schema-ref-parser/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
"@peezy.tech/flow-runtime/@peezy.tech/codex-flows": ["@peezy.tech/codex-flows@0.3.3", "", { "bin": { "codex-flows": "dist/cli/index.js" } }, "sha512-crhGJ8YsdqxKoqpulkKprIz+rCeULiQ6HmsDZIec4NJPg04AVv6PXmPJPHXlRJN8DhdDAomRZ8Codm0nuRObhQ=="],
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"hast-util-raw/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],

View file

@ -38,13 +38,25 @@ to identify the upstream update. The receiving workspace still reads Git and
forge state to discover the maintained branch, patch commits, candidate refs,
and current checks.
## Workspace Backends
## Flow Execution Surfaces
When `PATCH_WORKSPACE_BACKEND_URL` is unset, patch.moi uses local flow
execution from the process working directory. When it is set to an HTTP or
WebSocket URL, patch.moi dispatches to that Codex workspace backend. In both
cases patch.moi writes its own dispatch and maintenance-attempt records under
`DATA_DIR`.
patch.moi supports the codex-flows flow surfaces without requiring any one of
them to be running on this checkout:
| Surface | How patch.moi selects it | Run state |
| --- | --- | --- |
| synchronous local | no backend URL and no Actions mode | in-process local client state |
| Actions/local | `CODEX_WORKSPACE_MODE=actions` or `GITHUB_ACTIONS=true` and no backend URL | `.codex/workspace/actions/flow-client` |
| workspace HTTP | `PATCH_WORKSPACE_BACKEND_URL`, `PATCH_FLOW_BACKEND_URL`, or `PATCH_FLOW_DISPATCH_URL` is an HTTP URL | backend-owned |
| workspace WebSocket | configured workspace URL starts with `ws://` or `wss://` | backend-owned |
The Actions/local surface is the no-running-backend path for semi-autonomous
fork maintenance in CI. A persistent workspace backend remains optional: use it
when a host, service, or gateway needs durable run inspection, app-server
pass-through, or remote dispatch/replay control.
In every case patch.moi writes its own dispatch and maintenance-attempt records
under `DATA_DIR`.
Workspace backend run state is useful for inspection and sync. It is not the
authoritative patch.moi product state.
@ -66,8 +78,8 @@ local harness path. The repository also includes an explicit manual
bun run workspace:run:harness-flow
```
That flow task requires a running workspace backend URL. In
`@peezy.tech/codex-flows@0.3.4`, workspace-owned flow tasks synthesize unique
That flow task requires a running workspace backend URL for the repo-native
workspace automation command. In codex-flows, workspace-owned flow tasks synthesize unique
`id`, `occurredAt`, and `receivedAt` fields for every run. Those ids are useful
for workspace automation, but patch.moi must not use workspace-generated ids
for feed-owned maintenance attempts.

View file

@ -53,8 +53,15 @@ Verify Codex release flow matching without executing release work:
bun run patch.moi -- run codex-release --tag rust-v0.130.0 --dry-run
```
Dispatching the Codex release task requires a configured workspace backend by
default:
Dispatching the Codex release task requires an explicit execution surface. Use
Actions/local mode when no workspace backend is running:
```bash
CODEX_WORKSPACE_MODE=actions \
bun run patch.moi -- run codex-release --tag rust-v0.130.0
```
Or point at a workspace backend:
```bash
PATCH_WORKSPACE_BACKEND_URL=ws://127.0.0.1:3586 \
@ -63,7 +70,7 @@ bun run patch.moi -- run codex-release --tag rust-v0.130.0
Use `--allow-local` only when you intentionally want the local Patch process to
execute matching Codex release flows. The Code Mode step still requires its own
`CODEX_FLOWS_ENABLE_CODE_MODE=1` gate.
`CODEX_FLOWS_MODE=code-mode` gate.
## Status

View file

@ -18,7 +18,10 @@ description: Runtime environment variables used by patch.moi.
| `PATCH_FLOW_DISPATCH_URL` | unset | Legacy or explicit flow dispatch URL fallback. |
| `PATCH_FLOW_DISPATCH_SECRET` | unset | Legacy HMAC secret fallback. |
| `PEEZY_CODEX_REPO` | `../codex` | Optional Codex checkout path for `patch.moi setup codex`. |
| `CODEX_FLOWS_ENABLE_CODE_MODE` | unset | Required by Code Mode flow steps before local Code Mode execution. |
| `CODEX_WORKSPACE_MODE` | unset | Set to `actions` to use codex-flows Actions/local flow state when no workspace backend URL is configured. |
| `GITHUB_ACTIONS` | unset | When `true`, also selects the Actions/local no-backend flow surface. |
| `CODEX_FLOWS_MODE` | unset | Set to `code-mode` when Code Mode flow steps should run. |
| `CODEX_FLOWS_ENABLE_CODE_MODE` | unset | Legacy narrow gate accepted by Code Mode flow steps. |
| `CODEX_APP_SERVER_CODEX_COMMAND` | unset | Passed to local code-mode flow execution. |
| `CODEX_HOME` | unset | Passed to local code-mode flow execution. |
@ -30,4 +33,4 @@ Git topology is intentionally not represented here. Local mode should read
upstream, fork, branch, and tag state from Git. Service mode should read remote
repository, branch, workflow, and review state from the forge. Environment
variables should stay limited to runtime concerns such as workspace backend
URLs, data directories, and Codex execution settings.
URLs, data directories, workspace mode, and Codex execution settings.

View file

@ -35,10 +35,13 @@ bun run docs:build
## Root Workspace
The repository root owns shared scripts and dev dependencies. It installs
`@peezy.tech/codex-flows` so repo-native workspace commands are available:
The repository root owns shared scripts and dev dependencies. It installs the
current `@peezy.tech/codex-flows` package surface so repo-native workspace,
flow, app-server, and backend commands are available:
```bash
bun run flow:list
bun run workspace:backend
bun run workspace:doctor
bun run workspace:tick
bun run workspace:run:harness
@ -62,17 +65,15 @@ codex-flows pack doctor --json
not patch.moi product state. patch.moi still records feed-owned flow events,
workspace dispatches, and maintenance attempts under `DATA_DIR`.
## Related Runtime Packages
## Related Runtime Package
These published packages define the current patch.moi integration baseline:
patch.moi uses the consolidated codex-flows package surface:
| Package | Published version | patch.moi use |
| --- | --- | --- |
| `@peezy.tech/codex-flows` | `0.3.4` | repo-native workspace commands and CLI automation |
| `@peezy.tech/flow-runtime` | `0.4.0` | local flow discovery, matching, execution, and Bun flow helpers |
| `@peezy.tech/flow-backend-convex` | `0.4.0` | optional generic durable flow backend for future service experiments |
| `@peezy.tech/codex-flows` | `^0.3.5` | flow runtime, Bun flow helpers, workspace backend protocol/client, Actions/local flow state, CLI automation, and backend bins |
patch.moi product state still belongs in the Patch service JSONL store by
default. `flow-backend-convex` should be considered only when patch.moi needs a
generic durable flow event/run backend; it is not the default home for feed
signals, workspace dispatch records, or maintenance attempts.
default. Generic flow backend state is execution/run state. It is useful for
inspection and sync, but it is not the default home for feed signals, workspace
dispatch records, or maintenance attempts.

View file

@ -48,15 +48,15 @@ codex-flows pack doctor --json
```
The current local install pins `openai-codex-bindings` and `peezy-codex-fork`
in `.codex/pack-lock.json`. `@peezy.tech/flow-runtime@0.4.0` discovers
installed `.codex/flows/*` before source-owned `flows/*`, so the installed
Codex capabilities are visible to patch.moi while the harness remains a
source-owned repo flow.
in `.codex/pack-lock.json`. The codex-flows runtime discovers installed
`.codex/flows/*` before source-owned `flows/*`, so the installed Codex
capabilities are visible to patch.moi while the harness remains a source-owned
repo flow.
Safe local verification stops at event matching and runner gating. The test
suite confirms that a stored `upstream.release` event for `openai/codex`
selects both installed Codex release steps, and that the Code Mode step still
requires `CODEX_FLOWS_ENABLE_CODE_MODE=1`. Do not fabricate a full
requires `CODEX_FLOWS_MODE=code-mode`. Do not fabricate a full
`openai/codex` release lifecycle just to exercise the flow.
You can run the same safe match check through the CLI:
@ -65,7 +65,23 @@ You can run the same safe match check through the CLI:
bun run patch.moi -- run codex-release --tag rust-v0.130.0 --dry-run
```
## 3. Point Patch at a workspace backend
## 3. Pick an execution surface
Patch does not require a Codex workspace backend to be running in this checkout.
For CI-style no-backend maintenance, select the codex-flows Actions/local
surface:
```bash
CODEX_WORKSPACE_MODE=actions \
DATA_DIR=./data \
bun run patch.moi -- run codex-release --tag rust-v0.130.0
```
That writes flow run state under `.codex/workspace/actions/flow-client` and
patch.moi product state under `DATA_DIR`.
For a service or host-owned execution surface, point Patch at a workspace
backend:
```bash
PATCH_WORKSPACE_BACKEND_URL=http://127.0.0.1:3586 \
@ -81,7 +97,7 @@ workspace flow capability. `PATCH_FLOW_BACKEND_URL` and
`PATCH_FLOW_DISPATCH_URL` remain accepted for older feed targets.
Leave `PATCH_WORKSPACE_BACKEND_URL` unset only when you intentionally want local
flow execution from the Patch process working directory.
or Actions/local flow execution from the Patch process working directory.
## 4. Inspect the stored event

View file

@ -1,6 +1,6 @@
import path from "node:path";
import { defineBunFlow } from "@peezy.tech/flow-runtime/bun";
import type { FlowResult, FlowResultStatus, FlowRunContext } from "@peezy.tech/flow-runtime";
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"] & {

View file

@ -36,13 +36,14 @@
"patch.moi": "bun run --filter @peezy.tech/patch patch.moi",
"start": "bun run --filter @peezy.tech/patch start",
"test": "bun run --filter @peezy.tech/patch test",
"flow:list": "codex-flow-runner --cwd . list",
"workspace:backend": "codex-workspace-backend-local serve --cwd . --local-app-server",
"workspace:doctor": "codex-flows workspace doctor --workspace-root .",
"workspace:run:harness": "codex-flows workspace run harness-maintenance-fixture --workspace-root . --mode local",
"workspace:run:harness-flow": "codex-flows workspace run harness-maintenance-flow-smoke --workspace-root . --mode local",
"workspace:tick": "codex-flows workspace tick --workspace-root . --mode local"
},
"devDependencies": {
"@peezy.tech/codex-flows": "^0.3.4",
"@peezy.tech/flow-runtime": "^0.4.0"
"@peezy.tech/codex-flows": "^0.3.5"
}
}