codex-workflow-v2 2.0.0-alpha.1
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 +71 -0
- package/dist/src/artifacts.d.ts +4 -0
- package/dist/src/artifacts.js +106 -0
- package/dist/src/artifacts.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +356 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/contracts.d.ts +313 -0
- package/dist/src/contracts.js +3 -0
- package/dist/src/contracts.js.map +1 -0
- package/dist/src/diagnostics.d.ts +19 -0
- package/dist/src/diagnostics.js +25 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/domain/discovery.d.ts +8 -0
- package/dist/src/domain/discovery.js +25 -0
- package/dist/src/domain/discovery.js.map +1 -0
- package/dist/src/domain/validation.d.ts +3 -0
- package/dist/src/domain/validation.js +96 -0
- package/dist/src/domain/validation.js.map +1 -0
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.js +11 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/fs-utils.d.ts +4 -0
- package/dist/src/fs-utils.js +52 -0
- package/dist/src/fs-utils.js.map +1 -0
- package/dist/src/git.d.ts +19 -0
- package/dist/src/git.js +115 -0
- package/dist/src/git.js.map +1 -0
- package/dist/src/graph.d.ts +28 -0
- package/dist/src/graph.js +263 -0
- package/dist/src/graph.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +13 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/memory.d.ts +9 -0
- package/dist/src/memory.js +242 -0
- package/dist/src/memory.js.map +1 -0
- package/dist/src/migration.d.ts +46 -0
- package/dist/src/migration.js +112 -0
- package/dist/src/migration.js.map +1 -0
- package/dist/src/repository.d.ts +12 -0
- package/dist/src/repository.js +69 -0
- package/dist/src/repository.js.map +1 -0
- package/dist/src/reviewer.d.ts +82 -0
- package/dist/src/reviewer.js +184 -0
- package/dist/src/reviewer.js.map +1 -0
- package/dist/src/state/lock.d.ts +13 -0
- package/dist/src/state/lock.js +100 -0
- package/dist/src/state/lock.js.map +1 -0
- package/dist/src/state/store.d.ts +45 -0
- package/dist/src/state/store.js +185 -0
- package/dist/src/state/store.js.map +1 -0
- package/dist/src/ulid.d.ts +2 -0
- package/dist/src/ulid.js +22 -0
- package/dist/src/ulid.js.map +1 -0
- package/dist/src/version.d.ts +2 -0
- package/dist/src/version.js +3 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/workflow.d.ts +93 -0
- package/dist/src/workflow.js +1276 -0
- package/dist/src/workflow.js.map +1 -0
- package/docs/decisions.md +38 -0
- package/docs/development-flow.md +69 -0
- package/docs/project-memory.md +38 -0
- package/docs/release.md +26 -0
- package/docs/validation-report.md +19 -0
- package/package.json +56 -0
- package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +25 -0
- package/plugins/codex-workflow-gateway/references/protocol.md +66 -0
- package/plugins/codex-workflow-gateway/scripts/install-or-update.sh +26 -0
- package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +72 -0
- package/references/discovery.md +12 -0
- package/references/git-policy.md +7 -0
- package/references/state-machine.md +11 -0
- package/references/validation-and-review.md +13 -0
- package/roles/delivery-coordinator.md +10 -0
- package/roles/independent-reviewer.md +10 -0
- package/roles/scope-lead.md +11 -0
- package/roles/technical-planner.md +10 -0
- package/roles/worker.md +10 -0
- package/schemas/context-envelope.schema.json +58 -0
- package/schemas/discovery.schema.json +19 -0
- package/schemas/graph-binding.schema.json +30 -0
- package/schemas/milestone.schema.json +58 -0
- package/schemas/project-knowledge-map.schema.json +37 -0
- package/schemas/review-result.schema.json +30 -0
- package/schemas/task.schema.json +39 -0
- package/templates/brief.md +21 -0
- package/templates/plan.md +17 -0
- package/templates/result.md +13 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, lstatSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { changedFiles, headCommit, runGit } from './repository.js';
|
|
7
|
+
const REVIEW_SCHEMA = {
|
|
8
|
+
type: 'object',
|
|
9
|
+
additionalProperties: false,
|
|
10
|
+
required: ['status', 'reviewer', 'summary', 'findings', 'isolationProbe'],
|
|
11
|
+
properties: {
|
|
12
|
+
status: { enum: ['passed', 'failed', 'unverified'] },
|
|
13
|
+
reviewer: { type: 'string', minLength: 1 },
|
|
14
|
+
summary: { type: 'string', minLength: 1 },
|
|
15
|
+
isolationProbe: { enum: ['denied', 'written', 'not-attempted'] },
|
|
16
|
+
findings: {
|
|
17
|
+
type: 'array',
|
|
18
|
+
items: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
required: ['id', 'severity', 'requirement', 'summary', 'evidence', 'requiredAction'],
|
|
22
|
+
properties: {
|
|
23
|
+
id: { type: 'string', minLength: 1 },
|
|
24
|
+
severity: { enum: ['critical', 'major', 'minor'] },
|
|
25
|
+
requirement: { type: ['string', 'null'] },
|
|
26
|
+
summary: { type: 'string', minLength: 1 },
|
|
27
|
+
evidence: { type: 'string', minLength: 1 },
|
|
28
|
+
requiredAction: { type: 'string', minLength: 1 },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export function launchStrictReviewer(repositoryRoot, task, taskRoot, envelope, runner = spawnReviewer) {
|
|
35
|
+
const temporary = mkdtempSync(path.join(os.tmpdir(), 'codex-workflow-review-'));
|
|
36
|
+
const schemaFile = path.join(temporary, 'review-result.schema.json');
|
|
37
|
+
const outputFile = path.join(temporary, 'last-message.json');
|
|
38
|
+
writeFileSync(schemaFile, `${JSON.stringify(REVIEW_SCHEMA, null, 2)}\n`, { mode: 0o600 });
|
|
39
|
+
const probe = path.join(repositoryRoot, '.codex-reviewer-write-probe');
|
|
40
|
+
if (existsSync(probe)) {
|
|
41
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
42
|
+
return failedLaunch(repositoryRoot, [], null, 'Reviewer probe path already exists; isolation cannot be tested.');
|
|
43
|
+
}
|
|
44
|
+
const before = sealRepository(repositoryRoot);
|
|
45
|
+
const command = [
|
|
46
|
+
'codex', 'exec', '--ephemeral', '--ignore-user-config', '--ignore-rules', '--sandbox', 'read-only',
|
|
47
|
+
'--output-schema', schemaFile, '--output-last-message', outputFile, '--json', '-C', repositoryRoot, '-',
|
|
48
|
+
];
|
|
49
|
+
const packet = {
|
|
50
|
+
protocol: 'codex-workflow-strict-review-v1',
|
|
51
|
+
instruction: [
|
|
52
|
+
'Act only as the independent reviewer described by the context envelope.',
|
|
53
|
+
'Before reviewing, attempt to create .codex-reviewer-write-probe in the repository.',
|
|
54
|
+
'The attempt must be denied by the sandbox. Report isolationProbe="denied" only when it was denied.',
|
|
55
|
+
'Do not modify any repository file. Return only the requested JSON result.',
|
|
56
|
+
],
|
|
57
|
+
envelope,
|
|
58
|
+
task: {
|
|
59
|
+
id: task.id,
|
|
60
|
+
baseCommit: task.baseCommit,
|
|
61
|
+
headCommit: headCommit(repositoryRoot),
|
|
62
|
+
brief: readFileSync(path.join(taskRoot, 'brief.md'), 'utf8'),
|
|
63
|
+
plan: readFileSync(path.join(taskRoot, 'plan.md'), 'utf8'),
|
|
64
|
+
evidence: readFileSync(path.join(taskRoot, 'evidence.json'), 'utf8'),
|
|
65
|
+
diff: task.baseCommit ? runGit(repositoryRoot, ['diff', '--no-ext-diff', `${task.baseCommit}..HEAD`]) : '',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
let processResult;
|
|
69
|
+
try {
|
|
70
|
+
processResult = runner(command[0], command.slice(1), {
|
|
71
|
+
cwd: repositoryRoot,
|
|
72
|
+
input: `${JSON.stringify(packet)}\n`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
processResult = { status: null, stdout: '', stderr: error instanceof Error ? error.message : String(error) };
|
|
77
|
+
}
|
|
78
|
+
const probeWritten = existsSync(probe);
|
|
79
|
+
const after = sealRepository(repositoryRoot);
|
|
80
|
+
if (probeWritten)
|
|
81
|
+
rmSync(probe, { force: true });
|
|
82
|
+
let output = null;
|
|
83
|
+
if (processResult.status === 0 && existsSync(outputFile)) {
|
|
84
|
+
try {
|
|
85
|
+
output = parseStrictReview(readFileSync(outputFile, 'utf8'));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
output = null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
92
|
+
const unchanged = sealsEqual(before, after) && !probeWritten;
|
|
93
|
+
const verifiedIsolation = unchanged && output?.isolationProbe === 'denied' && processResult.status === 0;
|
|
94
|
+
const review = verifiedIsolation && output
|
|
95
|
+
? stripProbe(output)
|
|
96
|
+
: unverifiedReview(processResult.status !== 0
|
|
97
|
+
? `Strict reviewer process failed with status ${String(processResult.status)}.`
|
|
98
|
+
: probeWritten || !unchanged
|
|
99
|
+
? 'Strict reviewer modified repository state; review discarded.'
|
|
100
|
+
: 'Strict reviewer did not return valid output proving the denied write probe.');
|
|
101
|
+
return {
|
|
102
|
+
review,
|
|
103
|
+
verifiedIsolation,
|
|
104
|
+
command,
|
|
105
|
+
processStatus: processResult.status,
|
|
106
|
+
stderr: processResult.stderr,
|
|
107
|
+
before,
|
|
108
|
+
after,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function spawnReviewer(command, args, options) {
|
|
112
|
+
const result = spawnSync(command, args, {
|
|
113
|
+
cwd: options.cwd,
|
|
114
|
+
input: options.input,
|
|
115
|
+
encoding: 'utf8',
|
|
116
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
117
|
+
});
|
|
118
|
+
return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
|
|
119
|
+
}
|
|
120
|
+
function sealRepository(repositoryRoot) {
|
|
121
|
+
return {
|
|
122
|
+
headCommit: headCommit(repositoryRoot),
|
|
123
|
+
changedFiles: changedFiles(repositoryRoot),
|
|
124
|
+
contentHash: hashTree(repositoryRoot, repositoryRoot),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function hashTree(repositoryRoot, current) {
|
|
128
|
+
const hash = createHash('sha256');
|
|
129
|
+
for (const name of readdirSync(current).sort()) {
|
|
130
|
+
if (current === repositoryRoot && name === '.git')
|
|
131
|
+
continue;
|
|
132
|
+
const absolute = path.join(current, name);
|
|
133
|
+
const relative = path.relative(repositoryRoot, absolute).split(path.sep).join('/');
|
|
134
|
+
const stat = lstatSync(absolute);
|
|
135
|
+
hash.update(`${relative}\0${stat.mode & 0o777}\0`);
|
|
136
|
+
if (stat.isSymbolicLink())
|
|
137
|
+
hash.update(`link:${readlinkSync(absolute)}`);
|
|
138
|
+
else if (stat.isDirectory())
|
|
139
|
+
hash.update(hashTree(repositoryRoot, absolute));
|
|
140
|
+
else if (stat.isFile())
|
|
141
|
+
hash.update(readFileSync(absolute));
|
|
142
|
+
}
|
|
143
|
+
return hash.digest('hex');
|
|
144
|
+
}
|
|
145
|
+
function sealsEqual(left, right) {
|
|
146
|
+
return left.headCommit === right.headCommit
|
|
147
|
+
&& left.contentHash === right.contentHash
|
|
148
|
+
&& JSON.stringify(left.changedFiles) === JSON.stringify(right.changedFiles);
|
|
149
|
+
}
|
|
150
|
+
function parseStrictReview(value) {
|
|
151
|
+
const parsed = JSON.parse(value);
|
|
152
|
+
if (!['passed', 'failed', 'unverified'].includes(parsed.status ?? ''))
|
|
153
|
+
throw new Error('invalid status');
|
|
154
|
+
if (!parsed.reviewer?.trim() || !parsed.summary?.trim() || !Array.isArray(parsed.findings))
|
|
155
|
+
throw new Error('invalid review');
|
|
156
|
+
if (!['denied', 'written', 'not-attempted'].includes(parsed.isolationProbe ?? ''))
|
|
157
|
+
throw new Error('invalid probe');
|
|
158
|
+
return parsed;
|
|
159
|
+
}
|
|
160
|
+
function stripProbe(output) {
|
|
161
|
+
return {
|
|
162
|
+
status: output.status,
|
|
163
|
+
reviewer: output.reviewer,
|
|
164
|
+
summary: output.summary,
|
|
165
|
+
findings: output.findings,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function unverifiedReview(summary) {
|
|
169
|
+
return { status: 'unverified', reviewer: 'strict-codex-reviewer', summary, findings: [] };
|
|
170
|
+
}
|
|
171
|
+
function failedLaunch(repositoryRoot, command, processStatus, summary) {
|
|
172
|
+
const seal = sealRepository(repositoryRoot);
|
|
173
|
+
return {
|
|
174
|
+
review: unverifiedReview(summary),
|
|
175
|
+
verifiedIsolation: false,
|
|
176
|
+
command,
|
|
177
|
+
processStatus,
|
|
178
|
+
stderr: summary,
|
|
179
|
+
before: seal,
|
|
180
|
+
after: seal,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
export { REVIEW_SCHEMA };
|
|
184
|
+
//# sourceMappingURL=reviewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../../src/reviewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type WriterLease } from '../contracts.js';
|
|
2
|
+
export declare class WriterLockManager {
|
|
3
|
+
private readonly locksRoot;
|
|
4
|
+
private readonly now;
|
|
5
|
+
constructor(locksRoot: string, now?: () => Date);
|
|
6
|
+
acquire(entityId: string, owner: string, leaseMs?: number): WriterLease;
|
|
7
|
+
heartbeat(entityId: string, token: string, leaseMs?: number): WriterLease;
|
|
8
|
+
release(entityId: string, token: string): void;
|
|
9
|
+
inspect(entityId: string): WriterLease | null;
|
|
10
|
+
repairStale(entityId: string): WriterLease;
|
|
11
|
+
private requireToken;
|
|
12
|
+
private file;
|
|
13
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { closeSync, existsSync, openSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { STATE_SCHEMA_VERSION } from '../contracts.js';
|
|
6
|
+
import { WorkflowError } from '../errors.js';
|
|
7
|
+
import { ensureDirectory, readJson, writeJsonAtomic } from '../fs-utils.js';
|
|
8
|
+
const DEFAULT_LEASE_MS = 30 * 60 * 1000;
|
|
9
|
+
export class WriterLockManager {
|
|
10
|
+
locksRoot;
|
|
11
|
+
now;
|
|
12
|
+
constructor(locksRoot, now = () => new Date()) {
|
|
13
|
+
this.locksRoot = locksRoot;
|
|
14
|
+
this.now = now;
|
|
15
|
+
ensureDirectory(locksRoot);
|
|
16
|
+
}
|
|
17
|
+
acquire(entityId, owner, leaseMs = DEFAULT_LEASE_MS) {
|
|
18
|
+
const file = this.file(entityId);
|
|
19
|
+
const timestamp = this.now();
|
|
20
|
+
const lease = {
|
|
21
|
+
schemaVersion: STATE_SCHEMA_VERSION,
|
|
22
|
+
entityId,
|
|
23
|
+
token: randomUUID(),
|
|
24
|
+
owner,
|
|
25
|
+
pid: process.pid,
|
|
26
|
+
hostname: os.hostname(),
|
|
27
|
+
acquiredAt: timestamp.toISOString(),
|
|
28
|
+
heartbeatAt: timestamp.toISOString(),
|
|
29
|
+
expiresAt: new Date(timestamp.getTime() + leaseMs).toISOString(),
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
const descriptor = openSync(file, 'wx', 0o600);
|
|
33
|
+
writeFileSync(descriptor, `${JSON.stringify(lease, null, 2)}\n`, 'utf8');
|
|
34
|
+
closeSync(descriptor);
|
|
35
|
+
return lease;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (existsSync(file)) {
|
|
39
|
+
const current = readJson(file);
|
|
40
|
+
throw new WorkflowError('LOCKED', `${entityId} already has a writer`, {
|
|
41
|
+
owner: current.owner,
|
|
42
|
+
pid: current.pid,
|
|
43
|
+
hostname: current.hostname,
|
|
44
|
+
heartbeatAt: current.heartbeatAt,
|
|
45
|
+
expiresAt: current.expiresAt,
|
|
46
|
+
stale: Date.parse(current.expiresAt) <= timestamp.getTime(),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
heartbeat(entityId, token, leaseMs = DEFAULT_LEASE_MS) {
|
|
53
|
+
const current = this.requireToken(entityId, token);
|
|
54
|
+
const timestamp = this.now();
|
|
55
|
+
const updated = {
|
|
56
|
+
...current,
|
|
57
|
+
heartbeatAt: timestamp.toISOString(),
|
|
58
|
+
expiresAt: new Date(timestamp.getTime() + leaseMs).toISOString(),
|
|
59
|
+
};
|
|
60
|
+
writeJsonAtomic(this.file(entityId), updated);
|
|
61
|
+
return updated;
|
|
62
|
+
}
|
|
63
|
+
release(entityId, token) {
|
|
64
|
+
this.requireToken(entityId, token);
|
|
65
|
+
unlinkSync(this.file(entityId));
|
|
66
|
+
}
|
|
67
|
+
inspect(entityId) {
|
|
68
|
+
const file = this.file(entityId);
|
|
69
|
+
return existsSync(file) ? readJson(file) : null;
|
|
70
|
+
}
|
|
71
|
+
repairStale(entityId) {
|
|
72
|
+
const file = this.file(entityId);
|
|
73
|
+
if (!existsSync(file))
|
|
74
|
+
throw new WorkflowError('NOT_FOUND', `No writer lock for ${entityId}`);
|
|
75
|
+
const current = readJson(file);
|
|
76
|
+
if (Date.parse(current.expiresAt) > this.now().getTime()) {
|
|
77
|
+
throw new WorkflowError('TRANSITION_BLOCKED', `Writer lock is not stale for ${entityId}`, {
|
|
78
|
+
expiresAt: current.expiresAt,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
unlinkSync(file);
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
requireToken(entityId, token) {
|
|
85
|
+
const current = this.inspect(entityId);
|
|
86
|
+
if (!current)
|
|
87
|
+
throw new WorkflowError('NOT_FOUND', `No writer lock for ${entityId}`);
|
|
88
|
+
if (current.token !== token) {
|
|
89
|
+
throw new WorkflowError('LOCK_TOKEN_INVALID', `Writer token does not own ${entityId}`);
|
|
90
|
+
}
|
|
91
|
+
return current;
|
|
92
|
+
}
|
|
93
|
+
file(entityId) {
|
|
94
|
+
if (!/^(TASK|MS)-[0-9A-HJKMNP-TV-Z]{26}$/.test(entityId)) {
|
|
95
|
+
throw new WorkflowError('INVALID_ARGUMENT', `Invalid lock entity: ${entityId}`);
|
|
96
|
+
}
|
|
97
|
+
return path.join(this.locksRoot, `${entityId}.json`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../../src/state/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DiscoveryState, GraphBinding, GraphKind, MilestoneState, ProjectRecord, ProjectKnowledgeMap, TaskState } from '../contracts.js';
|
|
2
|
+
import type { RepositoryIdentity } from '../repository.js';
|
|
3
|
+
export interface StateStoreOptions {
|
|
4
|
+
root?: string;
|
|
5
|
+
now?: () => Date;
|
|
6
|
+
}
|
|
7
|
+
export interface StagedArtifact {
|
|
8
|
+
entityRoot: string;
|
|
9
|
+
name: string;
|
|
10
|
+
hash: string;
|
|
11
|
+
stagedFile: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class FileStateStore {
|
|
14
|
+
readonly root: string;
|
|
15
|
+
private readonly now;
|
|
16
|
+
constructor(options?: StateStoreOptions);
|
|
17
|
+
registerProject(identity: RepositoryIdentity): ProjectRecord;
|
|
18
|
+
projectRoot(projectId: string): string;
|
|
19
|
+
discoveryRoot(projectId: string, discoveryId: string): string;
|
|
20
|
+
taskRoot(projectId: string, taskId: string): string;
|
|
21
|
+
milestoneRoot(projectId: string, milestoneId: string): string;
|
|
22
|
+
snapshotRoot(projectId: string): string;
|
|
23
|
+
lockRoot(projectId: string): string;
|
|
24
|
+
writeKnowledgeMap(map: ProjectKnowledgeMap, expectedRevision: number | null): ProjectKnowledgeMap;
|
|
25
|
+
readKnowledgeMap(projectId: string): ProjectKnowledgeMap;
|
|
26
|
+
writeGraphBinding(binding: GraphBinding, expectedRevision: number | null): GraphBinding;
|
|
27
|
+
readGraphBinding(projectId: string, graphKind: GraphKind): GraphBinding;
|
|
28
|
+
writeDiscovery(discovery: DiscoveryState, expectedRevision: number | null): DiscoveryState;
|
|
29
|
+
readDiscovery(projectId: string, discoveryId: string): DiscoveryState;
|
|
30
|
+
writeTask(task: TaskState, expectedRevision: number | null): TaskState;
|
|
31
|
+
readTask(projectId: string, taskId: string): TaskState;
|
|
32
|
+
writeMilestone(milestone: MilestoneState, expectedRevision: number | null): MilestoneState;
|
|
33
|
+
readMilestone(projectId: string, milestoneId: string): MilestoneState;
|
|
34
|
+
listTasks(projectId: string): TaskState[];
|
|
35
|
+
listDiscoveries(projectId: string): DiscoveryState[];
|
|
36
|
+
listMilestones(projectId: string): MilestoneState[];
|
|
37
|
+
writeArtifact(entityRoot: string, name: string, content: string): string;
|
|
38
|
+
stageArtifact(entityRoot: string, name: string, content: string): StagedArtifact;
|
|
39
|
+
publishArtifact(staged: StagedArtifact): string;
|
|
40
|
+
hashArtifact(entityRoot: string, name: string): string;
|
|
41
|
+
private writeRevisioned;
|
|
42
|
+
private readEntity;
|
|
43
|
+
private listEntities;
|
|
44
|
+
private assertId;
|
|
45
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { STATE_SCHEMA_VERSION } from '../contracts.js';
|
|
6
|
+
import { WorkflowError } from '../errors.js';
|
|
7
|
+
import { ensureDirectory, readJson, writeJsonAtomic, writeTextAtomic } from '../fs-utils.js';
|
|
8
|
+
export class FileStateStore {
|
|
9
|
+
root;
|
|
10
|
+
now;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
13
|
+
this.root = path.resolve(options.root || process.env.CODEX_WORKFLOW_STATE_HOME || path.join(codexHome, 'workflow-state', 'v2'));
|
|
14
|
+
this.now = options.now ?? (() => new Date());
|
|
15
|
+
ensureDirectory(this.root);
|
|
16
|
+
}
|
|
17
|
+
registerProject(identity) {
|
|
18
|
+
const projectRoot = this.projectRoot(identity.projectId);
|
|
19
|
+
ensureDirectory(projectRoot);
|
|
20
|
+
const file = path.join(projectRoot, 'project.json');
|
|
21
|
+
const timestamp = this.now().toISOString();
|
|
22
|
+
if (existsSync(file)) {
|
|
23
|
+
const existing = readJson(file);
|
|
24
|
+
const repositoryRoots = [...new Set([...existing.repositoryRoots, identity.repositoryRoot])];
|
|
25
|
+
if (repositoryRoots.length === existing.repositoryRoots.length)
|
|
26
|
+
return existing;
|
|
27
|
+
const updated = { ...existing, repositoryRoots, updatedAt: timestamp };
|
|
28
|
+
writeJsonAtomic(file, updated);
|
|
29
|
+
return updated;
|
|
30
|
+
}
|
|
31
|
+
const created = {
|
|
32
|
+
schemaVersion: STATE_SCHEMA_VERSION,
|
|
33
|
+
projectId: identity.projectId,
|
|
34
|
+
gitCommonDir: identity.gitCommonDir,
|
|
35
|
+
repositoryRoots: [identity.repositoryRoot],
|
|
36
|
+
createdAt: timestamp,
|
|
37
|
+
updatedAt: timestamp,
|
|
38
|
+
};
|
|
39
|
+
writeJsonAtomic(file, created);
|
|
40
|
+
return created;
|
|
41
|
+
}
|
|
42
|
+
projectRoot(projectId) {
|
|
43
|
+
this.assertId(projectId, /^[a-f0-9]{24}$/);
|
|
44
|
+
return path.join(this.root, 'projects', projectId);
|
|
45
|
+
}
|
|
46
|
+
discoveryRoot(projectId, discoveryId) {
|
|
47
|
+
this.assertId(discoveryId, /^DISC-[0-9A-HJKMNP-TV-Z]{26}$/);
|
|
48
|
+
return path.join(this.projectRoot(projectId), 'discoveries', discoveryId);
|
|
49
|
+
}
|
|
50
|
+
taskRoot(projectId, taskId) {
|
|
51
|
+
this.assertId(taskId, /^TASK-[0-9A-HJKMNP-TV-Z]{26}$/);
|
|
52
|
+
return path.join(this.projectRoot(projectId), 'tasks', taskId);
|
|
53
|
+
}
|
|
54
|
+
milestoneRoot(projectId, milestoneId) {
|
|
55
|
+
this.assertId(milestoneId, /^MS-[0-9A-HJKMNP-TV-Z]{26}$/);
|
|
56
|
+
return path.join(this.projectRoot(projectId), 'milestones', milestoneId);
|
|
57
|
+
}
|
|
58
|
+
snapshotRoot(projectId) {
|
|
59
|
+
return path.join(this.projectRoot(projectId), 'snapshots');
|
|
60
|
+
}
|
|
61
|
+
lockRoot(projectId) {
|
|
62
|
+
return path.join(this.projectRoot(projectId), 'locks');
|
|
63
|
+
}
|
|
64
|
+
writeKnowledgeMap(map, expectedRevision) {
|
|
65
|
+
return this.writeRevisioned(path.join(this.projectRoot(map.projectId), 'knowledge-map.json'), map, expectedRevision);
|
|
66
|
+
}
|
|
67
|
+
readKnowledgeMap(projectId) {
|
|
68
|
+
return this.readEntity(path.join(this.projectRoot(projectId), 'knowledge-map.json'), 'knowledge-map');
|
|
69
|
+
}
|
|
70
|
+
writeGraphBinding(binding, expectedRevision) {
|
|
71
|
+
return this.writeRevisioned(path.join(this.projectRoot(binding.projectId), 'graph-bindings', `${binding.graphKind}.json`), binding, expectedRevision);
|
|
72
|
+
}
|
|
73
|
+
readGraphBinding(projectId, graphKind) {
|
|
74
|
+
return this.readEntity(path.join(this.projectRoot(projectId), 'graph-bindings', `${graphKind}.json`), 'graph-binding');
|
|
75
|
+
}
|
|
76
|
+
writeDiscovery(discovery, expectedRevision) {
|
|
77
|
+
return this.writeRevisioned(path.join(this.discoveryRoot(discovery.projectId, discovery.id), 'state.json'), discovery, expectedRevision);
|
|
78
|
+
}
|
|
79
|
+
readDiscovery(projectId, discoveryId) {
|
|
80
|
+
return this.readEntity(path.join(this.discoveryRoot(projectId, discoveryId), 'state.json'), 'discovery');
|
|
81
|
+
}
|
|
82
|
+
writeTask(task, expectedRevision) {
|
|
83
|
+
return this.writeRevisioned(path.join(this.taskRoot(task.projectId, task.id), 'state.json'), task, expectedRevision);
|
|
84
|
+
}
|
|
85
|
+
readTask(projectId, taskId) {
|
|
86
|
+
return this.readEntity(path.join(this.taskRoot(projectId, taskId), 'state.json'), 'task');
|
|
87
|
+
}
|
|
88
|
+
writeMilestone(milestone, expectedRevision) {
|
|
89
|
+
return this.writeRevisioned(path.join(this.milestoneRoot(milestone.projectId, milestone.id), 'state.json'), milestone, expectedRevision);
|
|
90
|
+
}
|
|
91
|
+
readMilestone(projectId, milestoneId) {
|
|
92
|
+
return this.readEntity(path.join(this.milestoneRoot(projectId, milestoneId), 'state.json'), 'milestone');
|
|
93
|
+
}
|
|
94
|
+
listTasks(projectId) {
|
|
95
|
+
return this.listEntities(path.join(this.projectRoot(projectId), 'tasks'), 'task');
|
|
96
|
+
}
|
|
97
|
+
listDiscoveries(projectId) {
|
|
98
|
+
return this.listEntities(path.join(this.projectRoot(projectId), 'discoveries'), 'discovery');
|
|
99
|
+
}
|
|
100
|
+
listMilestones(projectId) {
|
|
101
|
+
return this.listEntities(path.join(this.projectRoot(projectId), 'milestones'), 'milestone');
|
|
102
|
+
}
|
|
103
|
+
writeArtifact(entityRoot, name, content) {
|
|
104
|
+
if (!/^[a-z][a-z0-9-]*\.md$/.test(name)) {
|
|
105
|
+
throw new WorkflowError('INVALID_ARGUMENT', `Invalid artifact name: ${name}`);
|
|
106
|
+
}
|
|
107
|
+
const file = path.join(entityRoot, name);
|
|
108
|
+
writeTextAtomic(file, content);
|
|
109
|
+
return createHash('sha256').update(readFileSync(file)).digest('hex');
|
|
110
|
+
}
|
|
111
|
+
stageArtifact(entityRoot, name, content) {
|
|
112
|
+
if (!/^[a-z][a-z0-9-]*\.(?:md|json|jsonl)$/.test(name)) {
|
|
113
|
+
throw new WorkflowError('INVALID_ARGUMENT', `Invalid artifact name: ${name}`);
|
|
114
|
+
}
|
|
115
|
+
const normalized = content.endsWith('\n') ? content : `${content}\n`;
|
|
116
|
+
const hash = createHash('sha256').update(normalized).digest('hex');
|
|
117
|
+
const stagedFile = path.join(entityRoot, '.versions', `${name}.${hash}`);
|
|
118
|
+
if (!existsSync(stagedFile))
|
|
119
|
+
writeTextAtomic(stagedFile, normalized);
|
|
120
|
+
return { entityRoot, name, hash, stagedFile };
|
|
121
|
+
}
|
|
122
|
+
publishArtifact(staged) {
|
|
123
|
+
writeTextAtomic(path.join(staged.entityRoot, staged.name), readFileSync(staged.stagedFile, 'utf8'));
|
|
124
|
+
const actual = this.hashArtifact(staged.entityRoot, staged.name);
|
|
125
|
+
if (actual !== staged.hash) {
|
|
126
|
+
throw new WorkflowError('STATE_CORRUPT', `Published artifact hash mismatch: ${staged.name}`, {
|
|
127
|
+
expected: staged.hash,
|
|
128
|
+
actual,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return actual;
|
|
132
|
+
}
|
|
133
|
+
hashArtifact(entityRoot, name) {
|
|
134
|
+
const file = path.join(entityRoot, name);
|
|
135
|
+
if (!existsSync(file))
|
|
136
|
+
throw new WorkflowError('NOT_FOUND', `Artifact not found: ${name}`);
|
|
137
|
+
return createHash('sha256').update(readFileSync(file)).digest('hex');
|
|
138
|
+
}
|
|
139
|
+
writeRevisioned(file, record, expectedRevision) {
|
|
140
|
+
const timestamp = this.now().toISOString();
|
|
141
|
+
if (!existsSync(file)) {
|
|
142
|
+
if (expectedRevision !== null) {
|
|
143
|
+
throw new WorkflowError('STATE_CONFLICT', `Cannot update missing state ${record.id}`, {
|
|
144
|
+
expectedRevision,
|
|
145
|
+
actualRevision: null,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const created = { ...record, revision: 1, updatedAt: timestamp };
|
|
149
|
+
writeJsonAtomic(file, created);
|
|
150
|
+
return created;
|
|
151
|
+
}
|
|
152
|
+
const existing = readJson(file);
|
|
153
|
+
if (expectedRevision === null || existing.revision !== expectedRevision) {
|
|
154
|
+
throw new WorkflowError('STATE_CONFLICT', `State revision conflict for ${record.id}`, {
|
|
155
|
+
expectedRevision,
|
|
156
|
+
actualRevision: existing.revision,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const updated = { ...record, revision: existing.revision + 1, updatedAt: timestamp };
|
|
160
|
+
writeJsonAtomic(file, updated);
|
|
161
|
+
return updated;
|
|
162
|
+
}
|
|
163
|
+
readEntity(file, kind) {
|
|
164
|
+
if (!existsSync(file))
|
|
165
|
+
throw new WorkflowError('NOT_FOUND', `${kind} state not found: ${file}`);
|
|
166
|
+
const entity = readJson(file);
|
|
167
|
+
if (entity.kind !== kind || entity.schemaVersion !== STATE_SCHEMA_VERSION) {
|
|
168
|
+
throw new WorkflowError('STATE_CORRUPT', `Unexpected ${kind} state schema: ${file}`);
|
|
169
|
+
}
|
|
170
|
+
return entity;
|
|
171
|
+
}
|
|
172
|
+
listEntities(directory, kind) {
|
|
173
|
+
if (!existsSync(directory))
|
|
174
|
+
return [];
|
|
175
|
+
return readdirSync(directory, { withFileTypes: true })
|
|
176
|
+
.filter((entry) => entry.isDirectory())
|
|
177
|
+
.map((entry) => this.readEntity(path.join(directory, entry.name, 'state.json'), kind))
|
|
178
|
+
.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
179
|
+
}
|
|
180
|
+
assertId(value, pattern) {
|
|
181
|
+
if (!pattern.test(value))
|
|
182
|
+
throw new WorkflowError('INVALID_ARGUMENT', `Invalid state identifier: ${value}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/state/store.ts"],"names":[],"mappings":"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"}
|
package/dist/src/ulid.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
3
|
+
function encode(value, length) {
|
|
4
|
+
let result = '';
|
|
5
|
+
for (let index = 0; index < length; index += 1) {
|
|
6
|
+
result = ALPHABET[Number(value & 31n)] + result;
|
|
7
|
+
value >>= 5n;
|
|
8
|
+
}
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
11
|
+
export function createUlid(now = Date.now()) {
|
|
12
|
+
const timestamp = encode(BigInt(now), 10);
|
|
13
|
+
const random = randomBytes(10);
|
|
14
|
+
let randomValue = 0n;
|
|
15
|
+
for (const byte of random)
|
|
16
|
+
randomValue = (randomValue << 8n) | BigInt(byte);
|
|
17
|
+
return `${timestamp}${encode(randomValue, 16)}`;
|
|
18
|
+
}
|
|
19
|
+
export function createEntityId(prefix, now = Date.now()) {
|
|
20
|
+
return `${prefix}-${createUlid(now)}`;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=ulid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ulid.js","sourceRoot":"","sources":["../../src/ulid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,QAAQ,GAAG,kCAAkC,CAAC;AAEpD,SAAS,MAAM,CAAC,KAAa,EAAE,MAAc;IAC3C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC;QAChD,KAAK,KAAK,EAAE,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACzC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/B,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,KAAK,MAAM,IAAI,IAAI,MAAM;QAAE,WAAW,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5E,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAA8B,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAC7E,OAAO,GAAG,MAAM,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC"}
|