@mingchuno/agent-workflows 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,9 +52,10 @@ agent-workflows monitor
52
52
  agent-workflows status --json
53
53
  ```
54
54
 
55
- Alternatively, load database, hosting and application variables from one explicit
56
- file: `agent-workflows --env-file ./runner.env run`. Existing shell values win,
57
- including empty strings. See [environment file examples and boundaries](docs/configuration.md#cli-environment-files).
55
+ Alternatively, set `"envFile": "./runner.env"` at the top level of
56
+ `agent-workflows.json` to load database, hosting and application variables from
57
+ one explicit file. Existing process values win, including empty strings. See
58
+ [environment file examples and boundaries](docs/configuration.md#cli-environment-files).
58
59
 
59
60
  The runner fetches the configured base, creates a branch, implements an eligible issue, validates it, generates publication text, commits and pushes, creates a draft PR/MR, and publishes an independent review of its exact head. It never merges. Initial use should target a repository and issue you explicitly intend to automate; running the CLI authorizes these effects and agent usage.
60
61
 
@@ -76,6 +77,24 @@ await runner.shutdown();
76
77
 
77
78
  [Custom workflow](examples/custom-workflow.ts), [complete runner](examples/run.ts), [configuration](examples/config.ts), and [inspection](examples/observe.ts) examples are type-checked with the library. The custom workflow is also exercised using controlled providers.
78
79
 
80
+ ## Documentation
81
+
82
+ ### Using the package
83
+
84
+ - [Configuration and profiles](docs/configuration.md)
85
+ - [Default stage prompts](docs/configuration.md#default-stage-prompts)
86
+ - [Public SDK API and composition](docs/api.md)
87
+ - [DBOS SDK direct usage](docs/api.md#dbos-sdk-direct-usage)
88
+ - [Authentication and provider capabilities](docs/providers.md)
89
+ - [CLI, TUI, observability, and recovery](docs/operations.md)
90
+ - [Observability landscape](docs/operations.md#observability-landscape)
91
+
92
+ ### Maintaining the project
93
+
94
+ - [Architecture decisions](docs/adr/README.md)
95
+ - [Database maintenance](docs/database.md)
96
+ - [Release process](docs/releases.md)
97
+
79
98
  ## Development and review
80
99
 
81
100
  [mise](https://mise.jdx.dev/getting-started.html) pins the development Node and pnpm versions. Activate it in your shell or prefix commands with `mise exec --`.
@@ -92,14 +111,6 @@ Use `pnpm start --help` to run the CLI from source. `pnpm build` emits the runti
92
111
 
93
112
  `pnpm test` builds the application, then starts and removes a disposable real PostgreSQL database using `initdb`, `pg_ctl`, and `createdb` on PATH. Alternatively, set `TEST_DATABASE_URL` to a disposable database whose role can create test databases. Tests use real temporary Git repositories and controlled adapters/HTTP servers; they make no paid agent calls or writes to real hosting providers.
94
113
 
95
- Schema changes and database upgrades: [database maintenance](docs/database.md). Biome formats and lints supported source/configuration files; Markdown and YAML are maintained manually.
96
-
97
- - [Configuration and profiles](docs/configuration.md)
98
- - [Default stage prompts](docs/configuration.md#default-stage-prompts)
99
- - [Public SDK API and composition](docs/api.md)
100
- - [DBOS SDK direct usage](docs/api.md#dbos-sdk-direct-usage)
101
- - [Authentication and provider capabilities](docs/providers.md)
102
- - [CLI, TUI, observability and recovery](docs/operations.md)
103
- - [Observability Landscape](docs/operations.md#observability-landscape)
104
- - [Architecture](docs/architecture.md)
105
- - [Releases](docs/releases.md)
114
+ Schema changes and database upgrades: [database maintenance](docs/database.md).
115
+ Biome formats and lints supported source/configuration files; Markdown and YAML
116
+ are maintained manually.
@@ -0,0 +1,2 @@
1
+ import { type Configuration } from "./config.js";
2
+ export declare function readCliConfiguration(path: string, resolveEnvironmentFile: (path: string) => string): Promise<Configuration>;
@@ -0,0 +1,35 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { parseEnv } from "node:util";
3
+ import { z } from "zod";
4
+ import { configSchema } from "./config.js";
5
+ const cliConfigurationSchema = configSchema.extend({
6
+ envFile: z
7
+ .string()
8
+ .refine((path) => path.trim().length > 0, "Environment file path must be nonblank")
9
+ .optional(),
10
+ });
11
+ export async function readCliConfiguration(path, resolveEnvironmentFile) {
12
+ const { envFile, ...configuration } = cliConfigurationSchema.parse(JSON.parse(await readFile(path, "utf8")));
13
+ if (envFile !== undefined)
14
+ await loadEnvironmentFile(resolveEnvironmentFile(envFile));
15
+ return configuration;
16
+ }
17
+ async function loadEnvironmentFile(path) {
18
+ let contents;
19
+ try {
20
+ contents = await readFile(path, "utf8");
21
+ }
22
+ catch (error) {
23
+ throw new Error(`Cannot read environment file ${path} (${error.code ?? "read failed"})`);
24
+ }
25
+ let values;
26
+ try {
27
+ values = parseEnv(contents);
28
+ }
29
+ catch (error) {
30
+ throw new Error(`Cannot parse environment file ${path}`, { cause: error });
31
+ }
32
+ for (const [name, value] of Object.entries(values))
33
+ if (value !== undefined && process.env[name] === undefined)
34
+ process.env[name] = value;
35
+ }
package/dist/src/cli.js CHANGED
@@ -2,13 +2,12 @@
2
2
  import { realpathSync, statSync } from "node:fs";
3
3
  import { readFile, writeFile } from "node:fs/promises";
4
4
  import { basename, dirname, relative, resolve } from "node:path";
5
- import { parseEnv } from "node:util";
6
5
  import { Command } from "commander";
7
6
  import { render } from "ink";
8
7
  import React from "react";
9
8
  import { createAgents } from "./adapters/agents.js";
10
9
  import { createHosting } from "./adapters/hosting.js";
11
- import { configSchema } from "./config.js";
10
+ import { readCliConfiguration } from "./cli-config.js";
12
11
  import { defaultValidationTimeoutMs } from "./defaults.js";
13
12
  import { Runner } from "./runner.js";
14
13
  import { Store } from "./store.js";
@@ -18,27 +17,9 @@ const program = new Command()
18
17
  .name("agent-workflows")
19
18
  .description("Local durable issue-to-review workflows")
20
19
  .option("-c, --config <file>", "configuration path", "agent-workflows.json")
21
- .option("--config-base-directory <directory>", "base directory for paths contained in configuration")
22
- .option("--env-file <path>", "load literal dotenv values; existing environment wins")
23
- .hook("preAction", async () => {
24
- const path = program.opts().envFile;
25
- if (path === undefined)
26
- return;
27
- const file = resolve(launchDirectory, path);
28
- let contents;
29
- try {
30
- contents = await readFile(file, "utf8");
31
- }
32
- catch (error) {
33
- throw new Error(`Cannot read environment file ${file} (${error.code ?? "read failed"})`);
34
- }
35
- for (const [name, value] of Object.entries(parseEnv(contents))) {
36
- if (process.env[name] === undefined)
37
- process.env[name] = value;
38
- }
39
- });
20
+ .option("--config-base-directory <directory>", "base directory for paths contained in configuration");
40
21
  async function configuration() {
41
- return configSchema.parse(JSON.parse(await readFile(configPath(), "utf8")));
22
+ return readCliConfiguration(configPath(), (path) => resolve(configBaseDirectory(), path));
42
23
  }
43
24
  function configPath() {
44
25
  return resolve(launchDirectory, program.opts().config);
@@ -67,6 +48,9 @@ function databaseUrl(config) {
67
48
  }
68
49
  async function withStore(action) {
69
50
  const config = await configuration();
51
+ await withConfiguredStore(config, action);
52
+ }
53
+ async function withConfiguredStore(config, action) {
70
54
  const store = new Store(databaseUrl(config), config.id);
71
55
  try {
72
56
  await store.initialize();
@@ -210,9 +194,10 @@ program
210
194
  .command("monitor")
211
195
  .option("--notify", "notify when an observed execution reaches an outcome")
212
196
  .action(async (options) => {
197
+ const config = await configuration();
213
198
  if (!process.stdin.isTTY || !process.stdout.isTTY)
214
199
  throw new Error("Monitor requires an interactive terminal; use status --json instead");
215
- await withStore(async (store) => {
200
+ await withConfiguredStore(config, async (store) => {
216
201
  await render(React.createElement(Monitor, {
217
202
  source: store,
218
203
  notificationWriter: options.notify
package/docs/api.md CHANGED
@@ -23,7 +23,9 @@ the next attempt; another request for the same task fails while that retry is
23
23
  queued or running. Replaying the same command ID returns its existing retry,
24
24
  without rechecking the checkout or emitting events. A command ID cannot identify
25
25
  retries of different runs. Retry creation, project unblocking and their events
26
- commit together; failure preserves the blocked state.
26
+ commit together; failure preserves the blocked state. The rationale for separate
27
+ run and execution identities is in
28
+ [ADR 0003](adr/0003-run-and-execution-identity.md).
27
29
 
28
30
  ## Durable operations
29
31
 
@@ -156,7 +158,8 @@ Caveats:
156
158
  `Store.admitRetry` owns persisted retry admission. Runner supplies its checkout
157
159
  and process safety check, which runs under the project lock for new admissions
158
160
  only. This callback must not mutate Store records. Operator tools should use
159
- `retry` commands or `Runner.retry`, preserving those safety checks.
161
+ `retry` commands or `Runner.retry`, preserving those safety checks. The locking
162
+ boundary is recorded in [ADR 0005](adr/0005-postgresql-persistence-boundary.md).
160
163
 
161
164
  Invocation records include project/run IDs, stable DBOS step ID and name, invocation ID, attempt, timestamps, requested/effective profile, provider, effective task prompt/source/hash, output-contract and evidence identities, artifact path and session state (`pending`, `available`, `unavailable`). Repeated custom steps retain separate invocations. A retry has a separate run record linked to its predecessor.
162
165
 
@@ -182,7 +185,8 @@ markers. `Store.recoveryPlan(runId)` reports persisted eligibility and its reaso
182
185
  live safety checks happen at admission and execution. Runs without execution
183
186
  metadata remain readable and retryable, but cannot be recovered.
184
187
 
185
- Recovery uses DBOS forks, retaining the original workflow input and checkpoint
186
- prefix. The accepted command ID is the fork ID: dispatch adopts an existing fork
187
- after an uncertain response or crash. Copied start gates do not replace live
188
- checks in the first non-replayed operation. Successful prefixes cannot rerun.
188
+ Recovery preserves the original workflow input and completed checkpoint prefix.
189
+ The accepted command ID identifies the new execution so uncertain dispatch can
190
+ be reconciled after a crash. Live checks still run in the first non-replayed
191
+ operation. See [ADR 0003](adr/0003-run-and-execution-identity.md) for the complete
192
+ identity and recovery decision.
@@ -15,6 +15,9 @@ and is canonicalized before startup.
15
15
  | `stateDirectory` | `.agent-workflows`; relative to the configuration base and outside every managed checkout |
16
16
  | `projects` | Nonempty array; duplicate IDs or canonical checkout roots are rejected |
17
17
 
18
+ The JSON file also accepts optional CLI-only `envFile`; it is intentionally not
19
+ part of the public SDK `Configuration` type.
20
+
18
21
  | Project field | Default / meaning |
19
22
  | ---------------------- | --------------------------------------------------------------------------------------------------- |
20
23
  | `id`, `checkout` | Required stable identity and existing Git repository root; relative to the configuration base |
@@ -43,14 +46,17 @@ is the relative sibling `<checkout-name>.agent-workflows`.
43
46
 
44
47
  ## CLI environment files
45
48
 
46
- Select one file explicitly for any CLI command:
49
+ Set the optional top-level `envFile` property to load one file for every CLI
50
+ command that reads the configuration:
47
51
 
48
- ```sh
49
- agent-workflows --env-file ./runner.env run
50
- agent-workflows --env-file ./runner.env status --json
51
- agent-workflows --env-file /absolute/path/runner.env monitor
52
+ ```json
53
+ "envFile": "./runner.env"
52
54
  ```
