mustflow 2.18.0 → 2.18.2
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 +2 -2
- package/dist/cli/commands/explain-verify.js +2 -2
- package/dist/cli/commands/run.js +74 -97
- package/dist/cli/commands/verify.js +148 -44
- package/dist/cli/i18n/en.js +3 -1
- package/dist/cli/i18n/es.js +3 -1
- package/dist/cli/i18n/fr.js +3 -1
- package/dist/cli/i18n/hi.js +3 -1
- package/dist/cli/i18n/ko.js +3 -1
- package/dist/cli/i18n/zh.js +3 -1
- package/dist/cli/lib/repo-map.js +10 -3
- package/dist/core/atomic-state-write.js +31 -0
- package/dist/core/bounded-output.js +23 -1
- package/dist/core/check-issues.js +1 -0
- package/dist/core/command-contract-validation.js +57 -7
- package/dist/core/completion-verdict.js +2 -1
- package/dist/core/public-json-contracts.js +1 -1
- package/dist/core/run-receipt.js +20 -13
- package/dist/core/source-anchors.js +96 -24
- package/dist/core/verification-evidence.js +4 -1
- package/package.json +1 -1
- package/schemas/README.md +1 -1
- package/schemas/run-receipt.schema.json +26 -3
- package/schemas/verify-report.schema.json +1 -1
- package/schemas/verify-run-manifest.schema.json +1 -1
- package/templates/default/manifest.toml +1 -1
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { COMMAND_LIFECYCLES, COMMAND_RUN_POLICIES, LONG_RUNNING_LIFECYCLES, isRecord, } from './config-loading.js';
|
|
2
|
-
import { COMMAND_ENV_POLICIES } from './command-env.js';
|
|
2
|
+
import { COMMAND_ENV_POLICIES, DEFAULT_COMMAND_ENV_POLICY } from './command-env.js';
|
|
3
3
|
import { COMMAND_EFFECT_CONCURRENCY, COMMAND_EFFECT_MODES, COMMAND_EFFECT_TYPES, validateCommandEffectLockWarnings, validateCommandEffects, } from './command-effects.js';
|
|
4
4
|
import { commandIntentBlockedCommandPattern, commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
|
|
5
5
|
import { MAX_COMMAND_OUTPUT_BYTES, commandMaxOutputBytesLimitMessage } from './command-output-limits.js';
|
|
6
6
|
function commandContractIssue(message) {
|
|
7
7
|
return { message };
|
|
8
8
|
}
|
|
9
|
+
function commandContractWarning(message) {
|
|
10
|
+
return { message, severity: 'warning' };
|
|
11
|
+
}
|
|
9
12
|
function hasOwn(table, key) {
|
|
10
13
|
return Object.prototype.hasOwnProperty.call(table, key);
|
|
11
14
|
}
|
|
@@ -198,6 +201,50 @@ function validateCommandIntent(intentName, intent, issues) {
|
|
|
198
201
|
}
|
|
199
202
|
validateCommandIntentEffects(intentName, intent, issues);
|
|
200
203
|
}
|
|
204
|
+
function readValidCommandEnvPolicy(table) {
|
|
205
|
+
if (!table || !hasOwn(table, 'env_policy')) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
const value = table.env_policy;
|
|
209
|
+
return typeof value === 'string' && COMMAND_ENV_POLICIES.has(value)
|
|
210
|
+
? value
|
|
211
|
+
: undefined;
|
|
212
|
+
}
|
|
213
|
+
function getEffectiveCommandEnvPolicy(defaults, intent) {
|
|
214
|
+
const intentPolicy = readValidCommandEnvPolicy(intent);
|
|
215
|
+
if (intentPolicy) {
|
|
216
|
+
return { policy: intentPolicy, source: 'intent' };
|
|
217
|
+
}
|
|
218
|
+
const defaultPolicy = readValidCommandEnvPolicy(defaults);
|
|
219
|
+
if (defaultPolicy) {
|
|
220
|
+
return { policy: defaultPolicy, source: 'defaults' };
|
|
221
|
+
}
|
|
222
|
+
return { policy: DEFAULT_COMMAND_ENV_POLICY, source: 'implicit' };
|
|
223
|
+
}
|
|
224
|
+
function validateCommandEnvInheritanceWarnings(commandsToml) {
|
|
225
|
+
const issues = [];
|
|
226
|
+
if (!commandsToml || !isRecord(commandsToml.intents)) {
|
|
227
|
+
return issues;
|
|
228
|
+
}
|
|
229
|
+
const defaults = isRecord(commandsToml.defaults) ? commandsToml.defaults : undefined;
|
|
230
|
+
for (const [intentName, intent] of Object.entries(commandsToml.intents)) {
|
|
231
|
+
if (!isRecord(intent) || intent.status !== 'configured' || intent.run_policy !== 'agent_allowed') {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const envPolicy = getEffectiveCommandEnvPolicy(defaults, intent);
|
|
235
|
+
if (envPolicy.policy !== 'inherit') {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const networkScope = intent.network === true ? ' with network = true' : '';
|
|
239
|
+
const migration = 'set env_policy = "minimal" or "allowlist" unless broad host state is required';
|
|
240
|
+
if (envPolicy.source === 'implicit') {
|
|
241
|
+
issues.push(commandContractWarning(`configured agent-runnable intent ${intentName} implicitly inherits the host environment${networkScope}; ${migration}`));
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
issues.push(commandContractWarning(`configured agent-runnable intent ${intentName} uses env_policy = "inherit"${networkScope}; ${migration}`));
|
|
245
|
+
}
|
|
246
|
+
return issues;
|
|
247
|
+
}
|
|
201
248
|
/**
|
|
202
249
|
* mf:anchor core.command-contract-validation
|
|
203
250
|
* purpose: Validate command intent declarations that gate agent-executable repository commands.
|
|
@@ -228,15 +275,18 @@ export function validateCommandContractConfig(commandsToml) {
|
|
|
228
275
|
}
|
|
229
276
|
export function validateCommandContractStrictDefaults(projectRoot, commandsToml) {
|
|
230
277
|
const issues = [];
|
|
231
|
-
if (!commandsToml
|
|
278
|
+
if (!commandsToml) {
|
|
232
279
|
return issues;
|
|
233
280
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
281
|
+
if (isRecord(commandsToml.defaults)) {
|
|
282
|
+
if (!hasOwn(commandsToml.defaults, 'max_output_bytes')) {
|
|
283
|
+
issues.push(commandContractIssue('[commands.defaults].max_output_bytes is required'));
|
|
284
|
+
}
|
|
285
|
+
if (!hasOwn(commandsToml.defaults, 'on_timeout')) {
|
|
286
|
+
issues.push(commandContractIssue('[commands.defaults].on_timeout is required'));
|
|
287
|
+
}
|
|
239
288
|
}
|
|
289
|
+
issues.push(...validateCommandEnvInheritanceWarnings(commandsToml));
|
|
240
290
|
issues.push(...validateCommandEffects(projectRoot, commandsToml));
|
|
241
291
|
issues.push(...validateCommandEffectLockWarnings(commandsToml));
|
|
242
292
|
return issues;
|
|
@@ -244,7 +244,8 @@ export function createDashboardCompletionVerdict(input) {
|
|
|
244
244
|
const receiptBinding = input.receiptBinding ?? emptyReceiptBindingEvidence();
|
|
245
245
|
const latestRunFailed = input.latestRunStatus === 'failed' ||
|
|
246
246
|
input.latestRunStatus === 'timed_out' ||
|
|
247
|
-
input.latestRunStatus === 'start_failed'
|
|
247
|
+
input.latestRunStatus === 'start_failed' ||
|
|
248
|
+
input.latestRunStatus === 'output_limit_exceeded';
|
|
248
249
|
let status = 'unverified';
|
|
249
250
|
let primaryReason = 'dashboard_does_not_execute_verification';
|
|
250
251
|
const blockers = [];
|
|
@@ -135,7 +135,7 @@ const PUBLIC_JSON_SCHEMA_CONTRACTS = [
|
|
|
135
135
|
{
|
|
136
136
|
id: 'verify-run-manifest',
|
|
137
137
|
schemaFile: 'verify-run-manifest.schema.json',
|
|
138
|
-
producer: '.mustflow/state/runs/verify
|
|
138
|
+
producer: '.mustflow/state/runs/verify-*/manifest.json',
|
|
139
139
|
packaged: true,
|
|
140
140
|
documented: true,
|
|
141
141
|
},
|
package/dist/core/run-receipt.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
1
|
import { createHash } from 'node:crypto';
|
|
3
2
|
import path from 'node:path';
|
|
3
|
+
import { atomicWriteJsonFile, createStateRunId } from './atomic-state-write.js';
|
|
4
|
+
import { decodeUtf8Tail } from './bounded-output.js';
|
|
4
5
|
import { DEFAULT_RUN_RECEIPT_TAIL_BYTES } from './retention-policy.js';
|
|
5
6
|
import { redactSecretLikeText } from './secret-redaction.js';
|
|
6
7
|
const RUN_RECEIPT_SCHEMA_VERSION = '1';
|
|
@@ -11,13 +12,7 @@ function toPosixPath(value) {
|
|
|
11
12
|
}
|
|
12
13
|
function truncateTextByBytes(text, maxBytes) {
|
|
13
14
|
const buffer = Buffer.from(text, 'utf8');
|
|
14
|
-
|
|
15
|
-
return { text, truncated: false };
|
|
16
|
-
}
|
|
17
|
-
return {
|
|
18
|
-
text: buffer.subarray(buffer.byteLength - maxBytes).toString('utf8'),
|
|
19
|
-
truncated: true,
|
|
20
|
-
};
|
|
15
|
+
return decodeUtf8Tail(buffer, maxBytes);
|
|
21
16
|
}
|
|
22
17
|
function recordRedaction(state, field, result) {
|
|
23
18
|
if (!result.redacted) {
|
|
@@ -62,8 +57,11 @@ function summarizeOutput(output, maxOutputBytes, tailBytes, field, state) {
|
|
|
62
57
|
redaction_kinds: redaction.redactionKinds,
|
|
63
58
|
};
|
|
64
59
|
}
|
|
65
|
-
function
|
|
66
|
-
return toPosixPath(path.join(RUN_RECEIPT_DIR,
|
|
60
|
+
export function createRunReceiptRelativePath() {
|
|
61
|
+
return toPosixPath(path.join(RUN_RECEIPT_DIR, createStateRunId('run'), 'receipt.json'));
|
|
62
|
+
}
|
|
63
|
+
function getReceiptRelativePath(receiptPath) {
|
|
64
|
+
return receiptPath ?? toPosixPath(path.join(RUN_RECEIPT_DIR, LATEST_RUN_RECEIPT));
|
|
67
65
|
}
|
|
68
66
|
function stableJson(value) {
|
|
69
67
|
if (Array.isArray(value)) {
|
|
@@ -100,6 +98,9 @@ function getErrorKind(status, exitCode) {
|
|
|
100
98
|
if (status === 'start_failed') {
|
|
101
99
|
return 'start_failed';
|
|
102
100
|
}
|
|
101
|
+
if (status === 'output_limit_exceeded') {
|
|
102
|
+
return 'output_limit_exceeded';
|
|
103
|
+
}
|
|
103
104
|
if (status === 'failed' && exitCode !== null) {
|
|
104
105
|
return 'exit_code';
|
|
105
106
|
}
|
|
@@ -240,6 +241,7 @@ export function createRunReceipt(input) {
|
|
|
240
241
|
signal: input.signal,
|
|
241
242
|
error,
|
|
242
243
|
kill_method: input.killMethod,
|
|
244
|
+
...(input.termination ? { termination: input.termination } : {}),
|
|
243
245
|
stdout,
|
|
244
246
|
stderr,
|
|
245
247
|
write_drift: input.writeDrift,
|
|
@@ -272,12 +274,17 @@ export function createRunReceipt(input) {
|
|
|
272
274
|
redaction_kinds: [...redactionState.kinds].sort(),
|
|
273
275
|
fields: [...redactionState.fields].sort(),
|
|
274
276
|
},
|
|
275
|
-
receipt_path: getReceiptRelativePath(),
|
|
277
|
+
receipt_path: getReceiptRelativePath(input.receiptPath),
|
|
276
278
|
};
|
|
277
279
|
}
|
|
278
280
|
export function writeRunReceipt(projectRoot, receipt) {
|
|
279
281
|
const receiptDir = path.join(projectRoot, RUN_RECEIPT_DIR);
|
|
280
282
|
const latestPath = path.join(receiptDir, LATEST_RUN_RECEIPT);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
+
const receiptPath = path.resolve(projectRoot, receipt.receipt_path);
|
|
284
|
+
const relativeToRunDir = path.relative(receiptDir, receiptPath);
|
|
285
|
+
if (relativeToRunDir.startsWith('..') || path.isAbsolute(relativeToRunDir)) {
|
|
286
|
+
throw new Error(`Run receipt path must stay inside ${RUN_RECEIPT_DIR}`);
|
|
287
|
+
}
|
|
288
|
+
atomicWriteJsonFile(receiptPath, receipt);
|
|
289
|
+
atomicWriteJsonFile(latestPath, receipt);
|
|
283
290
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { SECRET_LIKE_PATTERNS, textContainsSecretLike } from './secret-redaction.js';
|
|
4
4
|
export const SOURCE_ANCHOR_EXTENSIONS = new Set(['.cjs', '.go', '.js', '.jsx', '.mjs', '.py', '.rs', '.ts', '.tsx']);
|
|
@@ -49,23 +49,94 @@ export const SOURCE_ANCHOR_SECRET_LIKE_PATTERNS = SECRET_LIKE_PATTERNS;
|
|
|
49
49
|
function toPosixPath(value) {
|
|
50
50
|
return value.split(path.sep).join('/');
|
|
51
51
|
}
|
|
52
|
-
function
|
|
52
|
+
function pathIsInsideRoot(rootRealPath, candidateRealPath) {
|
|
53
|
+
const relative = path.relative(rootRealPath, candidateRealPath);
|
|
54
|
+
return relative.length === 0 || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
55
|
+
}
|
|
56
|
+
function shouldIncludeSourceAnchorFile(relativePath, options) {
|
|
57
|
+
if (!options.allowedExtensions.has(path.posix.extname(relativePath))) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (options.excludeGeneratedOrVendor && sourceAnchorPathIsGeneratedOrVendor(relativePath)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (options.include.length > 0 && !matchesAnyGlob(relativePath, options.include)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (options.exclude.length > 0 && matchesAnyGlob(relativePath, options.exclude)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
function fileIsWithinSizeLimit(filePath, maxFileBytes) {
|
|
72
|
+
if (typeof maxFileBytes !== 'number' || !Number.isFinite(maxFileBytes) || maxFileBytes <= 0) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return statSync(filePath).size <= maxFileBytes;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function listFilesRecursive(root, options, current = root) {
|
|
53
83
|
if (!existsSync(current)) {
|
|
54
84
|
return [];
|
|
55
85
|
}
|
|
86
|
+
const currentRealPath = realpathSync(current);
|
|
87
|
+
if (!pathIsInsideRoot(options.rootRealPath, currentRealPath) || options.visitedRealDirectories.has(currentRealPath)) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
options.visitedRealDirectories.add(currentRealPath);
|
|
56
91
|
const files = [];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
92
|
+
const entries = readdirSync(current, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
const entryPath = path.join(current, entry.name);
|
|
95
|
+
if (options.ignoredDirectoryNames.has(entry.name)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (entry.isDirectory()) {
|
|
99
|
+
files.push(...listFilesRecursive(root, options, entryPath));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (entry.isSymbolicLink()) {
|
|
103
|
+
if (!options.followSymlinks) {
|
|
62
104
|
continue;
|
|
63
105
|
}
|
|
64
|
-
|
|
106
|
+
let realPath;
|
|
107
|
+
try {
|
|
108
|
+
realPath = realpathSync(entryPath);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (!pathIsInsideRoot(options.rootRealPath, realPath)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
let stat;
|
|
117
|
+
try {
|
|
118
|
+
stat = statSync(entryPath);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (stat.isDirectory()) {
|
|
124
|
+
files.push(...listFilesRecursive(root, options, entryPath));
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (stat.isFile()) {
|
|
128
|
+
const relativePath = toPosixPath(path.relative(root, entryPath));
|
|
129
|
+
if (shouldIncludeSourceAnchorFile(relativePath, options) && fileIsWithinSizeLimit(entryPath, options.maxFileBytes)) {
|
|
130
|
+
files.push(relativePath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
65
133
|
continue;
|
|
66
134
|
}
|
|
67
|
-
if (
|
|
68
|
-
|
|
135
|
+
if (entry.isFile()) {
|
|
136
|
+
const relativePath = toPosixPath(path.relative(root, entryPath));
|
|
137
|
+
if (shouldIncludeSourceAnchorFile(relativePath, options) && fileIsWithinSizeLimit(entryPath, options.maxFileBytes)) {
|
|
138
|
+
files.push(relativePath);
|
|
139
|
+
}
|
|
69
140
|
}
|
|
70
141
|
}
|
|
71
142
|
return files.sort((left, right) => left.localeCompare(right));
|
|
@@ -127,25 +198,26 @@ function normalizeAllowedExtensions(allowedExtensions) {
|
|
|
127
198
|
function mergeIgnoredDirectoryNames(ignoredDirectoryNames) {
|
|
128
199
|
return new Set([...(ignoredDirectoryNames ?? []), ...SOURCE_ANCHOR_DEFAULT_EXCLUDED_PATH_PARTS]);
|
|
129
200
|
}
|
|
130
|
-
function fileIsWithinSizeLimit(root, relativePath, maxFileBytes) {
|
|
131
|
-
if (typeof maxFileBytes !== 'number' || !Number.isFinite(maxFileBytes) || maxFileBytes <= 0) {
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
const filePath = path.join(root, ...relativePath.split('/'));
|
|
135
|
-
return statSync(filePath).size <= maxFileBytes;
|
|
136
|
-
}
|
|
137
201
|
export function listSourceAnchorFiles(root, options = {}) {
|
|
202
|
+
if (!existsSync(root)) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
138
205
|
const ignoredDirectoryNames = mergeIgnoredDirectoryNames(options.ignoredDirectoryNames);
|
|
139
206
|
const allowedExtensions = normalizeAllowedExtensions(options.allowedExtensions);
|
|
140
207
|
const include = (options.include ?? []).map((pattern) => globToRegExp(pattern));
|
|
141
208
|
const exclude = (options.exclude ?? []).map((pattern) => globToRegExp(pattern));
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
.
|
|
209
|
+
const rootRealPath = realpathSync(root);
|
|
210
|
+
return listFilesRecursive(root, {
|
|
211
|
+
ignoredDirectoryNames,
|
|
212
|
+
allowedExtensions,
|
|
213
|
+
include,
|
|
214
|
+
exclude,
|
|
215
|
+
excludeGeneratedOrVendor: options.excludeGeneratedOrVendor === true,
|
|
216
|
+
maxFileBytes: options.maxFileBytes,
|
|
217
|
+
followSymlinks: options.followSymlinks === true,
|
|
218
|
+
rootRealPath,
|
|
219
|
+
visitedRealDirectories: new Set(),
|
|
220
|
+
});
|
|
149
221
|
}
|
|
150
222
|
export function stripSourceAnchorCommentPrefix(line) {
|
|
151
223
|
return line
|
|
@@ -28,7 +28,10 @@ function resultForIntent(results, intent) {
|
|
|
28
28
|
function requirementOutcome(input) {
|
|
29
29
|
if (input.selectedIntents.some((intent) => {
|
|
30
30
|
const result = resultForIntent(input.results, intent);
|
|
31
|
-
return result?.status === 'failed' ||
|
|
31
|
+
return (result?.status === 'failed' ||
|
|
32
|
+
result?.status === 'timed_out' ||
|
|
33
|
+
result?.status === 'start_failed' ||
|
|
34
|
+
result?.status === 'output_limit_exceeded');
|
|
32
35
|
})) {
|
|
33
36
|
return 'contradicted';
|
|
34
37
|
}
|
package/package.json
CHANGED
package/schemas/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Current schemas:
|
|
|
37
37
|
- `verify-report.schema.json`: output of `mf verify --reason <event> --json`, including an
|
|
38
38
|
explicit execution aggregate, evidence-based completion verdict, and evidence model with a
|
|
39
39
|
conservative coverage matrix for the selected receipts and skipped checks
|
|
40
|
-
- `verify-run-manifest.schema.json`: `.mustflow/state/runs/verify
|
|
40
|
+
- `verify-run-manifest.schema.json`: `.mustflow/state/runs/verify-*/manifest.json`, including
|
|
41
41
|
the same execution aggregate, completion verdict, evidence model, and coverage matrix as the verify report
|
|
42
42
|
- `change-verification-report.schema.json`: output of `mf verify --reason <event> --plan-only --json` and
|
|
43
43
|
`mf verify --from-classification <classify-report.json> --plan-only --json`, including the `decision_graph` that links
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"schema_version": { "const": "1" },
|
|
36
36
|
"command": { "const": "run" },
|
|
37
37
|
"intent": { "type": "string" },
|
|
38
|
-
"status": { "enum": ["passed", "failed", "timed_out", "start_failed"] },
|
|
38
|
+
"status": { "enum": ["passed", "failed", "timed_out", "start_failed", "output_limit_exceeded"] },
|
|
39
39
|
"timed_out": { "type": "boolean" },
|
|
40
40
|
"started_at": { "type": "string", "format": "date-time" },
|
|
41
41
|
"finished_at": { "type": "string", "format": "date-time" },
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"signal": { "type": ["string", "null"] },
|
|
65
65
|
"error": { "type": ["string", "null"] },
|
|
66
66
|
"kill_method": { "type": ["string", "null"] },
|
|
67
|
+
"termination": { "$ref": "#/$defs/termination" },
|
|
67
68
|
"stdout": { "$ref": "#/$defs/output" },
|
|
68
69
|
"stderr": { "$ref": "#/$defs/output" },
|
|
69
70
|
"write_drift": { "$ref": "#/$defs/writeDrift" },
|
|
@@ -182,10 +183,10 @@
|
|
|
182
183
|
"additionalProperties": false,
|
|
183
184
|
"required": ["status", "exit_code_class", "timed_out", "error_kind"],
|
|
184
185
|
"properties": {
|
|
185
|
-
"status": { "enum": ["passed", "failed", "timed_out", "start_failed"] },
|
|
186
|
+
"status": { "enum": ["passed", "failed", "timed_out", "start_failed", "output_limit_exceeded"] },
|
|
186
187
|
"exit_code_class": { "enum": ["success", "failure", "no_exit_code"] },
|
|
187
188
|
"timed_out": { "type": "boolean" },
|
|
188
|
-
"error_kind": { "enum": ["timeout", "start_failed", "exit_code", null] }
|
|
189
|
+
"error_kind": { "enum": ["timeout", "start_failed", "output_limit_exceeded", "exit_code", null] }
|
|
189
190
|
}
|
|
190
191
|
},
|
|
191
192
|
"quality": {
|
|
@@ -200,6 +201,28 @@
|
|
|
200
201
|
}
|
|
201
202
|
}
|
|
202
203
|
},
|
|
204
|
+
"termination": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"additionalProperties": false,
|
|
207
|
+
"required": [
|
|
208
|
+
"reason",
|
|
209
|
+
"method",
|
|
210
|
+
"graceful_signal",
|
|
211
|
+
"forced_signal",
|
|
212
|
+
"forced_kill_attempted",
|
|
213
|
+
"confirmed",
|
|
214
|
+
"cleanup_pending"
|
|
215
|
+
],
|
|
216
|
+
"properties": {
|
|
217
|
+
"reason": { "const": "timeout" },
|
|
218
|
+
"method": { "type": "string" },
|
|
219
|
+
"graceful_signal": { "type": ["string", "null"] },
|
|
220
|
+
"forced_signal": { "type": ["string", "null"] },
|
|
221
|
+
"forced_kill_attempted": { "type": "boolean" },
|
|
222
|
+
"confirmed": { "type": "boolean" },
|
|
223
|
+
"cleanup_pending": { "type": "boolean" }
|
|
224
|
+
}
|
|
225
|
+
},
|
|
203
226
|
"redaction": {
|
|
204
227
|
"type": "object",
|
|
205
228
|
"additionalProperties": false,
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
],
|
|
89
89
|
"properties": {
|
|
90
90
|
"intent": { "type": ["string", "null"] },
|
|
91
|
-
"status": { "enum": ["passed", "failed", "timed_out", "start_failed", "skipped"] },
|
|
91
|
+
"status": { "enum": ["passed", "failed", "timed_out", "start_failed", "output_limit_exceeded", "skipped"] },
|
|
92
92
|
"skipped": { "type": "boolean" },
|
|
93
93
|
"reason": { "type": ["string", "null"] },
|
|
94
94
|
"detail": { "type": ["string", "null"] },
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
],
|
|
266
266
|
"properties": {
|
|
267
267
|
"intent": { "type": ["string", "null"] },
|
|
268
|
-
"status": { "enum": ["passed", "failed", "timed_out", "start_failed", "skipped"] },
|
|
268
|
+
"status": { "enum": ["passed", "failed", "timed_out", "start_failed", "output_limit_exceeded", "skipped"] },
|
|
269
269
|
"skipped": { "type": "boolean" },
|
|
270
270
|
"verification_plan_id": {
|
|
271
271
|
"type": ["string", "null"],
|