@cleocode/core 2026.6.14 → 2026.6.15
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/dist/check/pr-gate.d.ts +6 -2
- package/dist/check/pr-gate.d.ts.map +1 -1
- package/dist/check/pr-gate.js +14 -32
- package/dist/check/pr-gate.js.map +1 -1
- package/dist/config/config-repair.d.ts +70 -0
- package/dist/config/config-repair.d.ts.map +1 -0
- package/dist/config/config-repair.js +248 -0
- package/dist/config/config-repair.js.map +1 -0
- package/dist/config/registry.d.ts.map +1 -1
- package/dist/config/registry.js +4 -4
- package/dist/config/registry.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -10
- package/dist/config.js.map +1 -1
- package/dist/docs/export-document.js +17 -16
- package/dist/docs/export-document.js.map +2 -2
- package/dist/gc/cleanup.d.ts +23 -8
- package/dist/gc/cleanup.d.ts.map +1 -1
- package/dist/gc/cleanup.js +287 -22
- package/dist/gc/cleanup.js.map +1 -1
- package/dist/llm/cli-ops.d.ts +16 -0
- package/dist/llm/cli-ops.d.ts.map +1 -1
- package/dist/llm/cli-ops.js +32 -5
- package/dist/llm/cli-ops.js.map +1 -1
- package/dist/llm/cross-provider-selector.d.ts +249 -0
- package/dist/llm/cross-provider-selector.d.ts.map +1 -0
- package/dist/llm/cross-provider-selector.js +572 -0
- package/dist/llm/cross-provider-selector.js.map +1 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +1 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/local-model-fit.d.ts +260 -0
- package/dist/llm/local-model-fit.d.ts.map +1 -0
- package/dist/llm/local-model-fit.js +563 -0
- package/dist/llm/local-model-fit.js.map +1 -0
- package/dist/llm/plugin-facade.js +761 -230
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/llm/provider-registry/builtin/ollama.d.ts +18 -0
- package/dist/llm/provider-registry/builtin/ollama.d.ts.map +1 -1
- package/dist/llm/provider-registry/builtin/ollama.js +20 -2
- package/dist/llm/provider-registry/builtin/ollama.js.map +1 -1
- package/dist/llm/role-resolver.d.ts.map +1 -1
- package/dist/llm/role-resolver.js +45 -10
- package/dist/llm/role-resolver.js.map +1 -1
- package/dist/llm/transports/codex-responses.d.ts +111 -88
- package/dist/llm/transports/codex-responses.d.ts.map +1 -1
- package/dist/llm/transports/codex-responses.js +456 -258
- package/dist/llm/transports/codex-responses.js.map +1 -1
- package/dist/release/index.d.ts +2 -2
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +1 -1
- package/dist/release/index.js.map +1 -1
- package/dist/release/reconcile.d.ts +97 -0
- package/dist/release/reconcile.d.ts.map +1 -1
- package/dist/release/reconcile.js +412 -5
- package/dist/release/reconcile.js.map +1 -1
- package/dist/resources/backend.d.ts +155 -0
- package/dist/resources/backend.d.ts.map +1 -0
- package/dist/resources/backend.js +23 -0
- package/dist/resources/backend.js.map +1 -0
- package/dist/resources/linux-backend.d.ts +167 -0
- package/dist/resources/linux-backend.d.ts.map +1 -0
- package/dist/resources/linux-backend.js +259 -0
- package/dist/resources/linux-backend.js.map +1 -0
- package/dist/resources/monitor.d.ts +230 -0
- package/dist/resources/monitor.d.ts.map +1 -0
- package/dist/resources/monitor.js +255 -0
- package/dist/resources/monitor.js.map +1 -0
- package/dist/resources/spawn-wrapper.d.ts +265 -0
- package/dist/resources/spawn-wrapper.d.ts.map +1 -0
- package/dist/resources/spawn-wrapper.js +304 -0
- package/dist/resources/spawn-wrapper.js.map +1 -0
- package/dist/selfimprove/budget.d.ts +11 -2
- package/dist/selfimprove/budget.d.ts.map +1 -1
- package/dist/selfimprove/budget.js +11 -2
- package/dist/selfimprove/budget.js.map +1 -1
- package/dist/selfimprove/fix-gen.d.ts +229 -0
- package/dist/selfimprove/fix-gen.d.ts.map +1 -0
- package/dist/selfimprove/fix-gen.js +266 -0
- package/dist/selfimprove/fix-gen.js.map +1 -0
- package/dist/selfimprove/run-loop.d.ts +44 -4
- package/dist/selfimprove/run-loop.d.ts.map +1 -1
- package/dist/selfimprove/run-loop.js +73 -6
- package/dist/selfimprove/run-loop.js.map +1 -1
- package/dist/sentient/cross-project-hygiene.d.ts +11 -2
- package/dist/sentient/cross-project-hygiene.d.ts.map +1 -1
- package/dist/sentient/cross-project-hygiene.js +73 -10
- package/dist/sentient/cross-project-hygiene.js.map +1 -1
- package/dist/sentient/daemon.d.ts.map +1 -1
- package/dist/sentient/daemon.js +7 -2
- package/dist/sentient/daemon.js.map +1 -1
- package/dist/sentient/worktree-dispatch.d.ts.map +1 -1
- package/dist/sentient/worktree-dispatch.js +8 -1
- package/dist/sentient/worktree-dispatch.js.map +1 -1
- package/dist/spawn/branch-lock.d.ts.map +1 -1
- package/dist/spawn/branch-lock.js +7 -1
- package/dist/spawn/branch-lock.js.map +1 -1
- package/dist/store/attachment-repair.d.ts +110 -0
- package/dist/store/attachment-repair.d.ts.map +1 -0
- package/dist/store/attachment-repair.js +326 -0
- package/dist/store/attachment-repair.js.map +1 -0
- package/dist/store/attachment-store.d.ts.map +1 -1
- package/dist/store/attachment-store.js +24 -7
- package/dist/store/attachment-store.js.map +1 -1
- package/dist/tasks/evidence.d.ts +34 -2
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +293 -35
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/verification/verify-tools.d.ts.map +1 -1
- package/dist/verification/verify-tools.js +5 -6
- package/dist/verification/verify-tools.js.map +1 -1
- package/package.json +22 -12
- package/templates/workflows/release-prepare.yml.tmpl +48 -2
package/dist/tasks/evidence.d.ts
CHANGED
|
@@ -232,27 +232,54 @@ export type AtomValidation = {
|
|
|
232
232
|
*
|
|
233
233
|
* @param parsed - Parsed atom from {@link parseEvidence}
|
|
234
234
|
* @param projectRoot - Absolute path to project root (for resolving files, git)
|
|
235
|
+
* @param taskId - Optional CLEO task ID. When provided, enables branch-scope
|
|
236
|
+
* check (T9178), content-intersect check (T9245), worktree-aware HEAD
|
|
237
|
+
* resolution (T-WT-3), and git-show file fallback for branch-only files
|
|
238
|
+
* (T11959).
|
|
235
239
|
* @returns Validation outcome with canonicalised form on success
|
|
236
240
|
*
|
|
237
241
|
* @task T832
|
|
242
|
+
* @task T11959
|
|
238
243
|
* @adr ADR-051 §3
|
|
239
244
|
*/
|
|
240
245
|
export declare function validateAtom(parsed: ParsedAtom, projectRoot: string,
|
|
241
246
|
/** T9178: When provided, validates commit is on task/<taskId> branch. */
|
|
242
247
|
taskId?: string): Promise<AtomValidation>;
|
|
248
|
+
/**
|
|
249
|
+
* Heuristic guard that decides whether a regex-extracted slash token is a
|
|
250
|
+
* plausible repository file path rather than a URL or prose fragment.
|
|
251
|
+
*
|
|
252
|
+
* A token passes when it either:
|
|
253
|
+
* - starts with a known {@link REPO_DIR_PREFIXES} directory, OR
|
|
254
|
+
* - ends with a file extension in {@link REPO_FILE_EXTENSIONS}
|
|
255
|
+
*
|
|
256
|
+
* The guard intentionally accepts false negatives (omits exotic paths) rather
|
|
257
|
+
* than false positives (includes internet hostnames). When a task owner needs
|
|
258
|
+
* an exotic path captured they can use the explicit `--files` flag on
|
|
259
|
+
* `cleo add` / `cleo update`.
|
|
260
|
+
*
|
|
261
|
+
* @param token - Slash-containing token extracted from AC text.
|
|
262
|
+
* @returns `true` when the token looks like a repo path.
|
|
263
|
+
*
|
|
264
|
+
* @internal
|
|
265
|
+
* @task T11960
|
|
266
|
+
*/
|
|
267
|
+
export declare function isRepoPathLike(token: string): boolean;
|
|
243
268
|
/**
|
|
244
269
|
* Extract the canonical list of files a task's acceptance criteria declare.
|
|
245
270
|
*
|
|
246
271
|
* Resolution order:
|
|
247
272
|
* 1. `task.files` array — authoritative when populated (set via `--files`)
|
|
248
273
|
* 2. AC string parsing — extract path-like tokens from each
|
|
249
|
-
* `task.acceptance` string
|
|
274
|
+
* `task.acceptance` string, then filter with {@link isRepoPathLike}
|
|
275
|
+
* to discard URL/prose false-positives (T11960).
|
|
250
276
|
*
|
|
251
277
|
* Returns `null` when the task declares no AC files at all — caller MUST
|
|
252
278
|
* interpret this as "skip content-intersect" rather than "verify fails".
|
|
253
279
|
*
|
|
254
280
|
* @internal
|
|
255
281
|
* @task T9245
|
|
282
|
+
* @task T11960
|
|
256
283
|
*/
|
|
257
284
|
export declare function extractTaskAcFiles(task: {
|
|
258
285
|
files?: string[] | null;
|
|
@@ -401,11 +428,16 @@ export declare const CRITICAL_GATES_NO_OVERRIDE: readonly VerificationGate[];
|
|
|
401
428
|
* @param projectRoot - Absolute path to project root
|
|
402
429
|
* @param gate - Optional gate name; when provided enables the critical-gate
|
|
403
430
|
* override-rejection check (T9245). Omit for back-compat callers.
|
|
431
|
+
* @param taskId - Optional CLEO task ID; enables git-show fallback for files
|
|
432
|
+
* that were on the task branch at verify time but are now on main after
|
|
433
|
+
* merge (T11959). When provided, sha256 comparison uses the same git-show
|
|
434
|
+
* resolution path as validate time so the checksums remain consistent.
|
|
404
435
|
* @returns Revalidation outcome
|
|
405
436
|
*
|
|
406
437
|
* @task T832
|
|
407
438
|
* @task T9245
|
|
439
|
+
* @task T11959
|
|
408
440
|
* @adr ADR-051 §5 / §8 (Decision 8)
|
|
409
441
|
*/
|
|
410
|
-
export declare function revalidateEvidence(evidence: GateEvidence, projectRoot: string, gate?: VerificationGate): Promise<RevalidationResult>;
|
|
442
|
+
export declare function revalidateEvidence(evidence: GateEvidence, projectRoot: string, gate?: VerificationGate, taskId?: string): Promise<RevalidationResult>;
|
|
411
443
|
//# sourceMappingURL=evidence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/tasks/evidence.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/tasks/evidence.ts"],"names":[],"mappings":"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"}
|
package/dist/tasks/evidence.js
CHANGED
|
@@ -134,9 +134,14 @@ export function parseEvidence(raw) {
|
|
|
134
134
|
*
|
|
135
135
|
* @param parsed - Parsed atom from {@link parseEvidence}
|
|
136
136
|
* @param projectRoot - Absolute path to project root (for resolving files, git)
|
|
137
|
+
* @param taskId - Optional CLEO task ID. When provided, enables branch-scope
|
|
138
|
+
* check (T9178), content-intersect check (T9245), worktree-aware HEAD
|
|
139
|
+
* resolution (T-WT-3), and git-show file fallback for branch-only files
|
|
140
|
+
* (T11959).
|
|
137
141
|
* @returns Validation outcome with canonicalised form on success
|
|
138
142
|
*
|
|
139
143
|
* @task T832
|
|
144
|
+
* @task T11959
|
|
140
145
|
* @adr ADR-051 §3
|
|
141
146
|
*/
|
|
142
147
|
export async function validateAtom(parsed, projectRoot,
|
|
@@ -146,7 +151,7 @@ taskId) {
|
|
|
146
151
|
case 'commit':
|
|
147
152
|
return validateCommit(parsed.sha, projectRoot, taskId);
|
|
148
153
|
case 'files':
|
|
149
|
-
return validateFiles(parsed.paths, projectRoot);
|
|
154
|
+
return validateFiles(parsed.paths, projectRoot, taskId);
|
|
150
155
|
case 'test-run':
|
|
151
156
|
return validateTestRun(parsed.path, projectRoot);
|
|
152
157
|
case 'tool':
|
|
@@ -259,29 +264,82 @@ async function validateCommit(sha, projectRoot, taskId) {
|
|
|
259
264
|
const effectiveHead = await getEffectiveHead(projectRoot, taskId);
|
|
260
265
|
const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, effectiveHead], projectRoot);
|
|
261
266
|
if (reachable.exitCode !== 0) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
+
// DHQ-083 companion (a): when the commit is not reachable from the task
|
|
268
|
+
// branch HEAD, also accept commits reachable from the canonical integration
|
|
269
|
+
// branches (main or master). This covers:
|
|
270
|
+
// - owner commits directly to main (meta-tasks, emergency fixes)
|
|
271
|
+
// - post-merge cleanup where the task branch was deleted and the SHA is
|
|
272
|
+
// now only reachable from main
|
|
273
|
+
// - the `effectiveHead = "HEAD"` case when HEAD == main tip but taskId
|
|
274
|
+
// was not supplied (handled by the no-taskId fast path via getEffectiveHead)
|
|
275
|
+
let isReachableFromMain = false;
|
|
276
|
+
if (taskId) {
|
|
277
|
+
const mainBranches = ['main', 'master'];
|
|
278
|
+
for (const base of mainBranches) {
|
|
279
|
+
const baseExists = await runCommand('git', ['rev-parse', '--verify', `refs/heads/${base}`], projectRoot);
|
|
280
|
+
if (baseExists.exitCode !== 0)
|
|
281
|
+
continue;
|
|
282
|
+
const mainReachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, base], projectRoot);
|
|
283
|
+
if (mainReachable.exitCode === 0) {
|
|
284
|
+
isReachableFromMain = true;
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (!isReachableFromMain) {
|
|
290
|
+
return {
|
|
291
|
+
ok: false,
|
|
292
|
+
reason: taskId
|
|
293
|
+
? `Commit ${sha} exists but is not reachable from ${effectiveHead} or main`
|
|
294
|
+
: `Commit ${sha} exists but is not reachable from ${effectiveHead}`,
|
|
295
|
+
codeName: 'E_EVIDENCE_INVALID',
|
|
296
|
+
};
|
|
297
|
+
}
|
|
267
298
|
}
|
|
268
299
|
// T9178: branch-scope check — reject cross-branch fabricated SHAs.
|
|
269
300
|
// When the validator is invoked in the context of a specific task, require
|
|
270
|
-
// that the supplied SHA is reachable from task/<taskId
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
//
|
|
301
|
+
// that the supplied SHA is reachable from EITHER task/<taskId> OR main/master.
|
|
302
|
+
//
|
|
303
|
+
// Accepting main-reachable commits (DHQ-083 companion a): before this fix the
|
|
304
|
+
// check hard-failed for SHAs that were on main but not yet cherry-picked /
|
|
305
|
+
// merged onto the task branch. This is a valid workflow when an owner commits
|
|
306
|
+
// directly to main (rare but legitimate for meta-tasks) or when the branch
|
|
307
|
+
// was deleted after merge. The fix extends the allowlist to include the
|
|
308
|
+
// canonical integration branches so that merged-and-cleaned-up commits remain
|
|
309
|
+
// usable as evidence without forcing the agent to use the slower `pr:` atom.
|
|
310
|
+
//
|
|
311
|
+
// The check no-ops if the task branch does not yet exist and main has no
|
|
312
|
+
// commit (early-init edge case handled by the HEAD ancestry check above).
|
|
274
313
|
if (taskId) {
|
|
275
314
|
const branchRef = `task/${taskId}`;
|
|
276
315
|
const branchExists = await runCommand('git', ['rev-parse', '--verify', branchRef], projectRoot);
|
|
277
316
|
if (branchExists.exitCode === 0) {
|
|
278
317
|
const onBranch = await runCommand('git', ['merge-base', '--is-ancestor', sha, branchRef], projectRoot);
|
|
279
318
|
if (onBranch.exitCode !== 0) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
319
|
+
// T11959 / DHQ-083 companion (a): also accept commits reachable from the
|
|
320
|
+
// canonical integration branches (main or master). This covers:
|
|
321
|
+
// - commits on main used by meta-tasks / owner-driven completions
|
|
322
|
+
// - post-merge task branches that have been cleaned up (the SHA is now
|
|
323
|
+
// only reachable from main, not from `task/<id>` which no longer exists)
|
|
324
|
+
const mainBranches = ['main', 'master'];
|
|
325
|
+
let onMain = false;
|
|
326
|
+
for (const base of mainBranches) {
|
|
327
|
+
const exists = await runCommand('git', ['rev-parse', '--verify', `refs/heads/${base}`], projectRoot);
|
|
328
|
+
if (exists.exitCode !== 0)
|
|
329
|
+
continue;
|
|
330
|
+
const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, base], projectRoot);
|
|
331
|
+
if (reachable.exitCode === 0) {
|
|
332
|
+
onMain = true;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (!onMain) {
|
|
337
|
+
return {
|
|
338
|
+
ok: false,
|
|
339
|
+
reason: `Commit ${sha} not reachable from ${branchRef} or main — possible phantom evidence`,
|
|
340
|
+
codeName: 'E_EVIDENCE_INVALID',
|
|
341
|
+
};
|
|
342
|
+
}
|
|
285
343
|
}
|
|
286
344
|
}
|
|
287
345
|
// T9245: content-intersect check — diff MUST touch at least one AC file.
|
|
@@ -305,29 +363,122 @@ async function validateCommit(sha, projectRoot, taskId) {
|
|
|
305
363
|
* SSoT is the explicit `task.files` array — string-token parsing is a
|
|
306
364
|
* fallback for legacy tasks that predate the `--files` flag.
|
|
307
365
|
*
|
|
366
|
+
* NOTE: The regex is intentionally broad. False-positive prose tokens (e.g.
|
|
367
|
+
* "claude.com/platform.claude.com") are filtered out by
|
|
368
|
+
* {@link isRepoPathLike} before being added to the AC-files set (T11960).
|
|
369
|
+
*
|
|
308
370
|
* @task T9245
|
|
371
|
+
* @task T11960
|
|
309
372
|
*/
|
|
310
373
|
const AC_PATH_TOKEN = /(?:^|[\s"'`([])([a-zA-Z0-9_\-./@]+\/[a-zA-Z0-9_\-./]+\.[a-zA-Z0-9]{1,8})(?=$|[\s"'`)\],;:])/g;
|
|
374
|
+
/**
|
|
375
|
+
* Known repo directory prefixes that indicate a token is a source path.
|
|
376
|
+
*
|
|
377
|
+
* A regex-extracted token is accepted as a path only when it starts with one
|
|
378
|
+
* of these prefixes (or has a file extension from {@link REPO_FILE_EXTENSIONS}).
|
|
379
|
+
* This rejects URL-shaped prose tokens like `claude.com/platform.claude.com`
|
|
380
|
+
* that happen to match the path-token regex.
|
|
381
|
+
*
|
|
382
|
+
* @internal
|
|
383
|
+
* @task T11960
|
|
384
|
+
*/
|
|
385
|
+
const REPO_DIR_PREFIXES = Object.freeze([
|
|
386
|
+
'packages/',
|
|
387
|
+
'src/',
|
|
388
|
+
'scripts/',
|
|
389
|
+
'docs/',
|
|
390
|
+
'crates/',
|
|
391
|
+
'test/',
|
|
392
|
+
'tests/',
|
|
393
|
+
'.cleo/',
|
|
394
|
+
'.github/',
|
|
395
|
+
'apps/',
|
|
396
|
+
'lib/',
|
|
397
|
+
'bin/',
|
|
398
|
+
]);
|
|
399
|
+
/**
|
|
400
|
+
* File extensions that unambiguously identify a repo source path.
|
|
401
|
+
*
|
|
402
|
+
* A token with one of these extensions is accepted as a path even if it does
|
|
403
|
+
* not start with a known {@link REPO_DIR_PREFIXES} directory. Internet
|
|
404
|
+
* hostnames never end in `.ts`, `.mjs`, `.sql`, etc.
|
|
405
|
+
*
|
|
406
|
+
* @internal
|
|
407
|
+
* @task T11960
|
|
408
|
+
*/
|
|
409
|
+
const REPO_FILE_EXTENSIONS = new Set([
|
|
410
|
+
'.ts',
|
|
411
|
+
'.tsx',
|
|
412
|
+
'.mts',
|
|
413
|
+
'.cts',
|
|
414
|
+
'.js',
|
|
415
|
+
'.mjs',
|
|
416
|
+
'.cjs',
|
|
417
|
+
'.jsx',
|
|
418
|
+
'.json',
|
|
419
|
+
'.toml',
|
|
420
|
+
'.yaml',
|
|
421
|
+
'.yml',
|
|
422
|
+
'.sql',
|
|
423
|
+
'.sh',
|
|
424
|
+
'.rs',
|
|
425
|
+
'.md',
|
|
426
|
+
]);
|
|
427
|
+
/**
|
|
428
|
+
* Heuristic guard that decides whether a regex-extracted slash token is a
|
|
429
|
+
* plausible repository file path rather than a URL or prose fragment.
|
|
430
|
+
*
|
|
431
|
+
* A token passes when it either:
|
|
432
|
+
* - starts with a known {@link REPO_DIR_PREFIXES} directory, OR
|
|
433
|
+
* - ends with a file extension in {@link REPO_FILE_EXTENSIONS}
|
|
434
|
+
*
|
|
435
|
+
* The guard intentionally accepts false negatives (omits exotic paths) rather
|
|
436
|
+
* than false positives (includes internet hostnames). When a task owner needs
|
|
437
|
+
* an exotic path captured they can use the explicit `--files` flag on
|
|
438
|
+
* `cleo add` / `cleo update`.
|
|
439
|
+
*
|
|
440
|
+
* @param token - Slash-containing token extracted from AC text.
|
|
441
|
+
* @returns `true` when the token looks like a repo path.
|
|
442
|
+
*
|
|
443
|
+
* @internal
|
|
444
|
+
* @task T11960
|
|
445
|
+
*/
|
|
446
|
+
export function isRepoPathLike(token) {
|
|
447
|
+
for (const prefix of REPO_DIR_PREFIXES) {
|
|
448
|
+
if (token.startsWith(prefix))
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
// Check file extension — extract the final segment after the last dot.
|
|
452
|
+
const lastDot = token.lastIndexOf('.');
|
|
453
|
+
if (lastDot >= 1) {
|
|
454
|
+
const ext = token.slice(lastDot);
|
|
455
|
+
if (REPO_FILE_EXTENSIONS.has(ext))
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
311
460
|
/**
|
|
312
461
|
* Extract the canonical list of files a task's acceptance criteria declare.
|
|
313
462
|
*
|
|
314
463
|
* Resolution order:
|
|
315
464
|
* 1. `task.files` array — authoritative when populated (set via `--files`)
|
|
316
465
|
* 2. AC string parsing — extract path-like tokens from each
|
|
317
|
-
* `task.acceptance` string
|
|
466
|
+
* `task.acceptance` string, then filter with {@link isRepoPathLike}
|
|
467
|
+
* to discard URL/prose false-positives (T11960).
|
|
318
468
|
*
|
|
319
469
|
* Returns `null` when the task declares no AC files at all — caller MUST
|
|
320
470
|
* interpret this as "skip content-intersect" rather than "verify fails".
|
|
321
471
|
*
|
|
322
472
|
* @internal
|
|
323
473
|
* @task T9245
|
|
474
|
+
* @task T11960
|
|
324
475
|
*/
|
|
325
476
|
export function extractTaskAcFiles(task) {
|
|
326
477
|
// 1. Explicit files array wins.
|
|
327
478
|
if (task.files && task.files.length > 0) {
|
|
328
479
|
return [...task.files];
|
|
329
480
|
}
|
|
330
|
-
// 2. Parse path tokens from AC strings.
|
|
481
|
+
// 2. Parse path tokens from AC strings, filtered by repo-path heuristic.
|
|
331
482
|
if (!task.acceptance || task.acceptance.length === 0) {
|
|
332
483
|
return null;
|
|
333
484
|
}
|
|
@@ -339,7 +490,8 @@ export function extractTaskAcFiles(task) {
|
|
|
339
490
|
AC_PATH_TOKEN.lastIndex = 0;
|
|
340
491
|
let m = AC_PATH_TOKEN.exec(item);
|
|
341
492
|
while (m !== null) {
|
|
342
|
-
|
|
493
|
+
// T11960: apply repo-path heuristic to filter URL/prose false-positives.
|
|
494
|
+
if (m[1] && isRepoPathLike(m[1]))
|
|
343
495
|
parsed.add(m[1]);
|
|
344
496
|
m = AC_PATH_TOKEN.exec(item);
|
|
345
497
|
}
|
|
@@ -347,14 +499,35 @@ export function extractTaskAcFiles(task) {
|
|
|
347
499
|
return parsed.size > 0 ? Array.from(parsed) : null;
|
|
348
500
|
}
|
|
349
501
|
/**
|
|
350
|
-
*
|
|
351
|
-
*
|
|
502
|
+
* Return the list of file paths a commit touched (added, modified, or deleted).
|
|
503
|
+
*
|
|
504
|
+
* Uses `git diff-tree --no-commit-id -r --name-only -m --first-parent` which
|
|
505
|
+
* correctly handles **merge commits** by diffing against the first parent only.
|
|
506
|
+
* The legacy `git show --name-only` approach returned an empty list for merge
|
|
507
|
+
* commits because show without `--first-parent` produces a combined diff that
|
|
508
|
+
* `--name-only` cannot meaningfully summarise, causing the content-intersect
|
|
509
|
+
* gate to report "touches no files" for every merge commit (DHQ-083 companion b).
|
|
510
|
+
*
|
|
511
|
+
* @param sha - Commit SHA to inspect.
|
|
512
|
+
* @param projectRoot - Working directory for git operations.
|
|
513
|
+
* @returns File paths touched by the commit; empty array on git failure.
|
|
352
514
|
*
|
|
353
515
|
* @internal
|
|
354
516
|
* @task T9245
|
|
517
|
+
* @task T11959 (DHQ-083 companion b — merge-commit first-parent diff)
|
|
355
518
|
*/
|
|
356
519
|
async function gitShowFiles(sha, projectRoot) {
|
|
357
|
-
|
|
520
|
+
// diff-tree with --first-parent gives a clean single-parent diff for both
|
|
521
|
+
// regular commits and merge commits (compares merge result to first parent,
|
|
522
|
+
// i.e. the branch tip before the PR was merged).
|
|
523
|
+
//
|
|
524
|
+
// --root: Without this flag, git diff-tree returns EMPTY output for the
|
|
525
|
+
// repository's initial (root) commit because there is no parent to diff
|
|
526
|
+
// against. Adding --root causes the root commit to be treated as a diff
|
|
527
|
+
// against the empty tree, correctly listing all files it introduced.
|
|
528
|
+
// This fixes the gate-verify-hint tests (and any real workflow) where the
|
|
529
|
+
// evidence commit happens to be the very first commit in the repo (T11959).
|
|
530
|
+
const r = await runCommand('git', ['diff-tree', '--root', '--no-commit-id', '-r', '--name-only', '-m', '--first-parent', sha], projectRoot);
|
|
358
531
|
if (r.exitCode !== 0)
|
|
359
532
|
return [];
|
|
360
533
|
return r.stdout
|
|
@@ -539,7 +712,60 @@ async function checkCommitContentIntersect(sha, taskId, projectRoot) {
|
|
|
539
712
|
// Pass — caller's outer validateCommit will produce the final atom.
|
|
540
713
|
return { ok: true, atom: { kind: 'commit', sha, shortSha: sha.slice(0, 7) } };
|
|
541
714
|
}
|
|
542
|
-
|
|
715
|
+
/**
|
|
716
|
+
* Read file content from the git object store for the given task branch.
|
|
717
|
+
*
|
|
718
|
+
* Used as a fallback when the file does not exist at `projectRoot` on disk —
|
|
719
|
+
* i.e. when the file was added/modified only on `task/<taskId>` and has not
|
|
720
|
+
* yet been merged to main (the worktree isolation scenario from T11959).
|
|
721
|
+
*
|
|
722
|
+
* @param relPath - Repo-relative file path.
|
|
723
|
+
* @param taskId - CLEO task ID; the branch `task/<taskId>` is checked first.
|
|
724
|
+
* @param projectRoot - Working directory for git operations.
|
|
725
|
+
* @returns Buffer of the file content, or `null` when not found on any branch.
|
|
726
|
+
*
|
|
727
|
+
* @internal
|
|
728
|
+
* @task T11959
|
|
729
|
+
*/
|
|
730
|
+
async function gitShowFileContent(relPath, taskId, projectRoot) {
|
|
731
|
+
// Normalise the path: strip leading "./" so git:show works correctly.
|
|
732
|
+
const norm = relPath.replace(/^\.\//, '');
|
|
733
|
+
// Try task/<taskId> first, then main, then master, then HEAD.
|
|
734
|
+
const refs = [`task/${taskId}`, 'main', 'master', 'HEAD'];
|
|
735
|
+
for (const ref of refs) {
|
|
736
|
+
const r = await runCommand('git', ['show', `${ref}:${norm}`], projectRoot);
|
|
737
|
+
if (r.exitCode === 0 && r.stdout.length > 0) {
|
|
738
|
+
return Buffer.from(r.stdout, 'binary');
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Validate a `files:` evidence atom.
|
|
745
|
+
*
|
|
746
|
+
* Resolution order for each path:
|
|
747
|
+
* 1. Filesystem check — `existsSync(abs)` at `projectRoot`.
|
|
748
|
+
* 2. Git-show fallback (T11959) — when `taskId` is provided and the file is
|
|
749
|
+
* absent at `projectRoot`, try reading it from `task/<taskId>` (or main /
|
|
750
|
+
* HEAD) via `git show <ref>:<path>`. This allows worktree agents to record
|
|
751
|
+
* `files:` evidence for files that exist only on their branch and have not
|
|
752
|
+
* yet been merged to main, without triggering `E_WT_DB_ISOLATION_VIOLATION`.
|
|
753
|
+
*
|
|
754
|
+
* When the file is read from git (fallback path) the sha256 is computed from
|
|
755
|
+
* the git-object content rather than the on-disk content. The re-validation at
|
|
756
|
+
* `cleo complete` time uses the same fallback path so the checksums remain
|
|
757
|
+
* consistent across verify → complete.
|
|
758
|
+
*
|
|
759
|
+
* @param paths - Repo-relative or absolute file paths.
|
|
760
|
+
* @param projectRoot - Absolute path to project root.
|
|
761
|
+
* @param taskId - Optional CLEO task ID; enables the git-show fallback (T11959).
|
|
762
|
+
* @returns Validated atom on success, error on failure.
|
|
763
|
+
*
|
|
764
|
+
* @internal
|
|
765
|
+
* @task T832
|
|
766
|
+
* @task T11959
|
|
767
|
+
*/
|
|
768
|
+
async function validateFiles(paths, projectRoot, taskId) {
|
|
543
769
|
if (paths.length === 0) {
|
|
544
770
|
return {
|
|
545
771
|
ok: false,
|
|
@@ -550,22 +776,40 @@ async function validateFiles(paths, projectRoot) {
|
|
|
550
776
|
const files = [];
|
|
551
777
|
for (const p of paths) {
|
|
552
778
|
const abs = isAbsolute(p) ? p : resolvePath(projectRoot, p);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
779
|
+
let content;
|
|
780
|
+
if (existsSync(abs)) {
|
|
781
|
+
// Happy path — file exists on disk.
|
|
782
|
+
const st = await stat(abs);
|
|
783
|
+
if (!st.isFile()) {
|
|
784
|
+
return {
|
|
785
|
+
ok: false,
|
|
786
|
+
reason: `Path is not a regular file: ${p}`,
|
|
787
|
+
codeName: 'E_EVIDENCE_INVALID',
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
content = await readFile(abs);
|
|
791
|
+
}
|
|
792
|
+
else if (taskId) {
|
|
793
|
+
// T11959: Git-show fallback for branch-only files (worktree context).
|
|
794
|
+
// The file exists on the task branch but not yet at the canonical root.
|
|
795
|
+
const fromGit = await gitShowFileContent(p, taskId, projectRoot);
|
|
796
|
+
if (!fromGit) {
|
|
797
|
+
return {
|
|
798
|
+
ok: false,
|
|
799
|
+
reason: `File does not exist: ${p}` +
|
|
800
|
+
` (checked filesystem at ${projectRoot} and git refs task/${taskId}, main, HEAD)`,
|
|
801
|
+
codeName: 'E_EVIDENCE_INVALID',
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
content = fromGit;
|
|
559
805
|
}
|
|
560
|
-
|
|
561
|
-
if (!st.isFile()) {
|
|
806
|
+
else {
|
|
562
807
|
return {
|
|
563
808
|
ok: false,
|
|
564
|
-
reason: `
|
|
809
|
+
reason: `File does not exist: ${p}`,
|
|
565
810
|
codeName: 'E_EVIDENCE_INVALID',
|
|
566
811
|
};
|
|
567
812
|
}
|
|
568
|
-
const content = await readFile(abs);
|
|
569
813
|
const sha256 = createHash('sha256').update(content).digest('hex');
|
|
570
814
|
files.push({ path: p, sha256 });
|
|
571
815
|
}
|
|
@@ -1153,13 +1397,18 @@ export const CRITICAL_GATES_NO_OVERRIDE = Object.freeze([
|
|
|
1153
1397
|
* @param projectRoot - Absolute path to project root
|
|
1154
1398
|
* @param gate - Optional gate name; when provided enables the critical-gate
|
|
1155
1399
|
* override-rejection check (T9245). Omit for back-compat callers.
|
|
1400
|
+
* @param taskId - Optional CLEO task ID; enables git-show fallback for files
|
|
1401
|
+
* that were on the task branch at verify time but are now on main after
|
|
1402
|
+
* merge (T11959). When provided, sha256 comparison uses the same git-show
|
|
1403
|
+
* resolution path as validate time so the checksums remain consistent.
|
|
1156
1404
|
* @returns Revalidation outcome
|
|
1157
1405
|
*
|
|
1158
1406
|
* @task T832
|
|
1159
1407
|
* @task T9245
|
|
1408
|
+
* @task T11959
|
|
1160
1409
|
* @adr ADR-051 §5 / §8 (Decision 8)
|
|
1161
1410
|
*/
|
|
1162
|
-
export async function revalidateEvidence(evidence, projectRoot, gate) {
|
|
1411
|
+
export async function revalidateEvidence(evidence, projectRoot, gate, taskId) {
|
|
1163
1412
|
// T9245: critical-gate override rejection.
|
|
1164
1413
|
// When the gate is `implemented` or `testsPassed`, evidence that has no
|
|
1165
1414
|
// non-override hard atom is rejected outright. We define "override-only"
|
|
@@ -1202,11 +1451,20 @@ export async function revalidateEvidence(evidence, projectRoot, gate) {
|
|
|
1202
1451
|
case 'files': {
|
|
1203
1452
|
for (const f of atom.files) {
|
|
1204
1453
|
const abs = isAbsolute(f.path) ? f.path : resolvePath(projectRoot, f.path);
|
|
1205
|
-
|
|
1454
|
+
let content = null;
|
|
1455
|
+
if (existsSync(abs)) {
|
|
1456
|
+
content = await readFile(abs);
|
|
1457
|
+
}
|
|
1458
|
+
else if (taskId) {
|
|
1459
|
+
// T11959: git-show fallback — file may have been on task branch at
|
|
1460
|
+
// verify time and is now on main after merge, or it may still only
|
|
1461
|
+
// exist on the branch. Use the same resolution path as validateFiles.
|
|
1462
|
+
content = await gitShowFileContent(f.path, taskId, projectRoot);
|
|
1463
|
+
}
|
|
1464
|
+
if (!content) {
|
|
1206
1465
|
failed.push({ atom, reason: `File removed since verify: ${f.path}` });
|
|
1207
1466
|
break;
|
|
1208
1467
|
}
|
|
1209
|
-
const content = await readFile(abs);
|
|
1210
1468
|
const sha256 = createHash('sha256').update(content).digest('hex');
|
|
1211
1469
|
if (sha256 !== f.sha256) {
|
|
1212
1470
|
failed.push({
|