Skip to content

Commit 07dace0

Browse files
refactor(cli): split jobs command adapters
1 parent 58b2bd9 commit 07dace0

14 files changed

Lines changed: 239 additions & 218 deletions

File tree

docs/intentional-architecture-rewrite-2026-06-27/decision-log.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ Setup provider fix-command execution is explicitly wired at setup command regist
6969

7070
Automation command adapters are split by user-facing verb. `src/edges/cli/commands/automation/install.ts`, `uninstall.ts`, and `status.ts` each own only their command option shape and service request conversion. Automation rendering lives in `src/edges/cli/commands/automation/render.ts`. The deleted `automation.ts` and `automation-render.ts` catchalls should not return.
7171

72+
Jobs command adapters are split by user-facing verb group. `src/edges/cli/commands/jobs/read.ts` owns list/show request shaping, `logs.ts` owns logs/attach request shaping, and `cancel.ts` owns cancel request shaping. Jobs rendering and formatting live in `render.ts` and `format.ts` under the same folder. The deleted `jobs.ts`, `jobs-render.ts`, and `jobs-format.ts` catchalls should not return.
73+
7274
### Transcript file mechanics are platform, sync eligibility is service
7375

7476
Claude and Codex transcript-store scanning, raw transcript snapshot reads, line counting, and JSONL timestamp extraction belong under `src/platform/transcripts/`. The sync service owns quiet-window eligibility, ledger reconciliation, cursor decisions, and Absorb handoff over typed transcript candidates and snapshots.

docs/intentional-architecture-rewrite-2026-06-27/status.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Branch: `codex/intentional-architecture-rewrite`
55

66
## Current State
77

8-
The branch has more than 280 committed rewrite commits past `dev`. The worklog records 270 production slices so far.
8+
The branch has more than 280 committed rewrite commits past `dev`. The worklog records 271 production slices so far.
99

1010
The diff is broad: more than 490 files changed, with tens of thousands of lines reshaped.
1111

@@ -127,6 +127,7 @@ This is no longer a small cleanup branch. It is a real ownership rewrite.
127127
- Split lifecycle CLI operation adapters into owner-named init, Absorb, Garden, and render files under `src/edges/cli/commands/operations/`, deleting the old command catchall and render compatibility paths.
128128
- Moved setup provider-fix runtime wiring to setup command registration, so setup agent-choice UI consumes an explicit runner instead of importing platform runtime fallback mechanics.
129129
- Split automation CLI command adapters into install, uninstall, status, and render owner files, deleting the old automation command/render catchalls.
130+
- Split jobs CLI command adapters into read, logs, cancel, render, and format owner files, deleting the old jobs command/render/format catchalls.
130131
- Moved the provider-neutral operation spec contract into `src/shared/operation-spec.ts`, so lifecycle services build specs, job stores persist them, and provider adapters execute them without stores or providers importing lifecycle service internals.
131132
- Moved worker-lock and sync-lock process ownership/liveness facts out of stores; stores now persist lock files over injected owner PID and liveness contracts while CLI/worker edges provide platform process probes.
132133
- Moved repeated store atomic-write temp-file mechanics into `src/stores/atomic-write.ts`, removing process-PID temp names from job and sync stores.
@@ -145,12 +146,12 @@ This is no longer a small cleanup branch. It is a real ownership rewrite.
145146

146147
## Latest Checkpoint
147148

148-
The latest slice split automation CLI command adapters by verb. Install, uninstall, status, and automation rendering now live under `src/edges/cli/commands/automation/`; the old `src/edges/cli/commands/automation.ts` and `automation-render.ts` catchalls are gone.
149+
The latest slice split jobs CLI command adapters by verb. List/show, logs/attach, cancel, jobs rendering, and jobs formatting now live under `src/edges/cli/commands/jobs/`; the old `src/edges/cli/commands/jobs.ts`, `jobs-render.ts`, and `jobs-format.ts` catchalls are gone.
149150

150151
Verification passed:
151152

152153
- `npm run lint`
153-
- `npx vitest run test/architecture-automation-update-boundaries.test.ts test/architecture-lifecycle-provider-boundaries.test.ts test/automation.test.ts`
154+
- `npx vitest run test/architecture-jobs-sync-boundaries.test.ts test/jobs-command.test.ts`
154155
- `npx vitest run test/architecture-*-boundaries.test.ts`
155156
- `git diff --check`
156157
- `npm test`
@@ -175,7 +176,7 @@ Previous full-slice verification also passed:
175176

176177
## Immediate Next Work
177178