53
55
 
56
+ Add this alongside the other top-level properties in `agent-workflows.json`.
57
+ `envFile` is a CLI-file setting, not part of the public SDK `Configuration`.
58
+ SDK callers continue to prepare their own process environment.
59
+
54
60
  Example `runner.env` (replace placeholders locally):
55
61
 
56
62
  ```dotenv
@@ -65,16 +71,19 @@ APP_REFERENCE='${APP_MODE}'
65
71
  or hosting values still fail existing validation. Missing keys are filled from
66
72
  the file; arbitrary application variable names are supported. `databaseUrlEnv`
67
73
  and `hosting.tokenEnv` still select which names the runner uses.
68
- - Relative paths resolve from the CLI launch directory, independently of
69
- `--config` and project checkouts. Absolute paths work too. No `.env` discovery
70
- or multiple-file layering is performed.
74
+ - Relative paths resolve from the effective configuration base: the directory
75
+ containing the resolved configuration file, or `--config-base-directory`
76
+ when supplied. Absolute paths work too. Omitting `envFile` performs no `.env`
77
+ discovery. Multiple-file layering is not supported.
71
78
  - Parsing uses Node's literal dotenv syntax: quotes and comments are supported;
72
79
  `$NAME`, `${NAME}`, backticks and `$(command)` in values are not expanded or
73
80
  executed. This is not shell sourcing.
74
- - The file is read once before the command action, database access, hosting
75
- adapters or runner creation. Missing or unreadable files stop the command with
76
- a nonzero exit and a path/error code, without printing file contents. Restart
77
- the runner to pick up edits. Help only displays usage and does not load files.
81
+ - The configuration is validated before its environment file can be discovered.
82
+ The file is then read once before database access, hosting adapters or runner
83
+ creation. Missing, unreadable or invalid files stop the command with a nonzero
84
+ exit without printing file contents. Restart the runner to pick up edits.
85
+ `init` creates a configuration without `envFile`; help and `init` do not load
86
+ an environment file.
78
87
  - The merged environment is shared across all projects in the runner. Validation,
79
88
  Git and agent worker subprocesses inherit it. Provider runtimes may apply their
80
89
  own environment policies to tools they launch; see [providers](providers.md).
@@ -88,12 +97,13 @@ redaction remains in effect; arbitrary variable support does not classify every
88
97
  application value as a secret. A GitHub API token does not configure Git push
89
98
  credentials.
90
99
 
91
- The flag belongs to `agent-workflows`, not the separate `pnpm db:migrate` command.
92
- SDK callers load their own process environment before creating a runner and
93
- continue passing `databaseUrl` explicitly. Node startup-only settings, such as
94
- `NODE_EXTRA_CA_CERTS`, must be set before launching Node to affect the CLI process.
95
- When invoking the script directly through Node, separate Node arguments from
96
- application arguments: `node -- dist/src/cli.js --env-file ./runner.env status`.
100
+ The setting belongs to the CLI configuration file, not the separate
101
+ `pnpm db:migrate` command. SDK callers load their own process environment before
102
+ creating a runner and continue passing `databaseUrl` explicitly. The setting's
103
+ path and contents are not part of publication-recovery fingerprints, so
104
+ credential rotation does not invalidate recovery. Node startup-only settings,
105
+ such as `NODE_EXTRA_CA_CERTS`, must be set before launching Node to affect the
106
+ CLI process.
97
107
 
98
108
  ## Profiles
99
109
 
package/docs/database.md CHANGED
@@ -15,21 +15,5 @@ Do not edit applied migration files or use schema push against existing data. Ad
15
15
 
16
16
  The initial migration adopts the original application's identical tables using `IF NOT EXISTS`, preserving records and constraints. This supports the original schema, not arbitrary manually altered schemas; inspect and reconcile any local schema changes first.
17
17
 
18
- ## Query boundary
19
-
20
- `Store` uses typed Drizzle inserts, updates and selects. Concurrent JSON record patches use a transaction and row lock to preserve unrelated fields. Full-scope run and invocation queries sort their JSON fields in memory; introduce typed indexed columns if history size requires database pagination.
21
-
22
- Retry admission locks the project row before checking task history, then commits
23
- the new run, project unblocking and admission events in one transaction. The
24
- project lock serializes requests even when they target different historical
25
- attempts. Runner's checkout/process safety check runs while that lock is held;
26
- command replay skips it and does not write new events.
27
-
28
- `src/db/locks.ts` contains the only application driver SQL: fixed, parameterized PostgreSQL session-lock calls, which have no Drizzle query-builder equivalent. Generated migration SQL and the frozen legacy-schema test fixture are intentional SQL artifacts. No interpolated SQL template strings are used for record access.
29
-
30
- Execution history is stored in the existing run JSON record. Publication recovery
31
- adds optional fields without changing SQL tables; no migration or backfill is
32
- required. Legacy records remain readable, but lack the evidence needed for
33
- recovery. Recovery admission atomically appends an execution, queues the same
34
- run and writes an event under the project/task locks used by retry admission.
35
- The persisted execution ID lets dispatch reconcile a DBOS fork across crashes.
18
+ The persistence and locking rationale is recorded in
19
+ [ADR 0005](adr/0005-postgresql-persistence-boundary.md).
@@ -93,7 +93,7 @@ or `unavailable`. Noninteractive tools use `status --json` and `inspect`. Run
93
93
  details keeps complete errors under Diagnostics and exact identifiers, paths,
94
94
  profiles and ISO timestamps under Technical details.
95
95
 
96
- ## Observability Landscape
96
+ ## Observability landscape
97
97
 
98
98
  This section includes only options that run locally without a license key.
99
99
  Cloud services and tools requiring a license key are excluded. These boundaries
@@ -121,38 +121,30 @@ Conductor service or cloud login. See the [DBOS CLI reference](https://docs.dbos
121
121
  The initial DBOS workflow ID equals the run ID. Publication recovery keeps the
122
122
  run ID and adds a new DBOS execution ID; `inspect RUN` includes execution history
123
123
  and a persisted recovery eligibility assessment. Admission performs live checks.
124
- Inspect both layers: the
125
- runner catches execution errors and persists application outcomes, so a DBOS
126
- `SUCCESS` can accompany an application `failed` or `blocked` outcome. DBOS step
127
- history does not replace the local agent/validation artifacts.
128
-
129
- A browser dashboard is an extension path, not a bundled feature. A local server
130
- could combine the [Store query/event API](api.md#query-and-event-interface) with
131
- `DBOSClient.create({ systemDatabaseUrl: databaseUrl })`, joining records by run ID.
132
- Neither inspector needs to launch another DBOS runtime. Keep database access on
133
- the server and bind a local-only dashboard to loopback. See the
134
- [inspection example](../examples/observe.ts) for Store lifecycle handling.
135
-
136
- Route dashboard controls through `Store.request` or the runner's public controls.
137
- Direct DBOS cancellation, resumption or forking bypasses application coordination
138
- for process termination, checkout safety and retry admission.
124
+ Inspect both layers: the runner catches execution errors and persists application
125
+ outcomes, so DBOS `SUCCESS` can accompany an application `failed` or `blocked`
126
+ outcome. DBOS step history does not replace local agent or validation artifacts.
127
+ See [ADR 0003](adr/0003-run-and-execution-identity.md) for the identity model.
139
128
 
140
129
  ## Ownership and recovery
141
130
 
142
- Only the runner may edit or switch managed checkouts while it is active. PostgreSQL advisory locks protect runner/configuration and checkout identities. A local Git-directory lease also prevents runners using different databases from owning the same checkout. Worker/validation process-group journals prevent reuse while old work may still run.
131
+ Only the runner may edit or switch managed checkouts while it is active.
132
+ PostgreSQL advisory locks, a local Git-directory lease, and worker/validation
133
+ process journals prevent concurrent ownership and reuse while old work may still
134
+ run. See [ADR 0002](adr/0002-existing-checkouts-and-exclusive-ownership.md) for
135
+ the checkout and concurrency tradeoff.
143
136
 
144
137
  Git process journals live in `<git-directory>/agent-workflows-processes/`, independently of the configured state directory. A surviving Git process blocks ownership acquisition after a crash. Stop requests propagate to active fetch, staging, commit and push commands; interrupted effects still require reconciliation.
145
138
 
146
139
  Startup and phase boundaries check ownership assumptions, branch/head and actual changes. Unfinished files are never reset, cleaned, stashed or discarded automatically. Dirty files, unresolved Git operations, branch collisions, unexpected mutations and ambiguous agent recovery become inspectable blocked states. Other eligible projects continue.
147
140
 
148
- Publication effects have independent DBOS checkpoints. A task commit preserves
149
- publication trailers, carries exactly one `Agent-Workflows-Run`, and by default
150
- adds co-author trailers for providers with retained writable contributions.
151
- Commit reconciliation checks the expected parent, change set, finalized message,
152
- and clean checkout. Push recovery checks the remote ref, request creation checks
153
- the source branch, and review publication checks stable markers. Transient
154
- publication failures use bounded retries and reconciliation. Interrupted agent
155
- stages block rather than starting another writer.
141
+ Publication effects have independent DBOS checkpoints. A task commit carries
142
+ exactly one `Agent-Workflows-Run` marker and, by default, co-author trailers for
143
+ providers with retained writable contributions. Commit, push, request creation,
144
+ and review publication reconcile their durable identities before retrying an
145
+ ambiguous effect. Interrupted agent stages block rather than starting another
146
+ writer. See [ADR 0003](adr/0003-run-and-execution-identity.md) for the recovery
147
+ boundary.
156
148
 
157
149
  ## Publication recovery
158
150
 
package/docs/providers.md CHANGED
@@ -21,7 +21,7 @@ The runner deliberately does not automatically resume interrupted agent work. Ru
21
21
 
22
22
  ## Environment inheritance
23
23
 
24
- CLI `--env-file` values reach validation commands, Git subprocesses and agent
24
+ CLI `envFile` values reach validation commands, Git subprocesses and agent
25
25
  workers through the runner's process environment. SDK callers get the same
26
26
  inheritance from their own process environment. The installed Codex SDK forwards
27
27
  that environment to its executable; Copilot uses it for its local runtime, with
@@ -58,7 +58,7 @@ In GitHub **Settings → Developer settings → Personal access tokens → Fine-
58
58
 
59
59
  Git push uses the checkout's configured remote and Git credentials independently of this API token. The API adapter does not require **Contents** permission. If you also use this PAT for HTTPS Git pushes, grant **Contents: Read and write**; pushing changes to `.github/workflows/` additionally requires **Workflows: Read and write**. Do not embed credentials in remote URLs.
60
60
 
61
- If access is denied, check the selected owner/repository, PR write permission, token expiration, organization approval, and the token owner's repository access. If you replace the token value, restart the runner with the updated environment; existing environment variables override `--env-file` values. An already failed run requires explicit [recovery](operations.md#ownership-and-recovery); updating permissions does not restart it.
61
+ If access is denied, check the selected owner/repository, PR write permission, token expiration, organization approval, and the token owner's repository access. If you replace the token value, restart the runner with the updated environment; existing environment variables override configured `envFile` values. An already failed run requires explicit [recovery](operations.md#ownership-and-recovery); updating permissions does not restart it.
62
62
 
63
63
  References: GitHub's [PAT creation guide](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens), [issue permissions](https://docs.github.com/en/rest/issues/issues#list-repository-issues), [PR creation permissions](https://docs.github.com/en/rest/pulls/pulls#create-a-pull-request), and [review permissions](https://docs.github.com/en/rest/pulls/reviews#create-a-review-for-a-pull-request).
64
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingchuno/agent-workflows",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Local durable coding workflows on DBOS",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,41 +0,0 @@
1
- # Architecture
2
-
3
- DBOS owns workflow execution, durable steps and concurrency-one project queues. The runner only discovers candidates, dispatches durable identities and handles local operator commands. It does not introduce a workflow language or an interchangeable scheduler.
4
-
5
- - `config.ts` / `domain.ts`: validated configuration, vocabulary and adapter contracts.
6
- - `runner.ts`: local ownership, intake/deduplication, DBOS lifecycle and operator controls.
7
- - `operations.ts`: reusable durable coding operations and the default workflow.
8
- - `prompts.ts` / `invocation.ts`: resolved task text, output contracts and bounded format correction.
9
- - `evidence.ts`: indexed, hashed change artifacts and capture limits.
10
- - `recovery.ts`: publication recovery eligibility, input fingerprints and live safety checks.
11
- - `workspace.ts`: existing-checkout Git operations and change verification.
12
- - `store.ts`: typed Drizzle queries for run, invocation, project, command and event records; `db/schema.ts` and `drizzle/` own the application schema and migrations.
13
- - `adapters/`: provider clients and isolated SDK workers.
14
- - `runtime/`: process groups, ownership journals and redacted logging.
15
- - `cli.ts` / `tui/`: shared command/query interfaces.
16
-
17
- Application records live in `agent_workflows`; DBOS maintains its own execution schema in the same PostgreSQL database. Large streamed agent/validation logs live under the configured state directory, referenced by records. Interrupted or failed agent calls are never automatically retried. A returned response that fails its output contract may receive one fresh inspection-only format-correction attempt within the same stage deadline. Publication retries and explicit recovery reconcile external state first. Clean terminal state and terminal workflow outcome are deliberately separate.
18
-
19
- Node/PostgreSQL/Git are the only runtime infrastructure; providers require their normal local authentication. Zod, Commander, Ink/React, Drizzle/node-postgres, Pino, Octokit and Gitbeaker handle standard infrastructure. Drizzle ORM and Codex SDK are Apache-2.0; the other listed runtime libraries and Copilot SDK are MIT-licensed. Exact dependency versions are pinned by the lockfile. No custom HTTP client, CLI parser or terminal renderer is introduced.
20
-
21
- The test boundary is the public runner/workflow API using real PostgreSQL, real temporary Git repositories and controlled adapters. Separate adapter contracts exercise SDK argument/event mapping and HTTP behavior. Process-level recovery tests terminate a runner after external effects and restart it against the same state. Runtime/provider smoke calls are intentionally separate from deterministic acceptance tests.
22
-
23
- The runner supports existing checkouts only. Higher per-project concurrency requires isolated workspaces and lifecycle design; changing the DBOS queue limit alone is unsafe.
24
-
25
- ## Package boundary
26
-
27
- Keep one package while the SDK, CLI and TUI share a runtime, schema and release cycle. `src/adapters`, `src/runtime`, `src/db` and `src/tui` provide internal boundaries without workspace packages. Split into a monorepo when a separately deployed app or independently versioned package needs its own dependencies and build. `pnpm-workspace.yaml` currently configures installation policy only.
28
-
29
- ## Run and execution identity
30
-
31
- A run owns the branch, commit and publication markers. Its initial DBOS execution
32
- uses the run ID; publication recovery forks the failed execution at its failed
33
- step under a new execution ID, preserving completed checkpoints and the original
34
- run input. Execution history stays in the run record. Fresh retry creates a new
35
- run and branch.
36
-
37
- Recovery admission and retry share a project lock. Admission persists the fork ID
38
- before dispatch so a restarted runner can adopt an existing fork. Recovery gates
39
- run inside the first operation that actually executes, avoiding copied pause and
40
- safety decisions. Recovery is limited to the default workflow's publication
41
- steps; interrupted agents still require manual inspection.