@farmslot/agent-runtime 0.3.1 → 0.5.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/CHANGELOG.md +11 -0
- package/bin/farmslot-agent.mjs +1 -1
- package/dist/execution-template/create.d.ts.map +1 -1
- package/dist/execution-template/create.js +9 -1
- package/dist/execution-template/create.js.map +1 -1
- package/dist/execution-template/execution-template.test.js +309 -1
- package/dist/execution-template/execution-template.test.js.map +1 -1
- package/dist/execution-template/frontmatter.d.ts.map +1 -1
- package/dist/execution-template/frontmatter.js +19 -4
- package/dist/execution-template/frontmatter.js.map +1 -1
- package/dist/execution-template/index.d.ts +8 -1
- package/dist/execution-template/index.d.ts.map +1 -1
- package/dist/execution-template/index.js +5 -0
- package/dist/execution-template/index.js.map +1 -1
- package/dist/execution-template/infer.d.ts.map +1 -1
- package/dist/execution-template/infer.js +14 -1
- package/dist/execution-template/infer.js.map +1 -1
- package/dist/execution-template/lint.d.ts.map +1 -1
- package/dist/execution-template/lint.js +8 -0
- package/dist/execution-template/lint.js.map +1 -1
- package/dist/execution-template/project-worker.d.ts +19 -0
- package/dist/execution-template/project-worker.d.ts.map +1 -0
- package/dist/execution-template/project-worker.js +143 -0
- package/dist/execution-template/project-worker.js.map +1 -0
- package/dist/execution-template/resolve.d.ts +1 -1
- package/dist/execution-template/resolve.d.ts.map +1 -1
- package/dist/execution-template/resolve.js +12 -2
- package/dist/execution-template/resolve.js.map +1 -1
- package/dist/execution-template/select.d.ts +8 -0
- package/dist/execution-template/select.d.ts.map +1 -0
- package/dist/execution-template/select.js +80 -0
- package/dist/execution-template/select.js.map +1 -0
- package/dist/execution-template/snapshot.d.ts +13 -0
- package/dist/execution-template/snapshot.d.ts.map +1 -0
- package/dist/execution-template/snapshot.js +46 -0
- package/dist/execution-template/snapshot.js.map +1 -0
- package/dist/execution-template/source-config.d.ts +19 -0
- package/dist/execution-template/source-config.d.ts.map +1 -0
- package/dist/execution-template/source-config.js +90 -0
- package/dist/execution-template/source-config.js.map +1 -0
- package/dist/execution-template/types.d.ts +38 -1
- package/dist/execution-template/types.d.ts.map +1 -1
- package/dist/execution-template/types.js +7 -2
- package/dist/execution-template/types.js.map +1 -1
- package/package.json +4 -3
- package/scripts/check-task-artifact-contract.mjs +112 -33
- package/scripts/checklist-target.cjs +13 -0
- package/scripts/execution-template-cli.mjs +150 -19
- package/scripts/mark-checklist-step.cjs +16 -6
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export function sha256Text(text) {
|
|
5
|
+
return createHash('sha256').update(text).digest('hex');
|
|
6
|
+
}
|
|
7
|
+
export function executionTemplateReference(entry, renderedMarkdown) {
|
|
8
|
+
const declaredVersion = entry.frontmatter?.version;
|
|
9
|
+
return {
|
|
10
|
+
id: entry.id,
|
|
11
|
+
sourceId: entry.sourceId,
|
|
12
|
+
flow: entry.flow,
|
|
13
|
+
...(entry.runMode ? { runMode: entry.runMode } : {}),
|
|
14
|
+
platforms: entry.platforms,
|
|
15
|
+
labels: entry.labels,
|
|
16
|
+
relativePath: entry.relativePath,
|
|
17
|
+
...(entry.sourceRevision ? { sourceRevision: entry.sourceRevision } : {}),
|
|
18
|
+
...(entry.sourceDirty === undefined ? {} : { sourceDirty: entry.sourceDirty }),
|
|
19
|
+
...(declaredVersion == null || declaredVersion === ''
|
|
20
|
+
? {}
|
|
21
|
+
: { version: String(declaredVersion) }),
|
|
22
|
+
sha256: entry.sha256,
|
|
23
|
+
...(renderedMarkdown === undefined ? {} : { renderedSha256: sha256Text(renderedMarkdown) }),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function readExecutionTemplateSnapshot(entry) {
|
|
27
|
+
const markdown = readFileSync(entry.path, 'utf8');
|
|
28
|
+
if (sha256Text(markdown) !== entry.sha256) {
|
|
29
|
+
throw new Error(`Execution template "${entry.id}" changed after catalog resolution. Refresh the catalog and select it again.`);
|
|
30
|
+
}
|
|
31
|
+
return { markdown, reference: executionTemplateReference(entry) };
|
|
32
|
+
}
|
|
33
|
+
export function materializeExecutionTemplate(entry, outputPath) {
|
|
34
|
+
const destination = path.resolve(outputPath);
|
|
35
|
+
if (existsSync(destination)) {
|
|
36
|
+
throw new Error(`Execution-template destination already exists: ${destination}`);
|
|
37
|
+
}
|
|
38
|
+
const snapshot = readExecutionTemplateSnapshot(entry);
|
|
39
|
+
mkdirSync(path.dirname(destination), { recursive: true });
|
|
40
|
+
writeFileSync(destination, snapshot.markdown, { encoding: 'utf8', flag: 'wx' });
|
|
41
|
+
return {
|
|
42
|
+
path: destination,
|
|
43
|
+
reference: executionTemplateReference(entry, snapshot.markdown),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/execution-template/snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProjectExecutionTemplatesConfig, UnavailableExecutionTemplateSource } from '@farmslot/protocol';
|
|
2
|
+
import type { ExecutionTemplateSource } from './types.js';
|
|
3
|
+
export type { UnavailableExecutionTemplateSource } from '@farmslot/protocol';
|
|
4
|
+
export interface ResolvedExecutionTemplateSources {
|
|
5
|
+
sources: ExecutionTemplateSource[];
|
|
6
|
+
unavailable: UnavailableExecutionTemplateSource[];
|
|
7
|
+
}
|
|
8
|
+
export interface ResolveConfiguredExecutionTemplateSourcesOptions {
|
|
9
|
+
projectPackRoot: string;
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
}
|
|
12
|
+
export declare function executionTemplateSourceRevision(root: string): string | undefined;
|
|
13
|
+
export declare function executionTemplateSourceDirty(root: string): boolean | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve optional configured sources only when catalog options are requested.
|
|
16
|
+
* Missing roots are diagnostics, not project-load failures.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveConfiguredExecutionTemplateSources(config: ProjectExecutionTemplatesConfig | undefined, options: ResolveConfiguredExecutionTemplateSourcesOptions): ResolvedExecutionTemplateSources;
|
|
19
|
+
//# sourceMappingURL=source-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-config.d.ts","sourceRoot":"","sources":["../../src/execution-template/source-config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,+BAA+B,EAC/B,kCAAkC,EACnC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,YAAY,EAAE,kCAAkC,EAAE,MAAM,oBAAoB,CAAC;AAE7E,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,WAAW,EAAE,kCAAkC,EAAE,CAAC;CACnD;AAED,MAAM,WAAW,gDAAgD;IAC/D,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAUD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQhF;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAO9E;AAeD;;;GAGG;AACH,wBAAgB,yCAAyC,CACvD,MAAM,EAAE,+BAA+B,GAAG,SAAS,EACnD,OAAO,EAAE,gDAAgD,GACxD,gCAAgC,CAoDlC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
function isWithin(base, candidate) {
|
|
5
|
+
const relative = path.relative(base, candidate);
|
|
6
|
+
return (relative === '' ||
|
|
7
|
+
(!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative)));
|
|
8
|
+
}
|
|
9
|
+
export function executionTemplateSourceRevision(root) {
|
|
10
|
+
const result = spawnSync('git', ['-C', root, 'rev-parse', 'HEAD'], {
|
|
11
|
+
encoding: 'utf8',
|
|
12
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
13
|
+
});
|
|
14
|
+
if (result.status !== 0)
|
|
15
|
+
return undefined;
|
|
16
|
+
const revision = result.stdout.trim();
|
|
17
|
+
return /^[0-9a-f]{40}$/i.test(revision) ? revision : undefined;
|
|
18
|
+
}
|
|
19
|
+
export function executionTemplateSourceDirty(root) {
|
|
20
|
+
const result = spawnSync('git', ['-C', root, 'status', '--porcelain', '--', '.'], {
|
|
21
|
+
encoding: 'utf8',
|
|
22
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
23
|
+
});
|
|
24
|
+
if (result.status !== 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
return result.stdout.trim().length > 0;
|
|
27
|
+
}
|
|
28
|
+
function configuredRoot(source, options) {
|
|
29
|
+
if (typeof source.root.env === 'string') {
|
|
30
|
+
const value = (options.env ?? process.env)[source.root.env]?.trim();
|
|
31
|
+
if (!value)
|
|
32
|
+
return { reason: 'missing-environment' };
|
|
33
|
+
if (!path.isAbsolute(value))
|
|
34
|
+
return { reason: 'invalid-root' };
|
|
35
|
+
return { path: value };
|
|
36
|
+
}
|
|
37
|
+
return { path: path.resolve(options.projectPackRoot, source.root.projectPath) };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve optional configured sources only when catalog options are requested.
|
|
41
|
+
* Missing roots are diagnostics, not project-load failures.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveConfiguredExecutionTemplateSources(config, options) {
|
|
44
|
+
const resolved = [];
|
|
45
|
+
const unavailable = [];
|
|
46
|
+
for (const source of config?.sources ?? []) {
|
|
47
|
+
const rootResult = configuredRoot(source, options);
|
|
48
|
+
if (!rootResult.path) {
|
|
49
|
+
unavailable.push({ id: source.id, reason: rootResult.reason ?? 'invalid-root' });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!existsSync(rootResult.path) || !statSync(rootResult.path).isDirectory()) {
|
|
53
|
+
unavailable.push({ id: source.id, reason: 'missing-root' });
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const canonicalRoot = realpathSync(rootResult.path);
|
|
57
|
+
if ('projectPath' in source.root &&
|
|
58
|
+
!isWithin(realpathSync(options.projectPackRoot), canonicalRoot)) {
|
|
59
|
+
unavailable.push({ id: source.id, reason: 'invalid-root' });
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const scanPath = path.resolve(canonicalRoot, source.subpath ?? '.');
|
|
63
|
+
if (!isWithin(canonicalRoot, scanPath)) {
|
|
64
|
+
unavailable.push({ id: source.id, reason: 'invalid-root' });
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (!existsSync(scanPath) || !statSync(scanPath).isDirectory()) {
|
|
68
|
+
unavailable.push({ id: source.id, reason: 'missing-root' });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const canonicalScanPath = realpathSync(scanPath);
|
|
72
|
+
if (!isWithin(canonicalRoot, canonicalScanPath)) {
|
|
73
|
+
unavailable.push({ id: source.id, reason: 'invalid-root' });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const sourceRevision = executionTemplateSourceRevision(canonicalScanPath);
|
|
77
|
+
const sourceDirty = executionTemplateSourceDirty(canonicalScanPath);
|
|
78
|
+
resolved.push({
|
|
79
|
+
id: source.id,
|
|
80
|
+
kind: source.kind,
|
|
81
|
+
root: canonicalScanPath,
|
|
82
|
+
layout: 'flow-tree',
|
|
83
|
+
...(source.domains ? { domains: source.domains } : {}),
|
|
84
|
+
...(sourceRevision ? { sourceRevision } : {}),
|
|
85
|
+
...(sourceDirty === undefined ? {} : { sourceDirty }),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return { sources: resolved, unavailable };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=source-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-config.js","sourceRoot":"","sources":["../../src/execution-template/source-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Shared Markdown execution-template catalog (ADR-049). */
|
|
2
|
+
import type { ExecutionTemplateDefault, ExecutionTemplateReference, ExecutionTemplateRunMode } from '@farmslot/protocol';
|
|
2
3
|
export type ExecutionTemplateSourceKind = 'custom' | 'project' | 'workspace' | 'user' | 'package' | 'fallback';
|
|
3
4
|
export type ExecutionTemplateLayout = 'flow-tree' | 'worker-flat';
|
|
4
|
-
export type ExecutionRunMode =
|
|
5
|
+
export type ExecutionRunMode = ExecutionTemplateRunMode;
|
|
5
6
|
export interface ExecutionTemplateSource {
|
|
6
7
|
/** Stable source label for catalogs / shadowing (e.g. project:farmslot-farm). */
|
|
7
8
|
id: string;
|
|
@@ -9,10 +10,17 @@ export interface ExecutionTemplateSource {
|
|
|
9
10
|
/** Absolute directory to scan. */
|
|
10
11
|
root: string;
|
|
11
12
|
layout: ExecutionTemplateLayout;
|
|
13
|
+
/** Exact domains supplied by source configuration. */
|
|
14
|
+
domains?: string[];
|
|
15
|
+
/** Git revision of the source root when available. */
|
|
16
|
+
sourceRevision?: string;
|
|
17
|
+
/** True when the source checkout had uncommitted changes. */
|
|
18
|
+
sourceDirty?: boolean;
|
|
12
19
|
}
|
|
13
20
|
export interface ExecutionTemplateFrontmatter {
|
|
14
21
|
id?: string;
|
|
15
22
|
title?: string;
|
|
23
|
+
description?: string;
|
|
16
24
|
flow?: string;
|
|
17
25
|
version?: string | number;
|
|
18
26
|
runMode?: ExecutionRunMode;
|
|
@@ -23,6 +31,7 @@ export interface ExecutionTemplateFrontmatter {
|
|
|
23
31
|
export interface ExecutionTemplateEntry {
|
|
24
32
|
id: string;
|
|
25
33
|
title: string;
|
|
34
|
+
description?: string;
|
|
26
35
|
flow: string;
|
|
27
36
|
version: string;
|
|
28
37
|
runMode: ExecutionRunMode | null;
|
|
@@ -32,16 +41,43 @@ export interface ExecutionTemplateEntry {
|
|
|
32
41
|
relativePath: string;
|
|
33
42
|
sourceId: string;
|
|
34
43
|
sourceKind: ExecutionTemplateSourceKind;
|
|
44
|
+
/** SHA-256 of the source Markdown before rendering. */
|
|
45
|
+
sha256: string;
|
|
46
|
+
sourceRevision?: string;
|
|
47
|
+
sourceDirty?: boolean;
|
|
35
48
|
/** Present when a higher-precedence source already claimed this id. */
|
|
36
49
|
shadowedBy?: string;
|
|
37
50
|
frontmatter: ExecutionTemplateFrontmatter | null;
|
|
38
51
|
heading: string | null;
|
|
39
52
|
}
|
|
53
|
+
export interface SelectExecutionTemplateOptions {
|
|
54
|
+
sources: ExecutionTemplateSource[];
|
|
55
|
+
flow: string;
|
|
56
|
+
platform: string;
|
|
57
|
+
runMode: ExecutionRunMode;
|
|
58
|
+
domain?: string;
|
|
59
|
+
explicitId?: string;
|
|
60
|
+
defaults?: ExecutionTemplateDefault[];
|
|
61
|
+
}
|
|
62
|
+
export type ListCompatibleExecutionTemplatesOptions = Omit<SelectExecutionTemplateOptions, 'explicitId' | 'defaults'>;
|
|
63
|
+
export type ExecutionTemplateSelectionReason = 'explicit' | 'configured-default' | 'single-domain-candidate' | 'single-general-candidate';
|
|
64
|
+
export interface SelectedExecutionTemplate {
|
|
65
|
+
entry: ExecutionTemplateEntry;
|
|
66
|
+
reason: ExecutionTemplateSelectionReason;
|
|
67
|
+
reference: ExecutionTemplateReference;
|
|
68
|
+
}
|
|
69
|
+
export type ExecutionTemplateSelectionErrorCode = 'ambiguous' | 'missing-or-incompatible' | 'no-compatible-template';
|
|
70
|
+
export declare class ExecutionTemplateSelectionError extends Error {
|
|
71
|
+
readonly code: ExecutionTemplateSelectionErrorCode;
|
|
72
|
+
constructor(code: ExecutionTemplateSelectionErrorCode, message: string);
|
|
73
|
+
}
|
|
40
74
|
export interface ListExecutionTemplatesOptions {
|
|
41
75
|
sources: ExecutionTemplateSource[];
|
|
42
76
|
flow?: string;
|
|
43
77
|
runMode?: ExecutionRunMode;
|
|
44
78
|
platform?: string;
|
|
79
|
+
/** Keep general templates plus templates for this exact domain. */
|
|
80
|
+
domain?: string;
|
|
45
81
|
/** Include shadowed duplicates (default true for list diagnostics). */
|
|
46
82
|
includeShadowed?: boolean;
|
|
47
83
|
}
|
|
@@ -61,6 +97,7 @@ export interface CreateExecutionTemplateOptions {
|
|
|
61
97
|
runMode?: ExecutionRunMode;
|
|
62
98
|
platforms?: string[];
|
|
63
99
|
title?: string;
|
|
100
|
+
description?: string;
|
|
64
101
|
force?: boolean;
|
|
65
102
|
}
|
|
66
103
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/execution-template/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/execution-template/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export class ExecutionTemplateSelectionError extends Error {
|
|
2
|
+
constructor(code, message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.code = code;
|
|
5
|
+
this.name = 'ExecutionTemplateSelectionError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
3
8
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/execution-template/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/execution-template/types.ts"],"names":[],"mappings":"AAqGA,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IACxD,YACkB,IAAyC,EACzD,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAqC;QAIzD,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;IAChD,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farmslot/agent-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"./scripts/mark-checklist-step.cjs": "./scripts/mark-checklist-step.cjs",
|
|
17
17
|
"./scripts/checklist-target.cjs": "./scripts/checklist-target.cjs",
|
|
18
18
|
"./scripts/worker-terminal-contract.cjs": "./scripts/worker-terminal-contract.cjs",
|
|
19
|
-
"./scripts/check-task-artifact-contract.mjs": "./scripts/check-task-artifact-contract.mjs"
|
|
19
|
+
"./scripts/check-task-artifact-contract.mjs": "./scripts/check-task-artifact-contract.mjs",
|
|
20
|
+
"./scripts/execution-template-cli.mjs": "./scripts/execution-template-cli.mjs"
|
|
20
21
|
},
|
|
21
22
|
"scripts": {
|
|
22
23
|
"typecheck": "yarn workspace @farmslot/protocol build && tsc --noEmit --project tsconfig.json",
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
"prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/agent-runtime"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"@farmslot/protocol": "0.
|
|
35
|
+
"@farmslot/protocol": "0.15.0"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@types/node": "^22.0.0",
|
|
@@ -166,6 +166,12 @@ function isStringArray(value) {
|
|
|
166
166
|
return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
function isNonEmptyStringArray(value) {
|
|
170
|
+
return (
|
|
171
|
+
isStringArray(value) && value.length > 0 && value.every((entry) => entry.trim().length > 0)
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
169
175
|
function isRecipeQualityArtifactFallback(value) {
|
|
170
176
|
if (!isRecord(value)) return false;
|
|
171
177
|
if (value.version !== 1) return false;
|
|
@@ -289,8 +295,9 @@ function addRef(refs, value) {
|
|
|
289
295
|
}
|
|
290
296
|
|
|
291
297
|
function validateRecipeQualityArtifact() {
|
|
292
|
-
const
|
|
293
|
-
if (!
|
|
298
|
+
const relPath = 'artifacts/recipe-quality.json';
|
|
299
|
+
if (!fileExists(relPath)) return;
|
|
300
|
+
const text = readText(relPath) ?? '';
|
|
294
301
|
let artifact;
|
|
295
302
|
try {
|
|
296
303
|
artifact = JSON.parse(text);
|
|
@@ -325,6 +332,7 @@ function validateRecipeDocumentArtifact() {
|
|
|
325
332
|
`${packageRoot}/artifact-manifest.json`,
|
|
326
333
|
'recipe-run/artifact-manifest.json',
|
|
327
334
|
);
|
|
335
|
+
const summary = readJsonArtifact(`${packageRoot}/summary.json`, 'recipe-run/summary.json');
|
|
328
336
|
const trace = readJsonArtifact(`${packageRoot}/trace.json`, 'recipe-run/trace.json');
|
|
329
337
|
const recipeResolution = readJsonArtifact(
|
|
330
338
|
`${packageRoot}/recipe-resolution.json`,
|
|
@@ -349,6 +357,7 @@ function validateRecipeDocumentArtifact() {
|
|
|
349
357
|
const result = sharedRecipeArtifactPackageValidator({
|
|
350
358
|
recipe,
|
|
351
359
|
manifest,
|
|
360
|
+
summary,
|
|
352
361
|
trace,
|
|
353
362
|
recipeResolution,
|
|
354
363
|
resolvedRecipes,
|
|
@@ -379,8 +388,8 @@ function resolvedRecipeArtifactPath(digest) {
|
|
|
379
388
|
}
|
|
380
389
|
|
|
381
390
|
function readJsonArtifact(relPath, label) {
|
|
382
|
-
|
|
383
|
-
|
|
391
|
+
if (!fileExists(relPath)) return undefined;
|
|
392
|
+
const text = readText(relPath) ?? '';
|
|
384
393
|
try {
|
|
385
394
|
return JSON.parse(text);
|
|
386
395
|
} catch (error) {
|
|
@@ -459,8 +468,9 @@ function listArtifactPaths(root) {
|
|
|
459
468
|
}
|
|
460
469
|
|
|
461
470
|
function parseManifest() {
|
|
462
|
-
const
|
|
463
|
-
if (!
|
|
471
|
+
const relPath = 'artifacts/evidence-manifest.json';
|
|
472
|
+
if (!fileExists(relPath)) return null;
|
|
473
|
+
const text = readText(relPath) ?? '';
|
|
464
474
|
let manifest;
|
|
465
475
|
try {
|
|
466
476
|
manifest = JSON.parse(text);
|
|
@@ -570,32 +580,97 @@ function parseManifest() {
|
|
|
570
580
|
return { manifest, refs };
|
|
571
581
|
}
|
|
572
582
|
|
|
583
|
+
let terminalContract;
|
|
584
|
+
if (contractPath && terminalCommand && existsSync(contractPath)) {
|
|
585
|
+
try {
|
|
586
|
+
terminalContract = JSON.parse(readFileSync(contractPath, 'utf8'));
|
|
587
|
+
} catch (error) {
|
|
588
|
+
issues.push(`${contractPath}: invalid worker terminal contract JSON (${error.message})`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const isSelfReviewTerminal = terminalContract?.flowType === 'self-review';
|
|
573
592
|
const hasRecipe = fileExists('artifacts/recipe.json');
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if (
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
593
|
+
|
|
594
|
+
// A reviewer must be able to report ISSUES when worker-owned proof is invalid.
|
|
595
|
+
// Its terminal gate validates only the reviewer-owned feedback artifact below;
|
|
596
|
+
// contribution and fix flows still validate the complete proof package.
|
|
597
|
+
if (!isSelfReviewTerminal) {
|
|
598
|
+
const hasRecipeDecision = fileExists('artifacts/recipe-decision.json');
|
|
599
|
+
const recipeDecisionText = readText('artifacts/recipe-decision.json');
|
|
600
|
+
if (hasRecipeDecision) {
|
|
601
|
+
try {
|
|
602
|
+
const decision = JSON.parse(recipeDecisionText);
|
|
603
|
+
if (!isRecord(decision)) {
|
|
604
|
+
issues.push('artifacts/recipe-decision.json: expected object');
|
|
605
|
+
} else {
|
|
606
|
+
if (decision.version !== 1) {
|
|
607
|
+
issues.push('artifacts/recipe-decision.json.version: expected 1');
|
|
608
|
+
}
|
|
609
|
+
if (typeof decision.required !== 'boolean') {
|
|
610
|
+
issues.push('artifacts/recipe-decision.json.required: expected boolean');
|
|
611
|
+
}
|
|
612
|
+
if (typeof decision.reason !== 'string' || !decision.reason.trim()) {
|
|
613
|
+
issues.push('artifacts/recipe-decision.json.reason: expected non-empty string');
|
|
614
|
+
}
|
|
615
|
+
if (!isNonEmptyStringArray(decision.actionsChecked)) {
|
|
616
|
+
issues.push(
|
|
617
|
+
'artifacts/recipe-decision.json.actionsChecked: expected non-empty string array',
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
if (!isNonEmptyStringArray(decision.claims)) {
|
|
621
|
+
issues.push('artifacts/recipe-decision.json.claims: expected non-empty string array');
|
|
622
|
+
}
|
|
623
|
+
if (decision.recipePath !== undefined && decision.recipePath !== 'artifacts/recipe.json') {
|
|
624
|
+
issues.push(
|
|
625
|
+
'artifacts/recipe-decision.json.recipePath: expected artifacts/recipe.json when set',
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
if (decision.required === true && !hasRecipe) {
|
|
629
|
+
issues.push(
|
|
630
|
+
'artifacts/recipe-decision.json requires a recipe but artifacts/recipe.json is missing',
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
if (decision.required === false && hasRecipe) {
|
|
634
|
+
issues.push(
|
|
635
|
+
'artifacts/recipe-decision.json says recipe is not required but artifacts/recipe.json exists',
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
if (decision.required === true && decision.recipePath !== 'artifacts/recipe.json') {
|
|
639
|
+
issues.push(
|
|
640
|
+
'artifacts/recipe-decision.json.recipePath: required recipes must declare artifacts/recipe.json',
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
} catch (error) {
|
|
645
|
+
issues.push(`artifacts/recipe-decision.json: invalid JSON (${error.message})`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
validateRecipeDocumentArtifact();
|
|
649
|
+
if (
|
|
650
|
+
hasRecipe &&
|
|
651
|
+
flags.has('--require-recipe-quality-if-recipe') &&
|
|
652
|
+
!fileExists('artifacts/recipe-quality.json')
|
|
653
|
+
) {
|
|
654
|
+
issues.push('recipe.json exists but artifacts/recipe-quality.json is missing');
|
|
655
|
+
}
|
|
656
|
+
if (
|
|
657
|
+
hasRecipe &&
|
|
658
|
+
flags.has('--require-recipe-coverage-if-recipe') &&
|
|
659
|
+
!fileExists('artifacts/recipe-coverage.md')
|
|
660
|
+
) {
|
|
661
|
+
issues.push('recipe.json exists but artifacts/recipe-coverage.md is missing');
|
|
662
|
+
}
|
|
663
|
+
validateRecipeQualityArtifact();
|
|
664
|
+
|
|
665
|
+
const parsed = parseManifest();
|
|
666
|
+
const coverage = readText('artifacts/recipe-coverage.md');
|
|
667
|
+
if (coverage && /\|\s*(visual|mixed)\s*\|/i.test(coverage)) {
|
|
668
|
+
const refCount = parsed?.refs?.size ?? 0;
|
|
669
|
+
if (refCount === 0)
|
|
670
|
+
issues.push(
|
|
671
|
+
'recipe-coverage.md declares visual/mixed proof but evidence-manifest has no media references',
|
|
672
|
+
);
|
|
673
|
+
}
|
|
599
674
|
}
|
|
600
675
|
|
|
601
676
|
if (flags.has('--require-learnings')) {
|
|
@@ -605,8 +680,12 @@ if (flags.has('--require-learnings')) {
|
|
|
605
680
|
}
|
|
606
681
|
}
|
|
607
682
|
|
|
608
|
-
if (contractPath && existsSync(contractPath)
|
|
609
|
-
|
|
683
|
+
if (contractPath && terminalCommand && !existsSync(contractPath)) {
|
|
684
|
+
issues.push(`${contractPath}: explicit worker terminal contract missing`);
|
|
685
|
+
} else if (terminalContract && terminalCommand) {
|
|
686
|
+
const contract = isSelfReviewTerminal
|
|
687
|
+
? { ...terminalContract, whenPresent: [] }
|
|
688
|
+
: terminalContract;
|
|
610
689
|
const expanded = expandedArtifactsForCommand(contract, terminalCommand, fileExists);
|
|
611
690
|
for (const artifactPath of expanded.artifacts) {
|
|
612
691
|
if (flags.has('--skip-learnings') && artifactPath === 'artifacts/learnings.md') continue;
|
|
@@ -12,6 +12,7 @@ const ROLE_SIGNAL_SUFFIX = '-SIGNAL.json';
|
|
|
12
12
|
const SELF_REVIEW_CHECKLIST = 'SELF-REVIEW.md';
|
|
13
13
|
const SELF_REVIEW_FIX_CHECKLIST = 'SELF-REVIEW-FIX.md';
|
|
14
14
|
const CI_FIX_CHECKLIST = 'CI-FIX.md';
|
|
15
|
+
const WORKER_TERMINAL_CONTRACT_INPUT = path.join('inputs', 'worker-terminal-contract.json');
|
|
15
16
|
|
|
16
17
|
function signalFileForChecklist(checklistBasename) {
|
|
17
18
|
if (
|
|
@@ -36,6 +37,17 @@ function targetForChecklistBasename(checklistBasename) {
|
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
function terminalContractInputForChecklist(checklistBasename) {
|
|
41
|
+
if (
|
|
42
|
+
checklistBasename === TASK_PROGRESS_MARKDOWN ||
|
|
43
|
+
checklistBasename === INTERACTIVE_CHECKLIST_MARKDOWN
|
|
44
|
+
) {
|
|
45
|
+
return WORKER_TERMINAL_CONTRACT_INPUT;
|
|
46
|
+
}
|
|
47
|
+
const base = checklistBasename.replace(/\.md$/i, '');
|
|
48
|
+
return path.join('inputs', `worker-terminal-contract.${base}.json`);
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
function defaultWorkerTarget(taskDir) {
|
|
40
52
|
const checklist = fs.existsSync(path.join(taskDir, INTERACTIVE_CHECKLIST_MARKDOWN))
|
|
41
53
|
? INTERACTIVE_CHECKLIST_MARKDOWN
|
|
@@ -201,6 +213,7 @@ module.exports = {
|
|
|
201
213
|
taskDirRelPath,
|
|
202
214
|
signalFileForChecklist,
|
|
203
215
|
targetForChecklistBasename,
|
|
216
|
+
terminalContractInputForChecklist,
|
|
204
217
|
defaultWorkerTarget,
|
|
205
218
|
manifestModeTeachingError,
|
|
206
219
|
readManifest,
|