178-
Continue top-down subsystem passes before small leak cleanup. The major loose source buckets for jobs, init, config, wiki, viewer read models, worker entrypoints, serve process lifetime, setup/uninstall terminal UI, wiki file mechanics, automation scheduler mechanics, automation scheduler app composition, automation command adapter ownership, setup instruction runtime composition, provider setup-view ownership, setup provider-fix runtime composition, job provider-runner composition, job-worker process spawning, Absorb source resolver composition, Absorb source contract ownership, prompt loader mechanics, update runtime composition, update notifier ownership, setup runtime composition, sync transcript runtime composition, sync-to-job session lookup, CLI app composition, diagnostic fact contracts, provider-neutral agent runtime contracts, lock process-liveness contracts, operation-spec type ownership, operation-output type ownership, path-equality contract ownership, init prompt-context ownership, config command validation ownership, store atomic-write ownership, review command markdown ownership, lifecycle workflow type ownership, lifecycle workflow verb ownership, lifecycle command adapter verb ownership, path construction ownership, shared helper-contract ownership, wiki command target resolution, cross-wiki health coordination, job service view ownership, and lifecycle/job starter result ownership have now been removed or assigned. Remaining candidates include command files that still own workflow decisions, remaining platform modules that read config/store state directly, lifecycle/job boundary duplication that remains after the big moves, and large files whose size may still reflect mixed ownership.
179+
Continue top-down subsystem passes before small leak cleanup. The major loose source buckets for jobs, init, config, wiki, viewer read models, worker entrypoints, serve process lifetime, setup/uninstall terminal UI, wiki file mechanics, automation scheduler mechanics, automation scheduler app composition, automation command adapter ownership, jobs command adapter ownership, setup instruction runtime composition, provider setup-view ownership, setup provider-fix runtime composition, job provider-runner composition, job-worker process spawning, Absorb source resolver composition, Absorb source contract ownership, prompt loader mechanics, update runtime composition, update notifier ownership, setup runtime composition, sync transcript runtime composition, sync-to-job session lookup, CLI app composition, diagnostic fact contracts, provider-neutral agent runtime contracts, lock process-liveness contracts, operation-spec type ownership, operation-output type ownership, path-equality contract ownership, init prompt-context ownership, config command validation ownership, store atomic-write ownership, review command markdown ownership, lifecycle workflow type ownership, lifecycle workflow verb ownership, lifecycle command adapter verb ownership, path construction ownership, shared helper-contract ownership, wiki command target resolution, cross-wiki health coordination, job service view ownership, and lifecycle/job starter result ownership have now been removed or assigned. Remaining candidates include command files that still own workflow decisions, remaining platform modules that read config/store state directly, lifecycle/job boundary duplication that remains after the big moves, and large files whose size may still reflect mixed ownership.
179180

180181
## Decision Log
181182

docs/intentional-architecture-rewrite-2026-06-27/worklog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,3 +1919,12 @@ Two-hundred-seventieth production slice:
19191919
- Added owner-named automation command files for install, uninstall, status, and rendering under `src/edges/cli/commands/automation/`.
19201920
- Updated automation command registration and tests to import the verb-owned adapters directly.
19211921
- Strengthened automation boundary tests so the old automation command and render catchalls cannot return.
1922+
1923+
Two-hundred-seventy-first production slice:
1924+
1925+
- Deleted the catchall `src/edges/cli/commands/jobs.ts` command adapter.
1926+
- Deleted the catchall `src/edges/cli/commands/jobs-render.ts` renderer and `jobs-format.ts` formatter.
1927+
- Added owner-named jobs command files for read, logs, cancel, render, and format under `src/edges/cli/commands/jobs/`.
1928+
- Removed the unused non-stream attach compatibility helper while preserving the real streaming attach command path.
1929+
- Updated jobs command registration and tests to import the verb-owned adapters directly.
1930+
- Strengthened jobs/sync boundary tests so the old jobs command, render, and format catchalls cannot return.

src/edges/cli/commands/jobs.ts

