@deftai/directive-core 0.104.0 → 0.105.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/dist/authz/classify.js +526 -42
- package/dist/check/cached-orchestrator.d.ts +1 -1
- package/dist/check/cached-orchestrator.js +2 -2
- package/dist/check/named-cause.d.ts +1 -1
- package/dist/check/named-cause.js +33 -3
- package/dist/codebase/default-extractor.d.ts +5 -0
- package/dist/codebase/default-extractor.js +7 -15
- package/dist/deposit/copy-tree.js +60 -0
- package/dist/deposit/python-free.js +26 -18
- package/dist/doctor/checks.d.ts +8 -0
- package/dist/doctor/checks.js +37 -25
- package/dist/doctor/doctor-state.d.ts +4 -0
- package/dist/doctor/doctor-state.js +7 -1
- package/dist/doctor/main.js +9 -19
- package/dist/doctor/types.d.ts +9 -0
- package/dist/fs/contained-write.d.ts +115 -1
- package/dist/fs/contained-write.js +241 -5
- package/dist/fs/mutation-ledger.d.ts +54 -0
- package/dist/fs/mutation-ledger.js +133 -0
- package/dist/fs/non-product-dirs.d.ts +79 -0
- package/dist/fs/non-product-dirs.js +96 -0
- package/dist/hooks/dispatcher.d.ts +3 -1
- package/dist/hooks/dispatcher.js +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/agent-hooks.js +17 -15
- package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
- package/dist/init-deposit/core-guard-pin-content.js +393 -0
- package/dist/init-deposit/gitignore.js +9 -0
- package/dist/init-deposit/hygiene.d.ts +47 -11
- package/dist/init-deposit/hygiene.js +172 -37
- package/dist/init-deposit/legacy-detect.js +6 -2
- package/dist/init-deposit/refresh.d.ts +21 -1
- package/dist/init-deposit/refresh.js +106 -9
- package/dist/init-deposit/scaffold.js +28 -279
- package/dist/init-deposit/slash-deposit.js +13 -7
- package/dist/init-deposit/xbrief-projections.d.ts +8 -1
- package/dist/init-deposit/xbrief-projections.js +24 -11
- package/dist/intake/clause-derivation.d.ts +38 -1
- package/dist/intake/clause-derivation.js +211 -9
- package/dist/intake/issue-ingest.d.ts +3 -1
- package/dist/intake/issue-ingest.js +35 -6
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
- package/dist/literal-acceptance/capture.d.ts +10 -0
- package/dist/literal-acceptance/capture.js +224 -75
- package/dist/literal-acceptance/evaluate.d.ts +29 -4
- package/dist/literal-acceptance/evaluate.js +70 -2
- package/dist/literal-acceptance/index.d.ts +3 -3
- package/dist/literal-acceptance/index.js +3 -3
- package/dist/literal-acceptance/safety.d.ts +41 -0
- package/dist/literal-acceptance/safety.js +126 -2
- package/dist/literal-acceptance/types.d.ts +8 -0
- package/dist/orphan-active/evaluate.d.ts +7 -1
- package/dist/orphan-active/evaluate.js +102 -23
- package/dist/platform/platform-capabilities.js +2 -3
- package/dist/policy/base-branch.d.ts +37 -0
- package/dist/policy/base-branch.js +114 -0
- package/dist/policy/branch-sync.d.ts +80 -0
- package/dist/policy/branch-sync.js +211 -0
- package/dist/policy/ceremony-dial-escalation.js +1 -0
- package/dist/policy/ceremony-dial.js +4 -14
- package/dist/policy/delivery-branch.d.ts +5 -3
- package/dist/policy/delivery-branch.js +7 -3
- package/dist/policy/file-size-thresholds.d.ts +24 -0
- package/dist/policy/file-size-thresholds.js +23 -0
- package/dist/policy/forge-outage-retry.d.ts +47 -0
- package/dist/policy/forge-outage-retry.js +157 -0
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/index.js +66 -7
- package/dist/policy/project-invariants.d.ts +50 -0
- package/dist/policy/project-invariants.js +205 -0
- package/dist/policy/sync-default.d.ts +129 -0
- package/dist/policy/sync-default.js +452 -0
- package/dist/policy/sync-max-files.d.ts +93 -0
- package/dist/policy/sync-max-files.js +192 -0
- package/dist/pr-monitor/absent-required.d.ts +11 -0
- package/dist/pr-monitor/absent-required.js +61 -0
- package/dist/pr-monitor/constants.d.ts +7 -0
- package/dist/pr-monitor/constants.js +7 -0
- package/dist/pr-monitor/index.d.ts +2 -1
- package/dist/pr-monitor/index.js +2 -1
- package/dist/pr-monitor/main.js +4 -0
- package/dist/pr-monitor/monitor.js +24 -1
- package/dist/pr-wait-mergeable/classify.js +6 -0
- package/dist/preflight/evaluate.d.ts +4 -0
- package/dist/preflight/evaluate.js +35 -1
- package/dist/preflight/index.d.ts +4 -0
- package/dist/preflight/index.js +2 -0
- package/dist/preflight/intended-placement.d.ts +62 -0
- package/dist/preflight/intended-placement.js +275 -0
- package/dist/preflight/project-invariants-gate.d.ts +29 -0
- package/dist/preflight/project-invariants-gate.js +82 -0
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
- package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
- package/dist/product-first-done-gate/acceptance.d.ts +7 -4
- package/dist/product-first-done-gate/acceptance.js +79 -8
- package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
- package/dist/product-first-done-gate/empty-resolution.js +12 -1
- package/dist/product-first-done-gate/evaluate.d.ts +12 -0
- package/dist/product-first-done-gate/evaluate.js +254 -31
- package/dist/product-first-done-gate/index.d.ts +2 -0
- package/dist/product-first-done-gate/index.js +1 -0
- package/dist/release/build-dist.d.ts +101 -0
- package/dist/release/build-dist.js +428 -46
- package/dist/run-summary/emit.d.ts +16 -10
- package/dist/run-summary/emit.js +26 -15
- package/dist/run-summary/index.d.ts +3 -2
- package/dist/run-summary/index.js +2 -1
- package/dist/run-summary/session-id.d.ts +21 -0
- package/dist/run-summary/session-id.js +92 -0
- package/dist/run-summary/types.d.ts +29 -4
- package/dist/run-summary/types.js +1 -0
- package/dist/scope/acceptance-evidence.d.ts +19 -7
- package/dist/scope/acceptance-evidence.js +38 -11
- package/dist/scope/delivery-evidence.d.ts +3 -3
- package/dist/scope/delivery-evidence.js +22 -27
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/parent-lineage.js +7 -6
- package/dist/scope/project-invariant-coverage.d.ts +54 -0
- package/dist/scope/project-invariant-coverage.js +322 -0
- package/dist/scope/transition.js +1 -1
- package/dist/scope-provenance/compare-intent.d.ts +27 -0
- package/dist/scope-provenance/compare-intent.js +233 -0
- package/dist/scope-provenance/digest.d.ts +18 -1
- package/dist/scope-provenance/digest.js +16 -11
- package/dist/scope-provenance/evaluate.d.ts +5 -1
- package/dist/scope-provenance/evaluate.js +64 -4
- package/dist/scope-provenance/extract-intent.d.ts +43 -0
- package/dist/scope-provenance/extract-intent.js +262 -0
- package/dist/scope-provenance/index.d.ts +7 -1
- package/dist/scope-provenance/index.js +7 -1
- package/dist/scope-provenance/intent-digest.d.ts +12 -0
- package/dist/scope-provenance/intent-digest.js +33 -0
- package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
- package/dist/scope-provenance/intent-evaluate.js +149 -0
- package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
- package/dist/scope-provenance/json-tokenizer.js +225 -0
- package/dist/scope-provenance/known-machine.d.ts +38 -0
- package/dist/scope-provenance/known-machine.js +157 -0
- package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
- package/dist/scope-provenance/mint-artifacts.js +425 -0
- package/dist/session/ac-pass-banking.d.ts +4 -36
- package/dist/session/ac-pass-banking.js +30 -77
- package/dist/session/ac-pass-reuse.d.ts +53 -0
- package/dist/session/ac-pass-reuse.js +95 -0
- package/dist/session/git.js +19 -4
- package/dist/session/index.d.ts +4 -0
- package/dist/session/index.js +4 -0
- package/dist/session/occupancy-stress-worker.d.ts +7 -0
- package/dist/session/occupancy-stress-worker.js +36 -0
- package/dist/session/occupancy.d.ts +97 -0
- package/dist/session/occupancy.js +422 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
- package/dist/session/product-state-hash.d.ts +26 -0
- package/dist/session/product-state-hash.js +683 -0
- package/dist/session/session-ready.d.ts +2 -0
- package/dist/session/session-ready.js +52 -30
- package/dist/session/session-start.d.ts +7 -0
- package/dist/session/session-start.js +71 -7
- package/dist/session/verify-ac-session-cache.d.ts +42 -0
- package/dist/session/verify-ac-session-cache.js +126 -0
- package/dist/session/verify-session-ritual.d.ts +5 -0
- package/dist/session/verify-session-ritual.js +9 -0
- package/dist/slash/openclaw-deposit.js +13 -9
- package/dist/slice/constants.d.ts +2 -2
- package/dist/slice/constants.js +2 -2
- package/dist/slice/existing.js +9 -3
- package/dist/slice/lock.d.ts +16 -1
- package/dist/slice/lock.js +137 -11
- package/dist/story-ready/evaluate.d.ts +2 -0
- package/dist/story-ready/evaluate.js +13 -0
- package/dist/swarm/complete-cohort.d.ts +3 -0
- package/dist/swarm/complete-cohort.js +59 -11
- package/dist/swarm/finalize-cohort.js +33 -2
- package/dist/swarm/launch.d.ts +22 -0
- package/dist/swarm/launch.js +155 -0
- package/dist/swarm/routing-set-cli.d.ts +11 -1
- package/dist/swarm/routing-set-cli.js +13 -3
- package/dist/swarm/routing-verify.d.ts +12 -0
- package/dist/swarm/routing-verify.js +59 -20
- package/dist/swarm/routing.d.ts +14 -0
- package/dist/swarm/routing.js +53 -3
- package/dist/telemetry-coverage/fake-trial.js +10 -0
- package/dist/telemetry-coverage/kinds.js +2 -0
- package/dist/triage/actions/index.js +4 -1
- package/dist/ts-check-lane/index.d.ts +3 -0
- package/dist/ts-check-lane/index.js +2 -0
- package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
- package/dist/ts-check-lane/progress-reporter.js +38 -0
- package/dist/ts-check-lane/progress.d.ts +32 -0
- package/dist/ts-check-lane/progress.js +57 -0
- package/dist/ts-check-lane/run-lane.d.ts +3 -1
- package/dist/ts-check-lane/run-lane.js +8 -6
- package/dist/validate-content/validate-links.d.ts +8 -0
- package/dist/validate-content/validate-links.js +9 -15
- package/dist/value/feedback-file.js +12 -2
- package/dist/vbrief-build/index.d.ts +1 -0
- package/dist/vbrief-build/index.js +1 -0
- package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
- package/dist/vbrief-build/project-invariants-io.js +41 -0
- package/dist/vbrief-reconcile/graph.js +18 -1
- package/dist/vbrief-reconcile/index.d.ts +1 -1
- package/dist/vbrief-reconcile/index.js +1 -1
- package/dist/vbrief-reconcile/types.d.ts +13 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.js +261 -26
- package/dist/vbrief-validate/index.d.ts +1 -1
- package/dist/vbrief-validate/index.js +1 -1
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +68 -0
- package/dist/verify-ac/evaluate.d.ts +7 -1
- package/dist/verify-ac/evaluate.js +34 -26
- package/dist/verify-ac/flag.d.ts +4 -0
- package/dist/verify-ac/flag.js +16 -0
- package/dist/verify-ac/index.d.ts +2 -2
- package/dist/verify-ac/index.js +2 -2
- package/dist/verify-source/contained-writes.js +2 -0
- package/dist/verify-source/verify-stubs.d.ts +15 -1
- package/dist/verify-source/verify-stubs.js +20 -16
- package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
- package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
- package/dist/xbrief-migrate/migrate-project.js +2 -5
- package/package.json +3 -3
|
@@ -5,6 +5,8 @@ import { createIssueComment, editIssueBody, editIssueCommentBody, fetchIssueBody
|
|
|
5
5
|
import { fetchIssueStates, IssueState } from "../intake/reconcile-issues.js";
|
|
6
6
|
import { hasArtifactSuffix, resolveLifecycleRoot, stripArtifactSuffix } from "../layout/resolve.js";
|
|
7
7
|
import { call } from "../scm/call.js";
|
|
8
|
+
import { restCloseIssue } from "../scm/gh-rest.js";
|
|
9
|
+
import { readAll } from "../slice/record.js";
|
|
8
10
|
import { extractIssueRef, parseGithubIssueUri } from "../triage/reconcile/parse-uri.js";
|
|
9
11
|
import { isRepoMutationAllowed } from "./repo-guard.js";
|
|
10
12
|
export const OPEN_FOLDERS = ["proposed", "pending", "active"];
|
|
@@ -307,33 +309,43 @@ export function classifyPassType(prevTotal, total) {
|
|
|
307
309
|
function hasCurrentShape(body) {
|
|
308
310
|
return HEADER_RE.test(body);
|
|
309
311
|
}
|
|
312
|
+
function parseCommentPage(data) {
|
|
313
|
+
if (!Array.isArray(data))
|
|
314
|
+
return [];
|
|
315
|
+
const comments = [];
|
|
316
|
+
for (const entry of data) {
|
|
317
|
+
if (typeof entry === "object" &&
|
|
318
|
+
entry !== null &&
|
|
319
|
+
!Array.isArray(entry) &&
|
|
320
|
+
typeof entry.id === "number" &&
|
|
321
|
+
typeof entry.body === "string") {
|
|
322
|
+
const rec = entry;
|
|
323
|
+
comments.push({ id: rec.id, body: rec.body });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return comments;
|
|
327
|
+
}
|
|
310
328
|
export class ScmUmbrellaClient {
|
|
311
329
|
fetchComments(repo, issueNumber) {
|
|
312
|
-
const proc = call(SCM_SOURCE, "api", [
|
|
313
|
-
`repos/${repo}/issues/${issueNumber}/comments?per_page=100`,
|
|
314
|
-
]);
|
|
315
|
-
if (proc.returncode !== 0) {
|
|
316
|
-
throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) failed: ${(proc.stderr || "").trim()}`);
|
|
317
|
-
}
|
|
318
|
-
let data;
|
|
319
|
-
try {
|
|
320
|
-
data = JSON.parse(proc.stdout || "[]");
|
|
321
|
-
}
|
|
322
|
-
catch (exc) {
|
|
323
|
-
throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) returned non-JSON: ${String(exc)}`);
|
|
324
|
-
}
|
|
325
|
-
if (!Array.isArray(data))
|
|
326
|
-
return [];
|
|
327
330
|
const comments = [];
|
|
328
|
-
for (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
331
|
+
for (let page = 1; page <= 50; page += 1) {
|
|
332
|
+
const proc = call(SCM_SOURCE, "api", [
|
|
333
|
+
`repos/${repo}/issues/${issueNumber}/comments?per_page=100&page=${page}`,
|
|
334
|
+
]);
|
|
335
|
+
if (proc.returncode !== 0) {
|
|
336
|
+
throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) failed: ${(proc.stderr || "").trim()}`);
|
|
337
|
+
}
|
|
338
|
+
let data;
|
|
339
|
+
try {
|
|
340
|
+
data = JSON.parse(proc.stdout || "[]");
|
|
341
|
+
}
|
|
342
|
+
catch (exc) {
|
|
343
|
+
throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) returned non-JSON: ${String(exc)}`);
|
|
336
344
|
}
|
|
345
|
+
const batch = parseCommentPage(data);
|
|
346
|
+
comments.push(...batch);
|
|
347
|
+
if (batch.length < 100)
|
|
348
|
+
break;
|
|
337
349
|
}
|
|
338
350
|
return comments;
|
|
339
351
|
}
|
|
@@ -394,6 +406,15 @@ export class ScmUmbrellaClient {
|
|
|
394
406
|
throw new UmbrellaScmError(`edit issue body #${issueNumber} (${repo}) failed: ${message}`);
|
|
395
407
|
}
|
|
396
408
|
}
|
|
409
|
+
closeIssue(repo, issueNumber) {
|
|
410
|
+
try {
|
|
411
|
+
restCloseIssue(repo, issueNumber, "completed");
|
|
412
|
+
}
|
|
413
|
+
catch (exc) {
|
|
414
|
+
const message = exc instanceof Error ? exc.message : String(exc);
|
|
415
|
+
throw new UmbrellaScmError(`close issue #${issueNumber} (${repo}) failed: ${message}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
397
418
|
}
|
|
398
419
|
function planShape(epicData, index, options = {}) {
|
|
399
420
|
const children = computeChildren(epicData, index, {
|
|
@@ -545,6 +566,147 @@ function reconcileOneEpic(epicData, index, options) {
|
|
|
545
566
|
export function nowIso(date = new Date()) {
|
|
546
567
|
return date.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
547
568
|
}
|
|
569
|
+
const ALL_CHILDREN_MERGED = "all-children-merged";
|
|
570
|
+
/** Children listed on a slices.jsonl row (#3428). Last write of a duplicate `n` wins. */
|
|
571
|
+
export function childrenFromSliceRecord(record) {
|
|
572
|
+
const raw = record.children;
|
|
573
|
+
if (!Array.isArray(raw))
|
|
574
|
+
return [];
|
|
575
|
+
const byNumber = new Map();
|
|
576
|
+
for (const entry of raw) {
|
|
577
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry))
|
|
578
|
+
continue;
|
|
579
|
+
const rec = entry;
|
|
580
|
+
const n = rec.n;
|
|
581
|
+
if (typeof n !== "number" || !Number.isInteger(n) || n < 1)
|
|
582
|
+
continue;
|
|
583
|
+
byNumber.set(n, {
|
|
584
|
+
story_id: `#${n}`,
|
|
585
|
+
title: `#${n}`,
|
|
586
|
+
kind: "story",
|
|
587
|
+
folder: UNKNOWN_FOLDER,
|
|
588
|
+
depends_on: [],
|
|
589
|
+
issue_number: n,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
return [...byNumber.values()];
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Close only when the slice signal is all-children-merged, the umbrella is
|
|
596
|
+
* still open, and every child has forge state closed (#3428).
|
|
597
|
+
*/
|
|
598
|
+
export function shouldCloseOnAllChildrenMerged(options) {
|
|
599
|
+
if (options.signal !== ALL_CHILDREN_MERGED)
|
|
600
|
+
return false;
|
|
601
|
+
if (options.umbrellaState !== "open")
|
|
602
|
+
return false;
|
|
603
|
+
if (options.children.length === 0)
|
|
604
|
+
return false;
|
|
605
|
+
if (options.forgeStates == null)
|
|
606
|
+
return false;
|
|
607
|
+
return options.children.every((c) => typeof c.issue_number === "number" && options.forgeStates?.get(c.issue_number) === "closed");
|
|
608
|
+
}
|
|
609
|
+
export function formatCloseComment(signal, children) {
|
|
610
|
+
const nums = children
|
|
611
|
+
.map((c) => c.issue_number)
|
|
612
|
+
.filter((n) => typeof n === "number")
|
|
613
|
+
.sort((a, b) => a - b)
|
|
614
|
+
.map((n) => `#${n}`);
|
|
615
|
+
return `expected_close_signal=${signal}; children: ${nums.join(", ")}`;
|
|
616
|
+
}
|
|
617
|
+
function loadLatestSliceRecords(vbriefDir) {
|
|
618
|
+
const logPath = join(vbriefDir, ".triage-cache", "slices.jsonl");
|
|
619
|
+
if (!existsSync(logPath))
|
|
620
|
+
return [];
|
|
621
|
+
const latest = new Map();
|
|
622
|
+
for (const rec of readAll({ path: logPath })) {
|
|
623
|
+
const n = rec.umbrella;
|
|
624
|
+
if (typeof n !== "number" || !Number.isInteger(n) || n < 1)
|
|
625
|
+
continue;
|
|
626
|
+
const [urlRepo] = parseGithubIssueUri(rec.umbrella_url);
|
|
627
|
+
latest.set(`${urlRepo ?? ""}:${n}`, rec);
|
|
628
|
+
}
|
|
629
|
+
return [...latest.values()];
|
|
630
|
+
}
|
|
631
|
+
function epicDataFromSlice(repo, number, children) {
|
|
632
|
+
const refs = [
|
|
633
|
+
{ type: "x-xbrief/github-issue", uri: `https://github.com/${repo}/issues/${number}` },
|
|
634
|
+
...children.map((c) => ({
|
|
635
|
+
type: "x-xbrief/github-issue",
|
|
636
|
+
uri: `https://github.com/${repo}/issues/${c.issue_number}`,
|
|
637
|
+
title: c.title,
|
|
638
|
+
})),
|
|
639
|
+
];
|
|
640
|
+
return {
|
|
641
|
+
plan: {
|
|
642
|
+
id: `slice-umbrella-${number}`,
|
|
643
|
+
metadata: { kind: "epic" },
|
|
644
|
+
references: refs,
|
|
645
|
+
},
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function maybeCloseSliceUmbrella(options) {
|
|
649
|
+
if (typeof options.client.closeIssue !== "function")
|
|
650
|
+
return "skipped";
|
|
651
|
+
if (typeof options.client.fetchIssueStates !== "function")
|
|
652
|
+
return "skipped";
|
|
653
|
+
const mutateGate = isRepoMutationAllowed(options.repo, options.projectRoot, {
|
|
654
|
+
allowCrossRepo: options.allowCrossRepo,
|
|
655
|
+
allowlist: options.repoAllowlist,
|
|
656
|
+
explicitRepo: options.explicitRepo ?? null,
|
|
657
|
+
});
|
|
658
|
+
if (!mutateGate.allowed) {
|
|
659
|
+
throw new Error(mutateGate.reason ?? `refusing cross-repo mutation on ${options.repo}`);
|
|
660
|
+
}
|
|
661
|
+
const childNumbers = options.children
|
|
662
|
+
.map((c) => c.issue_number)
|
|
663
|
+
.filter((n) => typeof n === "number");
|
|
664
|
+
const states = options.client.fetchIssueStates(options.repo, [...childNumbers, options.number]);
|
|
665
|
+
const umbrellaState = states.get(options.number) ?? null;
|
|
666
|
+
if (!shouldCloseOnAllChildrenMerged({
|
|
667
|
+
signal: options.signal,
|
|
668
|
+
children: options.children,
|
|
669
|
+
forgeStates: states,
|
|
670
|
+
umbrellaState,
|
|
671
|
+
})) {
|
|
672
|
+
return options.signal === ALL_CHILDREN_MERGED && umbrellaState === "closed"
|
|
673
|
+
? "unchanged"
|
|
674
|
+
: "skipped";
|
|
675
|
+
}
|
|
676
|
+
const closeBody = formatCloseComment(ALL_CHILDREN_MERGED, options.children);
|
|
677
|
+
if (!options.dryRun) {
|
|
678
|
+
const alreadyCommented = options.client
|
|
679
|
+
.fetchComments(options.repo, options.number)
|
|
680
|
+
.some((c) => c.body.trim() === closeBody);
|
|
681
|
+
if (!alreadyCommented) {
|
|
682
|
+
options.client.createComment(options.repo, options.number, closeBody);
|
|
683
|
+
}
|
|
684
|
+
options.client.closeIssue(options.repo, options.number);
|
|
685
|
+
}
|
|
686
|
+
return "closed";
|
|
687
|
+
}
|
|
688
|
+
function pushUmbrellaChange(outcome, change) {
|
|
689
|
+
if (change.action === "unchanged" && change.close_action !== "closed") {
|
|
690
|
+
outcome.unchanged.push(change);
|
|
691
|
+
}
|
|
692
|
+
else {
|
|
693
|
+
outcome.changed.push(change);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function replaceCloseAction(outcome, repo, issueNumber, closeAction) {
|
|
697
|
+
const match = (c) => c.repo === repo && c.issue_number === issueNumber;
|
|
698
|
+
for (const list of [outcome.changed, outcome.unchanged]) {
|
|
699
|
+
const idx = list.findIndex(match);
|
|
700
|
+
if (idx < 0)
|
|
701
|
+
continue;
|
|
702
|
+
const prev = list[idx];
|
|
703
|
+
if (prev === undefined)
|
|
704
|
+
return;
|
|
705
|
+
list.splice(idx, 1);
|
|
706
|
+
pushUmbrellaChange(outcome, { ...prev, close_action: closeAction });
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
548
710
|
export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
549
711
|
const root = resolve(projectRoot);
|
|
550
712
|
let vbriefDir;
|
|
@@ -616,7 +778,6 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
|
616
778
|
const key = `${effectiveRepo}:${number}`;
|
|
617
779
|
if (seenIssues.has(key))
|
|
618
780
|
continue;
|
|
619
|
-
seenIssues.add(key);
|
|
620
781
|
try {
|
|
621
782
|
const change = reconcileOneEpic(data, index, {
|
|
622
783
|
storyId,
|
|
@@ -630,6 +791,7 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
|
630
791
|
repoAllowlist: options.repoAllowlist,
|
|
631
792
|
explicitRepo: options.repo ?? null,
|
|
632
793
|
});
|
|
794
|
+
seenIssues.add(key);
|
|
633
795
|
if (change.action === "unchanged")
|
|
634
796
|
outcome.unchanged.push(change);
|
|
635
797
|
else
|
|
@@ -643,6 +805,71 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
|
643
805
|
}
|
|
644
806
|
}
|
|
645
807
|
}
|
|
808
|
+
// slices.jsonl umbrellas have no epic xBRIEF (#3428). Last row per umbrella wins.
|
|
809
|
+
for (const record of loadLatestSliceRecords(vbriefDir)) {
|
|
810
|
+
const number = record.umbrella;
|
|
811
|
+
if (typeof number !== "number")
|
|
812
|
+
continue;
|
|
813
|
+
const [urlRepo] = parseGithubIssueUri(record.umbrella_url);
|
|
814
|
+
const effectiveRepo = urlRepo ?? options.repo ?? null;
|
|
815
|
+
if (effectiveRepo === null) {
|
|
816
|
+
outcome.skipped_no_ref.push(`slice-umbrella-${number}`);
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
const key = `${effectiveRepo}:${number}`;
|
|
820
|
+
const children = childrenFromSliceRecord(record);
|
|
821
|
+
const storyId = `slice-umbrella-${number}`;
|
|
822
|
+
try {
|
|
823
|
+
if (seenIssues.has(key)) {
|
|
824
|
+
const closeAction = maybeCloseSliceUmbrella({
|
|
825
|
+
signal: record.expected_close_signal,
|
|
826
|
+
children,
|
|
827
|
+
repo: effectiveRepo,
|
|
828
|
+
number,
|
|
829
|
+
client,
|
|
830
|
+
dryRun: options.dryRun ?? false,
|
|
831
|
+
projectRoot: root,
|
|
832
|
+
allowCrossRepo: options.allowCrossRepo ?? false,
|
|
833
|
+
repoAllowlist: options.repoAllowlist,
|
|
834
|
+
explicitRepo: options.repo ?? null,
|
|
835
|
+
});
|
|
836
|
+
replaceCloseAction(outcome, effectiveRepo, number, closeAction);
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
const change = reconcileOneEpic(epicDataFromSlice(effectiveRepo, number, children), index, {
|
|
840
|
+
storyId,
|
|
841
|
+
repo: effectiveRepo,
|
|
842
|
+
number,
|
|
843
|
+
client,
|
|
844
|
+
dryRun: options.dryRun ?? false,
|
|
845
|
+
now,
|
|
846
|
+
projectRoot: root,
|
|
847
|
+
allowCrossRepo: options.allowCrossRepo ?? false,
|
|
848
|
+
repoAllowlist: options.repoAllowlist,
|
|
849
|
+
explicitRepo: options.repo ?? null,
|
|
850
|
+
});
|
|
851
|
+
seenIssues.add(key);
|
|
852
|
+
const closeAction = maybeCloseSliceUmbrella({
|
|
853
|
+
signal: record.expected_close_signal,
|
|
854
|
+
children,
|
|
855
|
+
repo: effectiveRepo,
|
|
856
|
+
number,
|
|
857
|
+
client,
|
|
858
|
+
dryRun: options.dryRun ?? false,
|
|
859
|
+
projectRoot: root,
|
|
860
|
+
allowCrossRepo: options.allowCrossRepo ?? false,
|
|
861
|
+
repoAllowlist: options.repoAllowlist,
|
|
862
|
+
explicitRepo: options.repo ?? null,
|
|
863
|
+
});
|
|
864
|
+
pushUmbrellaChange(outcome, { ...change, close_action: closeAction });
|
|
865
|
+
}
|
|
866
|
+
catch (exc) {
|
|
867
|
+
outcome.errors.push({
|
|
868
|
+
story_id: storyId,
|
|
869
|
+
message: exc instanceof Error ? exc.message : String(exc),
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
646
873
|
return [outcome.errors.length > 0 ? 1 : 0, outcome];
|
|
647
874
|
}
|
|
648
875
|
function formatChecklistSuffix(action) {
|
|
@@ -650,6 +877,11 @@ function formatChecklistSuffix(action) {
|
|
|
650
877
|
return "";
|
|
651
878
|
return `; checklist=${action}`;
|
|
652
879
|
}
|
|
880
|
+
function formatCloseSuffix(action) {
|
|
881
|
+
if (action === undefined || action === "skipped")
|
|
882
|
+
return "";
|
|
883
|
+
return `; close=${action}`;
|
|
884
|
+
}
|
|
653
885
|
export function renderUmbrellasReport(outcome) {
|
|
654
886
|
const lines = ["vBRIEF reconcile umbrellas", ""];
|
|
655
887
|
const suffix = outcome.dry_run ? " (dry-run)" : "";
|
|
@@ -657,7 +889,8 @@ export function renderUmbrellasReport(outcome) {
|
|
|
657
889
|
if (outcome.changed.length > 0) {
|
|
658
890
|
for (const c of outcome.changed) {
|
|
659
891
|
lines.push(`- #${c.issue_number} (${c.repo}) [${c.story_id}]: ${c.action} -> pass-${c.pass_n}` +
|
|
660
|
-
formatChecklistSuffix(c.checklist_action)
|
|
892
|
+
formatChecklistSuffix(c.checklist_action) +
|
|
893
|
+
formatCloseSuffix(c.close_action));
|
|
661
894
|
}
|
|
662
895
|
}
|
|
663
896
|
else {
|
|
@@ -668,7 +901,8 @@ export function renderUmbrellasReport(outcome) {
|
|
|
668
901
|
if (outcome.unchanged.length > 0) {
|
|
669
902
|
for (const c of outcome.unchanged) {
|
|
670
903
|
lines.push(`- #${c.issue_number} (${c.repo}) [${c.story_id}]: pass-${c.pass_n}` +
|
|
671
|
-
formatChecklistSuffix(c.checklist_action)
|
|
904
|
+
formatChecklistSuffix(c.checklist_action) +
|
|
905
|
+
formatCloseSuffix(c.close_action));
|
|
672
906
|
}
|
|
673
907
|
}
|
|
674
908
|
else {
|
|
@@ -696,6 +930,7 @@ export function umbrellasOutcomeToJson(outcome) {
|
|
|
696
930
|
action: c.action,
|
|
697
931
|
pass_n: c.pass_n,
|
|
698
932
|
checklist_action: c.checklist_action ?? "skipped",
|
|
933
|
+
close_action: c.close_action ?? "skipped",
|
|
699
934
|
});
|
|
700
935
|
return {
|
|
701
936
|
changed: outcome.changed.map(toChange),
|
|
@@ -6,7 +6,7 @@ export { validateFolderStatus } from "./folder-status.js";
|
|
|
6
6
|
export type { ConformanceCliOptions, ValidateCliOptions } from "./main.js";
|
|
7
7
|
export { cmdVbriefValidate, runConformance, runValidate } from "./main.js";
|
|
8
8
|
export { validateOriginProvenance } from "./origin.js";
|
|
9
|
-
export { validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
|
|
9
|
+
export { validateForgeOutageRetryMinutesOnPlan, validateProjectInvariantsOnPlan, validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
|
|
10
10
|
export { validateProjectDefinition } from "./project-definition.js";
|
|
11
11
|
export type { ExtensionEntry, ExtensionRoundtripEvaluateResult, ExtensionRoundtripFinding, } from "./roundtrip.js";
|
|
12
12
|
export { collectExtensionEntries, EXTENSION_CONFORMANCE_FIXTURES_DIR, evaluateExtensionRoundtrip, findExtensionPreservationViolations, reEmitVbriefArtifact, renderExtensionRoundtripFinding, VbriefSchemaValidationError, } from "./roundtrip.js";
|
|
@@ -4,7 +4,7 @@ export { matchesFilenameConvention, validateFilename } from "./filename.js";
|
|
|
4
4
|
export { validateFolderStatus } from "./folder-status.js";
|
|
5
5
|
export { cmdVbriefValidate, runConformance, runValidate } from "./main.js";
|
|
6
6
|
export { validateOriginProvenance } from "./origin.js";
|
|
7
|
-
export { validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
|
|
7
|
+
export { validateForgeOutageRetryMinutesOnPlan, validateProjectInvariantsOnPlan, validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
|
|
8
8
|
export { validateProjectDefinition } from "./project-definition.js";
|
|
9
9
|
export { collectExtensionEntries, EXTENSION_CONFORMANCE_FIXTURES_DIR, evaluateExtensionRoundtrip, findExtensionPreservationViolations, reEmitVbriefArtifact, renderExtensionRoundtripFinding, VbriefSchemaValidationError, } from "./roundtrip.js";
|
|
10
10
|
export { normalizeNarrativeKey, validateVbriefSchema } from "./schema.js";
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
export declare function validateWipCapOnPlan(plan: unknown, filepath: string): string[];
|
|
3
3
|
/** vbrief_validate hook for ``sessionRitualStalenessHours`` (#1348). */
|
|
4
4
|
export declare function validateSessionRitualStalenessHoursOnPlan(plan: unknown, filepath: string): string[];
|
|
5
|
+
/** vbrief_validate hook for ``projectInvariants`` (#3425). */
|
|
6
|
+
export declare function validateProjectInvariantsOnPlan(plan: unknown, filepath: string): string[];
|
|
7
|
+
/** vbrief_validate hook for ``forgeOutageRetryMinutes`` (#3422). */
|
|
8
|
+
export declare function validateForgeOutageRetryMinutesOnPlan(plan: unknown, filepath: string): string[];
|
|
5
9
|
/** vbrief_validate hook: validate ``plan.policy.triageRankingLabels`` (#1128). */
|
|
6
10
|
export declare function validateTriageRankingLabelsOnPlan(plan: unknown, filepath: string): string[];
|
|
7
11
|
/** vbrief_validate hook: validate ``plan.policy.runtimeAuthority`` (#1394). */
|
|
@@ -3,6 +3,7 @@ import { validateCoverageDebt } from "../policy/coverage-debt.js";
|
|
|
3
3
|
import { validateHostHooks } from "../policy/host-hooks.js";
|
|
4
4
|
import { validateHostSlashCommands } from "../policy/host-slash-commands.js";
|
|
5
5
|
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
6
|
+
import { parseProjectInvariants } from "../policy/project-invariants.js";
|
|
6
7
|
import { validateRuntimeAuthority } from "../policy/runtime-authority.js";
|
|
7
8
|
import { validateStalenessTickler } from "../policy/staleness-tickler.js";
|
|
8
9
|
import { validateOpenClawProductCommands } from "../slash/openclaw-deposit.js";
|
|
@@ -93,6 +94,61 @@ export function validateSessionRitualStalenessHoursOnPlan(plan, filepath) {
|
|
|
93
94
|
}
|
|
94
95
|
return out;
|
|
95
96
|
}
|
|
97
|
+
function validateForgeOutageRetryMinutes(value) {
|
|
98
|
+
if (value === null || value === undefined) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
102
|
+
const repr = typeof value === "string"
|
|
103
|
+
? pyStrRepr(value)
|
|
104
|
+
: typeof value === "boolean"
|
|
105
|
+
? value
|
|
106
|
+
? "True"
|
|
107
|
+
: "False"
|
|
108
|
+
: String(value);
|
|
109
|
+
return [
|
|
110
|
+
"plan.policy.forgeOutageRetryMinutes must be an integer; got " +
|
|
111
|
+
`${pythonTypeName(value)} (${repr})`,
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
if (value < 5) {
|
|
115
|
+
return [`plan.policy.forgeOutageRetryMinutes must be >= 5; got ${value}`];
|
|
116
|
+
}
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
/** vbrief_validate hook for ``projectInvariants`` (#3425). */
|
|
120
|
+
export function validateProjectInvariantsOnPlan(plan, filepath) {
|
|
121
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const policy = readPlanPolicy(plan);
|
|
125
|
+
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
if (!("projectInvariants" in policy)) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
const { errors } = parseProjectInvariants(policy.projectInvariants);
|
|
132
|
+
return errors.map((err) => `${filepath}: ${err} (#3425)`);
|
|
133
|
+
}
|
|
134
|
+
/** vbrief_validate hook for ``forgeOutageRetryMinutes`` (#3422). */
|
|
135
|
+
export function validateForgeOutageRetryMinutesOnPlan(plan, filepath) {
|
|
136
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
const policy = readPlanPolicy(plan);
|
|
140
|
+
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
if (!("forgeOutageRetryMinutes" in policy)) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const out = [];
|
|
147
|
+
for (const err of validateForgeOutageRetryMinutes(policy.forgeOutageRetryMinutes)) {
|
|
148
|
+
out.push(`${filepath}: ${err} (#3422)`);
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
96
152
|
/** vbrief_validate hook: validate ``plan.policy.triageRankingLabels`` (#1128). */
|
|
97
153
|
export function validateTriageRankingLabelsOnPlan(plan, filepath) {
|
|
98
154
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
@@ -283,6 +339,18 @@ export function runProjectDefinitionHooks(plan, filepath) {
|
|
|
283
339
|
catch {
|
|
284
340
|
/* hook must not break validation */
|
|
285
341
|
}
|
|
342
|
+
try {
|
|
343
|
+
errors.push(...validateForgeOutageRetryMinutesOnPlan(plan, filepath));
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
/* hook must not break validation */
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
errors.push(...validateProjectInvariantsOnPlan(plan, filepath));
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
/* hook must not break validation */
|
|
353
|
+
}
|
|
286
354
|
try {
|
|
287
355
|
errors.push(...validateStalenessTicklerOnPlan(plan, filepath));
|
|
288
356
|
}
|
|
@@ -27,6 +27,11 @@ export interface OracleIntegrityResultFields {
|
|
|
27
27
|
readonly code: number;
|
|
28
28
|
readonly message: string;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Walk-level method fingerprint: command list + cwd hash (#3397 / #3322).
|
|
32
|
+
* Same shape for fail and pass so an honest product fix does not flag.
|
|
33
|
+
*/
|
|
34
|
+
export declare function methodFingerprintForWalk(commands: readonly string[], cwd: string): string;
|
|
30
35
|
/** Stable prefix for product-oracle check ids emitted by verify:ac (#3322 / #3337). */
|
|
31
36
|
export declare const VERIFY_AC_CHECK_ID_PREFIX = "verify:ac";
|
|
32
37
|
/**
|
|
@@ -40,7 +45,8 @@ export declare function verifyAcCheckId(scopeKey?: string | null): string;
|
|
|
40
45
|
/** Test seam: isolate stdout-dest buffer across cases. */
|
|
41
46
|
export declare function resetInProcessVerificationBuffer(): void;
|
|
42
47
|
/**
|
|
43
|
-
* Record
|
|
48
|
+
* Record one walk-level verification event for the executed command set (#3322 / #3397).
|
|
49
|
+
* Fail and pass use the same method_fingerprint shape (command list + cwd hash).
|
|
44
50
|
* Fail-open: missing dest / write errors never change the AC result.
|
|
45
51
|
* Stdout dest also appends to the same-process buffer so evaluate can
|
|
46
52
|
* inspect this invocation without re-reading a file.
|
|
@@ -7,14 +7,25 @@
|
|
|
7
7
|
* attempts just emitted by this verify:ac — it does not treat non-file
|
|
8
8
|
* dest as no evidence.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
11
|
import { existsSync, readFileSync } from "node:fs";
|
|
12
12
|
import { resolve } from "node:path";
|
|
13
13
|
import { parseRunSummaryJsonl, RunSummaryEmitter, resolveRunSummaryDestination, } from "../run-summary/index.js";
|
|
14
14
|
import { flagPassAfterFailWithMethodChange, readVerificationAttempts, unresolvedMethodChangePasses, } from "./flag.js";
|
|
15
15
|
function formatUnresolved(flag) {
|
|
16
|
+
const delta = typeof flag.resolved_command_count_delta === "number"
|
|
17
|
+
? ` command_count_delta=${flag.resolved_command_count_delta}`
|
|
18
|
+
: "";
|
|
16
19
|
return (`check_id=${flag.check_id} fail method=${flag.failed_method} ` +
|
|
17
|
-
`then pass method=${flag.passed_method} without independent re-derivation`);
|
|
20
|
+
`then pass method=${flag.passed_method} without independent re-derivation${delta}`);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Walk-level method fingerprint: command list + cwd hash (#3397 / #3322).
|
|
24
|
+
* Same shape for fail and pass so an honest product fix does not flag.
|
|
25
|
+
*/
|
|
26
|
+
export function methodFingerprintForWalk(commands, cwd) {
|
|
27
|
+
const cwdHash = createHash("sha256").update(cwd, "utf8").digest("hex");
|
|
28
|
+
return `${commands.join("\0")}\0${cwdHash}`;
|
|
18
29
|
}
|
|
19
30
|
/** Stable prefix for product-oracle check ids emitted by verify:ac (#3322 / #3337). */
|
|
20
31
|
export const VERIFY_AC_CHECK_ID_PREFIX = "verify:ac";
|
|
@@ -36,11 +47,9 @@ export function verifyAcCheckId(scopeKey) {
|
|
|
36
47
|
}
|
|
37
48
|
/** Same-process verification JSONL when dest is stdout (`-`). */
|
|
38
49
|
const inProcessVerificationLines = [];
|
|
39
|
-
let inProcessStdoutSessionId;
|
|
40
50
|
/** Test seam: isolate stdout-dest buffer across cases. */
|
|
41
51
|
export function resetInProcessVerificationBuffer() {
|
|
42
52
|
inProcessVerificationLines.length = 0;
|
|
43
|
-
inProcessStdoutSessionId = undefined;
|
|
44
53
|
}
|
|
45
54
|
function inProcessVerificationText() {
|
|
46
55
|
if (inProcessVerificationLines.length === 0) {
|
|
@@ -49,7 +58,8 @@ function inProcessVerificationText() {
|
|
|
49
58
|
return inProcessVerificationLines.join("\n");
|
|
50
59
|
}
|
|
51
60
|
/**
|
|
52
|
-
* Record
|
|
61
|
+
* Record one walk-level verification event for the executed command set (#3322 / #3397).
|
|
62
|
+
* Fail and pass use the same method_fingerprint shape (command list + cwd hash).
|
|
53
63
|
* Fail-open: missing dest / write errors never change the AC result.
|
|
54
64
|
* Stdout dest also appends to the same-process buffer so evaluate can
|
|
55
65
|
* inspect this invocation without re-reading a file.
|
|
@@ -63,18 +73,7 @@ export function emitVerifyAcAttempts(options) {
|
|
|
63
73
|
const env = options.env ?? process.env;
|
|
64
74
|
const projectRoot = resolve(options.projectRoot);
|
|
65
75
|
const dest = resolveRunSummaryDestination(projectRoot, { env });
|
|
66
|
-
const
|
|
67
|
-
(typeof env.DEFT_SESSION_ID === "string" ? env.DEFT_SESSION_ID.trim() : "");
|
|
68
|
-
let sessionId = explicitSession;
|
|
69
|
-
if (!sessionId) {
|
|
70
|
-
if (dest.kind === "stdout") {
|
|
71
|
-
inProcessStdoutSessionId ??= randomUUID();
|
|
72
|
-
sessionId = inProcessStdoutSessionId;
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
sessionId = randomUUID();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
76
|
+
const sessionId = options.sessionId;
|
|
78
77
|
const explicitCheck = typeof options.checkId === "string" && options.checkId.trim().length > 0
|
|
79
78
|
? options.checkId.trim()
|
|
80
79
|
: null;
|
|
@@ -83,17 +82,26 @@ export function emitVerifyAcAttempts(options) {
|
|
|
83
82
|
projectRoot,
|
|
84
83
|
sessionId,
|
|
85
84
|
env,
|
|
85
|
+
component: "verify-ac",
|
|
86
86
|
writeStdout: options.writeStdout,
|
|
87
87
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
// One walk-level event: the method is the command set, not each command.
|
|
89
|
+
// Per-command emit was blind to a shrinking list that kept the first command (#3397).
|
|
90
|
+
const commands = options.runs.map((run) => run.command);
|
|
91
|
+
const firstCwd = options.runs[0]?.cwd ?? "";
|
|
92
|
+
// Hash the cwd key (joined only when cwds differ) so cwd paths never enter
|
|
93
|
+
// the fingerprint; commandCountFromFingerprint can still count commands.
|
|
94
|
+
const cwdKey = options.runs.every((run) => run.cwd === firstCwd)
|
|
95
|
+
? firstCwd
|
|
96
|
+
: options.runs.map((run) => run.cwd).join("\0");
|
|
97
|
+
const outcome = options.runs.every((run) => run.ok) ? "pass" : "fail";
|
|
98
|
+
const emitted = emitter.emitVerification({
|
|
99
|
+
check_id: checkId,
|
|
100
|
+
method_fingerprint: methodFingerprintForWalk(commands, cwdKey),
|
|
101
|
+
outcome,
|
|
102
|
+
});
|
|
103
|
+
if (dest.kind === "stdout" && emitted.line !== null) {
|
|
104
|
+
inProcessVerificationLines.push(JSON.stringify(emitted.line));
|
|
97
105
|
}
|
|
98
106
|
}
|
|
99
107
|
catch {
|
package/dist/verify-ac/flag.d.ts
CHANGED
|
@@ -20,7 +20,11 @@ export interface FlaggedMethodChangePass {
|
|
|
20
20
|
readonly failed_method: string;
|
|
21
21
|
readonly passed_method: string;
|
|
22
22
|
readonly independent_rederivation: boolean;
|
|
23
|
+
/** Pass count minus fail count when both fingerprints carry a command list (#3397). */
|
|
24
|
+
readonly resolved_command_count_delta?: number;
|
|
23
25
|
}
|
|
26
|
+
/** Command count encoded in a walk fingerprint (`cmd\0cmd\0cwd-or-hash`). */
|
|
27
|
+
export declare function commandCountFromFingerprint(fingerprint: string): number;
|
|
24
28
|
/** Extract well-formed verification attempts in stream order. */
|
|
25
29
|
export declare function readVerificationAttempts(lines: readonly RunSummaryLine[]): VerificationAttempt[];
|
|
26
30
|
/**
|
package/dist/verify-ac/flag.js
CHANGED
|
@@ -6,6 +6,19 @@
|
|
|
6
6
|
* In-place comparison repair then pass is unresolved.
|
|
7
7
|
*/
|
|
8
8
|
import { parseRunSummaryJsonl } from "../run-summary/index.js";
|
|
9
|
+
/** Command count encoded in a walk fingerprint (`cmd\0cmd\0cwd-or-hash`). */
|
|
10
|
+
export function commandCountFromFingerprint(fingerprint) {
|
|
11
|
+
const parts = fingerprint.split("\0");
|
|
12
|
+
if (parts.length <= 1) {
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
const last = parts[parts.length - 1] ?? "";
|
|
16
|
+
const lastIsCwdOrHash = /^[0-9a-f]{8,64}$/i.test(last) ||
|
|
17
|
+
last.includes("/") ||
|
|
18
|
+
last.includes("\\") ||
|
|
19
|
+
last.includes(":");
|
|
20
|
+
return lastIsCwdOrHash ? Math.max(1, parts.length - 1) : parts.length;
|
|
21
|
+
}
|
|
9
22
|
function asRecord(value) {
|
|
10
23
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
11
24
|
return null;
|
|
@@ -74,11 +87,14 @@ export function flagPassAfterFailWithMethodChange(attempts) {
|
|
|
74
87
|
}
|
|
75
88
|
const other = [...prior].find((method) => method !== attempt.method_fingerprint);
|
|
76
89
|
if (other !== undefined) {
|
|
90
|
+
const delta = commandCountFromFingerprint(attempt.method_fingerprint) -
|
|
91
|
+
commandCountFromFingerprint(other);
|
|
77
92
|
flagged.push({
|
|
78
93
|
check_id: attempt.check_id,
|
|
79
94
|
failed_method: other,
|
|
80
95
|
passed_method: attempt.method_fingerprint,
|
|
81
96
|
independent_rederivation: attempt.independent_rederivation,
|
|
97
|
+
...(delta !== 0 ? { resolved_command_count_delta: delta } : {}),
|
|
82
98
|
});
|
|
83
99
|
}
|
|
84
100
|
if (attempt.independent_rederivation || other === undefined) {
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* by editing the comparison. Deterministic surface is the flagged event.
|
|
6
6
|
*/
|
|
7
7
|
export { type AcceptanceClause, type AcceptanceClauseReading, type ClauseOutcome, type ClauseWalkReport, type ClauseWalkResult, deriveAcceptanceClauses, formatClauseWalkMessage, isScratchArtifactPath, readAcceptanceClauses, serializeAcceptanceClauses, stampDerivedClausesOnAcceptance, walkAcceptanceClauses, } from "./clauses.js";
|
|
8
|
-
export { type EvaluateProductOracleIntegrityOptions, emitVerifyAcAttempts, evaluateProductOracleIntegrity, mergeOracleVerdict, type OracleIntegrityResultFields, type ProductOracleIntegrityVerdict, VERIFY_AC_CHECK_ID_PREFIX, verifyAcCheckId, } from "./evaluate.js";
|
|
9
|
-
export { type FlaggedMethodChangePass, flagPassAfterFailFromJsonl, flagPassAfterFailWithMethodChange, readVerificationAttempts, unresolvedMethodChangePasses, type VerificationAttempt, } from "./flag.js";
|
|
8
|
+
export { type EvaluateProductOracleIntegrityOptions, emitVerifyAcAttempts, evaluateProductOracleIntegrity, mergeOracleVerdict, methodFingerprintForWalk, type OracleIntegrityResultFields, type ProductOracleIntegrityVerdict, VERIFY_AC_CHECK_ID_PREFIX, verifyAcCheckId, } from "./evaluate.js";
|
|
9
|
+
export { commandCountFromFingerprint, type FlaggedMethodChangePass, flagPassAfterFailFromJsonl, flagPassAfterFailWithMethodChange, readVerificationAttempts, unresolvedMethodChangePasses, type VerificationAttempt, } from "./flag.js";
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|