@besales/ops-framework 0.1.3 → 0.1.4
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 +7 -0
- package/README.md +14 -11
- package/bin/lib/bootstrap-utils.mjs +22 -2
- package/bin/lib/bootstrap-utils.test.mjs +7 -7
- package/bin/lib/llm-input-pack-utils.mjs +1 -10
- package/bin/lib/llm-input-pack-utils.test.mjs +3 -3
- package/bin/lib/task-manifest-utils.mjs +10 -1
- package/bin/run-check.mjs +165 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.4
|
|
4
|
+
|
|
5
|
+
- Switched generated project scripts from repeated `yarn dlx` execution to installed `ops-agent` package scripts.
|
|
6
|
+
- Added deterministic Check preflight to skip external checker calls when manifest/context quality gates already block Human Gate.
|
|
7
|
+
- Changed Check context policy to standard-first with strict escalation only when needed.
|
|
8
|
+
- Added Check timing telemetry in `task-manifest.json`.
|
|
9
|
+
|
|
3
10
|
## 0.1.0
|
|
4
11
|
|
|
5
12
|
- Created the first local shared Ops Framework package boundary.
|
package/README.md
CHANGED
|
@@ -49,16 +49,19 @@ The recommended setup is:
|
|
|
49
49
|
corepack yarn dlx @besales/ops-framework@latest init --project-name ExampleProject --install-scripts
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
This creates the project-owned `ops/**` structure, including `ops/project.ops.yaml`, task roots, cache, memory files and project-local agent config. With `--install-scripts`, it also writes package-manager scripts that call the
|
|
52
|
+
This creates the project-owned `ops/**` structure, including `ops/project.ops.yaml`, task roots, cache, memory files and project-local agent config. With `--install-scripts`, it also writes package-manager scripts that call the installed `ops-agent` bin and pins the framework package in `devDependencies`.
|
|
53
53
|
|
|
54
54
|
The generated scripts look like:
|
|
55
55
|
|
|
56
56
|
```json
|
|
57
57
|
{
|
|
58
58
|
"scripts": {
|
|
59
|
-
"ops": "
|
|
60
|
-
"agent:run-check": "
|
|
61
|
-
"agent:run-verify": "
|
|
59
|
+
"ops": "ops-agent",
|
|
60
|
+
"agent:run-check": "ops-agent run-check",
|
|
61
|
+
"agent:run-verify": "ops-agent run-verify"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@besales/ops-framework": "0.1.0"
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
67
|
```
|
|
@@ -119,13 +122,13 @@ The framework blocks obvious inefficiency early, but speculative or broad optimi
|
|
|
119
122
|
From a project root, run commands through the package entry point:
|
|
120
123
|
|
|
121
124
|
```bash
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
ops-agent init --project-name ExampleProject --install-scripts
|
|
126
|
+
ops-agent new-task TASK-001-example --title "Example task"
|
|
127
|
+
ops-agent manifest TASK-001-example
|
|
128
|
+
ops-agent preflight TASK-001-example --target execute
|
|
129
|
+
ops-agent build-check-context TASK-001-example
|
|
130
|
+
ops-agent run-check TASK-001-example
|
|
131
|
+
ops-agent run-verify TASK-001-example
|
|
129
132
|
```
|
|
130
133
|
|
|
131
134
|
When installed as a package, the intended command is:
|
|
@@ -93,9 +93,9 @@ export function buildFrameworkPackageSpec({ frameworkPackage = null, frameworkVe
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
export function buildOpsScripts(packageSpec) {
|
|
96
|
-
const run = (command) => `
|
|
96
|
+
const run = (command) => `ops-agent ${command}`;
|
|
97
97
|
return {
|
|
98
|
-
ops:
|
|
98
|
+
ops: 'ops-agent',
|
|
99
99
|
'agent:build-check-context': run('build-check-context'),
|
|
100
100
|
'agent:validate-check-artifacts': run('validate-check-artifacts'),
|
|
101
101
|
'agent:manifest': run('manifest'),
|
|
@@ -139,10 +139,30 @@ function upsertPackageScripts({ packageJsonPath, packageSpec, changes }) {
|
|
|
139
139
|
...(packageJson.scripts || {}),
|
|
140
140
|
...buildOpsScripts(packageSpec),
|
|
141
141
|
};
|
|
142
|
+
const dependency = buildFrameworkPackageDependency(packageSpec);
|
|
143
|
+
packageJson.devDependencies = {
|
|
144
|
+
...(packageJson.devDependencies || {}),
|
|
145
|
+
[dependency.name]: dependency.version,
|
|
146
|
+
};
|
|
142
147
|
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
143
148
|
changes.push({ path: packageJsonPath, kind: 'file', status: 'overwritten' });
|
|
144
149
|
}
|
|
145
150
|
|
|
151
|
+
function buildFrameworkPackageDependency(packageSpec) {
|
|
152
|
+
const spec = String(packageSpec || '@besales/ops-framework').trim();
|
|
153
|
+
const atIndex = spec.startsWith('@') ? spec.indexOf('@', 1) : spec.indexOf('@');
|
|
154
|
+
if (atIndex > 0) {
|
|
155
|
+
return {
|
|
156
|
+
name: spec.slice(0, atIndex),
|
|
157
|
+
version: spec.slice(atIndex + 1) || 'latest',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
name: spec,
|
|
162
|
+
version: 'latest',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
146
166
|
export function createTask({
|
|
147
167
|
projectRoot = process.cwd(),
|
|
148
168
|
taskId,
|
|
@@ -56,7 +56,7 @@ describe('bootstrap utilities', () => {
|
|
|
56
56
|
expect(() => assertPortableGeneratedConfig({ projectConfigContent, agentsConfigContent })).not.toThrow();
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
it('can install
|
|
59
|
+
it('can install local ops-agent scripts with a pinned package dependency', () => {
|
|
60
60
|
const root = makeTempProject();
|
|
61
61
|
fs.writeFileSync(path.join(root, 'package.json'), `${JSON.stringify({
|
|
62
62
|
name: 'fixture-project',
|
|
@@ -79,10 +79,10 @@ describe('bootstrap utilities', () => {
|
|
|
79
79
|
|
|
80
80
|
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
81
81
|
expect(packageJson.scripts.test).toBe('echo ok');
|
|
82
|
-
expect(packageJson.scripts.ops).toBe('
|
|
83
|
-
expect(packageJson.scripts['agent:run-verify']).toBe('
|
|
82
|
+
expect(packageJson.scripts.ops).toBe('ops-agent');
|
|
83
|
+
expect(packageJson.scripts['agent:run-verify']).toBe('ops-agent run-verify');
|
|
84
84
|
expect(packageJson.dependencies.leftpad).toBe('1.0.0');
|
|
85
|
-
expect(packageJson.devDependencies?.['@besales/ops-framework']).
|
|
85
|
+
expect(packageJson.devDependencies?.['@besales/ops-framework']).toBe('0.1.0');
|
|
86
86
|
});
|
|
87
87
|
|
|
88
88
|
it('creates a minimal task artifact set', () => {
|
|
@@ -136,9 +136,9 @@ describe('buildOpsScripts', () => {
|
|
|
136
136
|
it('generates the standard project script surface', () => {
|
|
137
137
|
const scripts = buildOpsScripts('@besales/ops-framework@0.1.0');
|
|
138
138
|
|
|
139
|
-
expect(scripts.ops).toBe('
|
|
140
|
-
expect(scripts['agent:quality-gates']).toBe('
|
|
141
|
-
expect(scripts['agent:test']).toBe('
|
|
139
|
+
expect(scripts.ops).toBe('ops-agent');
|
|
140
|
+
expect(scripts['agent:quality-gates']).toBe('ops-agent quality-gates');
|
|
141
|
+
expect(scripts['agent:test']).toBe('ops-agent test/self-test');
|
|
142
142
|
});
|
|
143
143
|
});
|
|
144
144
|
|
|
@@ -23,13 +23,6 @@ const MEMORY_MAX_CHARS = {
|
|
|
23
23
|
strict: Infinity,
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const STRICT_TRIGGER_PATTERNS = [
|
|
27
|
-
'auth-security',
|
|
28
|
-
'prisma-schema',
|
|
29
|
-
'production-runtime',
|
|
30
|
-
'source-sync-provider',
|
|
31
|
-
];
|
|
32
|
-
|
|
33
26
|
const CHECK_RELEVANT_SECTIONS = [
|
|
34
27
|
'цель',
|
|
35
28
|
'goal',
|
|
@@ -157,9 +150,7 @@ export function resolveLlmContextMode({ requestedMode, riskTriggers = [] } = {})
|
|
|
157
150
|
if (requested) {
|
|
158
151
|
return requested;
|
|
159
152
|
}
|
|
160
|
-
return
|
|
161
|
-
? 'strict'
|
|
162
|
-
: 'standard';
|
|
153
|
+
return 'standard';
|
|
163
154
|
}
|
|
164
155
|
|
|
165
156
|
export function normalizeLlmContextMode(value) {
|
|
@@ -202,9 +202,9 @@ describe('llm input pack utilities', () => {
|
|
|
202
202
|
expect(pack.estimatedTokens).toBeGreaterThan(Math.ceil(value.length / 2.3));
|
|
203
203
|
});
|
|
204
204
|
|
|
205
|
-
it('defaults
|
|
206
|
-
expect(resolveLlmContextMode({ riskTriggers: ['production-runtime'] })).toBe('
|
|
207
|
-
expect(resolveLlmContextMode({ riskTriggers: ['source-sync-provider'] })).toBe('
|
|
205
|
+
it('defaults to standard context and escalates only after context_insufficient', () => {
|
|
206
|
+
expect(resolveLlmContextMode({ riskTriggers: ['production-runtime'] })).toBe('standard');
|
|
207
|
+
expect(resolveLlmContextMode({ riskTriggers: ['source-sync-provider'] })).toBe('standard');
|
|
208
208
|
expect(resolveLlmContextMode({ riskTriggers: ['panel-ui'] })).toBe('standard');
|
|
209
209
|
expect(resolveLlmContextMode({ requestedMode: 'fast', riskTriggers: ['production-runtime'] })).toBe('fast');
|
|
210
210
|
});
|
|
@@ -234,7 +234,15 @@ export function writeTaskManifest(taskDir, manifest) {
|
|
|
234
234
|
fs.writeFileSync(path.join(taskDir, TASK_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
export function recordLlmInputUsage({
|
|
237
|
+
export function recordLlmInputUsage({
|
|
238
|
+
taskDir,
|
|
239
|
+
stage,
|
|
240
|
+
packMeta,
|
|
241
|
+
attempts = [],
|
|
242
|
+
rerunCount = 0,
|
|
243
|
+
now = new Date().toISOString(),
|
|
244
|
+
timing = null,
|
|
245
|
+
}) {
|
|
238
246
|
const existing = readExistingManifest(taskDir);
|
|
239
247
|
if (!existing) {
|
|
240
248
|
return null;
|
|
@@ -263,6 +271,7 @@ export function recordLlmInputUsage({ taskDir, stage, packMeta, attempts = [], r
|
|
|
263
271
|
rerunCount,
|
|
264
272
|
attempts: normalizedAttempts,
|
|
265
273
|
cumulativeEstimatedTokens: normalizedAttempts.reduce((sum, attempt) => sum + (attempt.estimatedTokens || 0), 0),
|
|
274
|
+
timing: timing || undefined,
|
|
266
275
|
updatedAt: now,
|
|
267
276
|
},
|
|
268
277
|
},
|
package/bin/run-check.mjs
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
resolveConfigValue,
|
|
25
25
|
resolveTaskDir,
|
|
26
26
|
sha256Json,
|
|
27
|
+
updateStatus,
|
|
27
28
|
writeTaskFile,
|
|
28
29
|
} from './lib/check-context-utils.mjs';
|
|
29
30
|
import {
|
|
@@ -38,7 +39,12 @@ import {
|
|
|
38
39
|
resolveLlmContextMode,
|
|
39
40
|
summarizePackForConsole,
|
|
40
41
|
} from './lib/llm-input-pack-utils.mjs';
|
|
41
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
buildTaskManifest,
|
|
44
|
+
recordLlmInputUsage,
|
|
45
|
+
validateManifest,
|
|
46
|
+
writeTaskManifest,
|
|
47
|
+
} from './lib/task-manifest-utils.mjs';
|
|
42
48
|
|
|
43
49
|
function main() {
|
|
44
50
|
runMain().catch((error) => {
|
|
@@ -59,8 +65,25 @@ async function runMain() {
|
|
|
59
65
|
const dryRun = getFlag(args, 'dry-run', false) === true;
|
|
60
66
|
const noCache = getFlag(args, 'no-cache', false) === true;
|
|
61
67
|
const checkerConfig = resolveCheckerConfig(args);
|
|
68
|
+
const runStartedAt = new Date();
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
let checkContext = ensureFreshCheckContext(taskDir, taskId);
|
|
71
|
+
const deterministicPrecheck = syncManifestAndStatusBeforeCheck({ taskDir, taskId, checkContext });
|
|
72
|
+
checkContext = deterministicPrecheck.checkContext;
|
|
73
|
+
if (!deterministicPrecheck.ok) {
|
|
74
|
+
writeDeterministicPrecheckReturn({
|
|
75
|
+
taskDir,
|
|
76
|
+
taskId,
|
|
77
|
+
checkContext,
|
|
78
|
+
checkerConfig,
|
|
79
|
+
issues: deterministicPrecheck.issues,
|
|
80
|
+
startedAt: runStartedAt,
|
|
81
|
+
});
|
|
82
|
+
runValidator(taskArg);
|
|
83
|
+
console.log(`Checker preflight blocked ${taskId}: return_to_plan`);
|
|
84
|
+
console.log(`- deterministicIssues: ${deterministicPrecheck.issues.length}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
64
87
|
const initialContextMode = resolveLlmContextMode({
|
|
65
88
|
requestedMode: getFlag(args, 'context-mode') || getFlag(args, 'llm-context-mode'),
|
|
66
89
|
riskTriggers: checkContext.riskTriggers,
|
|
@@ -152,6 +175,7 @@ async function runMain() {
|
|
|
152
175
|
packMeta: promptPayload.pack.meta,
|
|
153
176
|
attempts: llmInputAttempts,
|
|
154
177
|
rerunCount,
|
|
178
|
+
timing: buildTiming(runStartedAt),
|
|
155
179
|
});
|
|
156
180
|
runValidator(taskArg);
|
|
157
181
|
console.log(`Checker blocked for ${taskId}: strict context pack over cap`);
|
|
@@ -166,6 +190,7 @@ async function runMain() {
|
|
|
166
190
|
packMeta: promptPayload.pack.meta,
|
|
167
191
|
attempts: llmInputAttempts,
|
|
168
192
|
rerunCount,
|
|
193
|
+
timing: buildTiming(runStartedAt),
|
|
169
194
|
});
|
|
170
195
|
console.log(`Checker cache hit for ${taskId}: ${cacheKeySha}`);
|
|
171
196
|
return;
|
|
@@ -197,6 +222,7 @@ async function runMain() {
|
|
|
197
222
|
packMeta: promptPayload.pack.meta,
|
|
198
223
|
attempts: llmInputAttempts,
|
|
199
224
|
rerunCount,
|
|
225
|
+
timing: buildTiming(runStartedAt),
|
|
200
226
|
});
|
|
201
227
|
runValidator(taskArg);
|
|
202
228
|
throw new Error(`Checker provider failed with ${failureReason}: ${error.message}`);
|
|
@@ -229,6 +255,7 @@ async function runMain() {
|
|
|
229
255
|
packMeta: promptPayload.pack.meta,
|
|
230
256
|
attempts: llmInputAttempts,
|
|
231
257
|
rerunCount,
|
|
258
|
+
timing: buildTiming(runStartedAt),
|
|
232
259
|
});
|
|
233
260
|
runValidator(taskArg);
|
|
234
261
|
console.log(`Checker run completed for ${taskId}: ${providerOutput.checkResultJson?.verdict}`);
|
|
@@ -247,6 +274,142 @@ function buildAttemptRecord(packMeta, outcome) {
|
|
|
247
274
|
};
|
|
248
275
|
}
|
|
249
276
|
|
|
277
|
+
function buildTiming(startedAt, completedAt = new Date()) {
|
|
278
|
+
return {
|
|
279
|
+
startedAt: startedAt.toISOString(),
|
|
280
|
+
completedAt: completedAt.toISOString(),
|
|
281
|
+
durationMs: Math.max(0, completedAt.getTime() - startedAt.getTime()),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function syncManifestAndStatusBeforeCheck({ taskDir, taskId, checkContext }) {
|
|
286
|
+
updateStatus(taskDir, {
|
|
287
|
+
planSha: `\`${checkContext.planSha}\``,
|
|
288
|
+
memorySha: `\`${checkContext.memorySha}\``,
|
|
289
|
+
checkResult: '- `check.result.json`: pending_current_check',
|
|
290
|
+
supervisorAction: 'Synchronized manifest/status before Check.',
|
|
291
|
+
nextStep: 'Run Check or fix deterministic plan quality gates before Human Gate.',
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const syncedCheckContext = ensureFreshCheckContext(taskDir, taskId);
|
|
295
|
+
const manifest = buildTaskManifest({ taskDir });
|
|
296
|
+
writeTaskManifest(taskDir, manifest);
|
|
297
|
+
|
|
298
|
+
const issues = validateManifest(manifest).map((message) => ({
|
|
299
|
+
category: 'manifest_validation',
|
|
300
|
+
message,
|
|
301
|
+
}));
|
|
302
|
+
if (!manifest.context.checkContextCurrent) {
|
|
303
|
+
issues.push({
|
|
304
|
+
category: 'stale_check_context',
|
|
305
|
+
message: 'check-context.json/checker-context-pack.md is stale after synchronization.',
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
for (const signal of manifest.qualitySignals || []) {
|
|
309
|
+
issues.push({
|
|
310
|
+
category: 'plan_quality_gate',
|
|
311
|
+
message: signal,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
ok: issues.length === 0,
|
|
317
|
+
manifest,
|
|
318
|
+
issues,
|
|
319
|
+
checkContext: syncedCheckContext,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function writeDeterministicPrecheckReturn({
|
|
324
|
+
taskDir,
|
|
325
|
+
taskId,
|
|
326
|
+
checkContext,
|
|
327
|
+
checkerConfig,
|
|
328
|
+
issues,
|
|
329
|
+
startedAt,
|
|
330
|
+
}) {
|
|
331
|
+
const findings = issues.map((issue, index) => ({
|
|
332
|
+
id: `F-${String(index + 1).padStart(3, '0')}`,
|
|
333
|
+
severity: 'blocking',
|
|
334
|
+
claimCategory: issue.category === 'plan_quality_gate' ? 'risk_unaddressed' : 'missing_evidence',
|
|
335
|
+
claim: issue.message,
|
|
336
|
+
evidenceRefs: [
|
|
337
|
+
{
|
|
338
|
+
type: 'file',
|
|
339
|
+
ref: issue.category === 'plan_quality_gate' ? 'plan.md' : 'task-manifest.json',
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
affectedPlanSections: ['Plan/Check'],
|
|
343
|
+
expectedCorrection: expectedCorrectionForPrecheckIssue(issue),
|
|
344
|
+
}));
|
|
345
|
+
const result = {
|
|
346
|
+
taskId,
|
|
347
|
+
stage: 'Check',
|
|
348
|
+
checkerProvider: 'deterministic-precheck',
|
|
349
|
+
checkerModel: 'none',
|
|
350
|
+
planSha: checkContext.planSha,
|
|
351
|
+
memorySha: checkContext.memorySha,
|
|
352
|
+
riskProfile: checkContext.riskProfile,
|
|
353
|
+
verdict: 'return_to_plan',
|
|
354
|
+
failureReason: null,
|
|
355
|
+
blockingFindings: findings.length,
|
|
356
|
+
nonBlockingFindings: 0,
|
|
357
|
+
humanQuestions: 0,
|
|
358
|
+
findings,
|
|
359
|
+
readyForHumanGate: false,
|
|
360
|
+
createdAt: new Date().toISOString(),
|
|
361
|
+
};
|
|
362
|
+
const markdown = [
|
|
363
|
+
'# Check',
|
|
364
|
+
'',
|
|
365
|
+
'## итоговая оценка',
|
|
366
|
+
'',
|
|
367
|
+
'`return_to_plan` from deterministic pre-check.',
|
|
368
|
+
'',
|
|
369
|
+
'External checker was not invoked because machine-readable plan/context gates already found blocking issues.',
|
|
370
|
+
'',
|
|
371
|
+
'## structured findings',
|
|
372
|
+
'',
|
|
373
|
+
'| ID | Severity | Category | Claim | Expected correction |',
|
|
374
|
+
'| --- | --- | --- | --- | --- |',
|
|
375
|
+
...findings.map((finding) => `| ${finding.id} | ${finding.severity} | ${finding.claimCategory} | ${escapeTableCell(finding.claim)} | ${escapeTableCell(finding.expectedCorrection)} |`),
|
|
376
|
+
'',
|
|
377
|
+
'## рекомендация supervisor',
|
|
378
|
+
'',
|
|
379
|
+
'`return_to_plan`',
|
|
380
|
+
'',
|
|
381
|
+
'## Timing',
|
|
382
|
+
'',
|
|
383
|
+
`- Duration: ${buildTiming(startedAt).durationMs}ms`,
|
|
384
|
+
].join('\n');
|
|
385
|
+
|
|
386
|
+
writeTaskFile(taskDir, 'check.md', markdown);
|
|
387
|
+
writeTaskFile(taskDir, 'check.result.json', JSON.stringify(result, null, 2));
|
|
388
|
+
updateStatus(taskDir, {
|
|
389
|
+
checkVerdict: '`return_to_plan`',
|
|
390
|
+
checkResult: '- `check.result.json`: current',
|
|
391
|
+
supervisorAction: 'Deterministic Check preflight blocked external checker invocation.',
|
|
392
|
+
nextStep: 'Fix deterministic Check findings, then rerun Check.',
|
|
393
|
+
humanApproval: 'no',
|
|
394
|
+
});
|
|
395
|
+
ensureFreshCheckContext(taskDir, taskId);
|
|
396
|
+
appendOrchestrationLog(taskDir, `deterministic Check preflight returned return_to_plan; findings=${findings.length}; external checker skipped`);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function expectedCorrectionForPrecheckIssue(issue) {
|
|
400
|
+
if (issue.category === 'plan_quality_gate') {
|
|
401
|
+
return 'Update plan.md so deterministic quality gates are complete before external Check.';
|
|
402
|
+
}
|
|
403
|
+
if (issue.category === 'stale_check_context') {
|
|
404
|
+
return 'Regenerate check context and task manifest before rerunning Check.';
|
|
405
|
+
}
|
|
406
|
+
return 'Fix task-manifest/check-context consistency before external Check.';
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function escapeTableCell(value) {
|
|
410
|
+
return String(value || '').replace(/\|/g, '\\|').replace(/\n/g, ' ').trim();
|
|
411
|
+
}
|
|
412
|
+
|
|
250
413
|
function buildCheckerCacheKey({
|
|
251
414
|
taskId,
|
|
252
415
|
checkContext,
|