Lines changed: 0 additions & 159 deletions
This file was deleted.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { cancelJob } from "../../../../services/jobs/index.js";
2+
import type { CancelJobRequest } from "../../../../services/jobs/index.js";
3+
import {
4+
renderCancelJobResult,
5+
type JobsCommandResult,
6+
} from "./render.js";
7+
8+
export interface JobCancelCommandOptions {
9+
cwd: string;
10+
jobId: string;
11+
json?: boolean;
12+
now?: () => Date;
13+
signalProcess: (pid: number, signal: NodeJS.Signals) => void;
14+
}
15+
16+
export async function runJobsCancel(
17+
options: JobCancelCommandOptions,
18+
): Promise<JobsCommandResult> {
19+
return renderCancelJobResult(
20+
await cancelJob(toCancelJobRequest(options)),
21+
options.json,
22+
);
23+
}
24+
25+
function toCancelJobRequest(
26+
options: JobCancelCommandOptions,
27+
): CancelJobRequest {
28+
return {
29+
cwd: options.cwd,
30+
jobId: options.jobId,
31+
now: options.now,
32+
signalProcess: options.signalProcess,
33+
};
34+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { formatTextTable } from "./table.js";
2-
import type { JobServiceView } from "../../../services/jobs/index.js";
1+
import { formatTextTable } from "../table.js";
2+
import type { JobServiceView } from "../../../../services/jobs/index.js";
33

44
export function formatJobRows(views: JobServiceView[]): string[] {
55
return formatTextTable({
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {
2+
readJobLog,
3+
streamJobLog,
4+
} from "../../../../services/jobs/index.js";
5+
import type {
6+
JobLogRequest,
7+
StreamJobLogRequest,
8+
} from "../../../../services/jobs/index.js";
9+
import {
10+
renderJobLog,
11+
renderStreamJobLogResult,
12+
type JobsCommandResult,
13+
} from "./render.js";
14+
15+
export interface JobLogCommandOptions {
16+
cwd: string;
17+
jobId: string;
18+
json?: boolean;
19+
}
20+
21+
export interface JobAttachStreamCommandOptions {
22+
cwd: string;
23+
jobId: string;
24+
json?: boolean;
25+
now?: () => Date;
26+
isPidAlive: (pid: number) => boolean;
27+
write: (chunk: string) => void;
28+
pollMs?: number;
29+
}
30+
31+
export async function runJobsLogs(
32+
options: JobLogCommandOptions,
33+
): Promise<JobsCommandResult> {
34+
const result = await readJobLog(toJobLogRequest(options));
35+
return renderJobLog(result, options.json);
36+
}
37+
38+
export async function streamJobsAttach(
39+
options: JobAttachStreamCommandOptions,
40+
): Promise<JobsCommandResult> {
41+
const result = await streamJobLog(toStreamJobLogRequest(options));
42+
return renderStreamJobLogResult(result, options.json, options.write);
43+
}
44+
45+
function toJobLogRequest(options: JobLogCommandOptions): JobLogRequest {
46+
return {
47+
cwd: options.cwd,
48+
jobId: options.jobId,
49+
};
50+
}
51+
52+
function toStreamJobLogRequest(
53+
options: JobAttachStreamCommandOptions,
54+
): StreamJobLogRequest {
55+
return {
56+
cwd: options.cwd,
57+
jobId: options.jobId,
58+
write: options.write,
59+
pollMs: options.pollMs,
60+
now: options.now,
61+
isPidAlive: options.isPidAlive,
62+
};
63+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { listJobs, readJob } from "../../../../services/jobs/index.js";
2+
import type {
3+
JobRequest,
4+
JobsRequest,
5+
} from "../../../../services/jobs/index.js";
6+
import {
7+
renderJobsListResult,
8+
renderJobsShowResult,
9+
type JobsCommandResult,
10+
} from "./render.js";
11+
12+
export interface JobsListCommandOptions {
13+
cwd: string;
14+
json?: boolean;
15+
now?: () => Date;
16+
isPidAlive: (pid: number) => boolean;
17+
}
18+
19+
export interface JobByIdCommandOptions {
20+
cwd: string;
21+
jobId: string;
22+
json?: boolean;
23+
now?: () => Date;
24+
isPidAlive: (pid: number) => boolean;
25+
}
26+
27+
export async function runJobsList(
28+
options: JobsListCommandOptions,
29+
): Promise<JobsCommandResult> {
30+
return renderJobsListResult(
31+
await listJobs(toJobsRequest(options)),
32+
options.json,
33+
);
34+
}
35+
36+
export async function runJobsShow(
37+
options: JobByIdCommandOptions,
38+
): Promise<JobsCommandResult> {
39+
return renderJobsShowResult(await readJob(toJobRequest(options)), options.json);
40+
}
41+
42+
function toJobsRequest(options: JobsListCommandOptions): JobsRequest {
43+
return {
44+
cwd: options.cwd,
45+
now: options.now,
46+
isPidAlive: options.isPidAlive,
47+
};
48+
}
49+
50+
function toJobRequest(options: JobByIdCommandOptions): JobRequest {
51+
return {
52+
cwd: options.cwd,
53+
jobId: options.jobId,
54+
now: options.now,
55+
isPidAlive: options.isPidAlive,
56+
};
57+
}

0 commit comments

Comments
 (0)