@deftai/directive-core 0.104.0 → 0.106.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 +1108 -67
- 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 +34 -4
- 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/forward-coverage/diff-coverage.d.ts +39 -0
- package/dist/forward-coverage/diff-coverage.js +112 -0
- package/dist/forward-coverage/diff-lines.d.ts +19 -0
- package/dist/forward-coverage/diff-lines.js +173 -0
- package/dist/forward-coverage/evaluate.d.ts +24 -9
- package/dist/forward-coverage/evaluate.js +125 -19
- 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.d.ts +1 -1
- package/dist/init-deposit/agent-hooks.js +18 -16
- 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 +47 -1
- package/dist/intake/clause-derivation.js +238 -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/lifecycle-visible/evaluate.d.ts +98 -0
- package/dist/lifecycle-visible/evaluate.js +710 -0
- package/dist/lifecycle-visible/index.d.ts +2 -0
- package/dist/lifecycle-visible/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +10 -0
- package/dist/literal-acceptance/capture.js +226 -77
- 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/orchestration/judgment-policy.d.ts +13 -0
- package/dist/orchestration/judgment-policy.js +15 -0
- package/dist/orchestration/probe-session.js +13 -8
- package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
- package/dist/orchestration/verify-judgment-gates.js +43 -11
- 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 +11 -21
- 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/host-hooks.d.ts +20 -0
- package/dist/policy/host-hooks.js +121 -2
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/index.js +66 -7
- package/dist/policy/org-force-on-migration.js +2 -1
- package/dist/policy/plan-extensions.d.ts +1 -1
- package/dist/policy/plan-extensions.js +2 -0
- package/dist/policy/product-signal.js +6 -7
- package/dist/policy/project-invariants.d.ts +50 -0
- package/dist/policy/project-invariants.js +205 -0
- package/dist/policy/require-human-merge.js +8 -6
- package/dist/policy/resolve.d.ts +7 -2
- package/dist/policy/resolve.js +25 -8
- 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/policy/value-feedback.js +7 -8
- 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/preflight-cache/evaluate.d.ts +12 -0
- package/dist/preflight-cache/evaluate.js +15 -1
- package/dist/preflight-cache/index.d.ts +1 -1
- package/dist/preflight-cache/index.js +1 -1
- 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 +16 -0
- package/dist/product-first-done-gate/evaluate.js +298 -34
- 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/release/closed-verb-gate.d.ts +12 -0
- package/dist/release/closed-verb-gate.js +51 -0
- package/dist/release/index.d.ts +1 -0
- package/dist/release/index.js +1 -0
- package/dist/release/pipeline.js +7 -0
- package/dist/release/types.d.ts +12 -0
- package/dist/render/constants.d.ts +2 -1
- package/dist/render/constants.js +2 -1
- package/dist/render/framework-commands.js +4 -0
- package/dist/render/spec-validate.js +23 -12
- 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 +33 -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/main.js +15 -5
- 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/promote-from-issue.js +5 -5
- 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 +67 -7
- 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 +9 -36
- package/dist/session/ac-pass-banking.js +33 -79
- 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/process-cost.d.ts +32 -10
- package/dist/session/process-cost.js +189 -1
- package/dist/session/product-state-hash.d.ts +26 -0
- package/dist/session/product-state-hash.js +700 -0
- package/dist/session/ritual-entrypoint.js +16 -1
- package/dist/session/session-ready.d.ts +2 -0
- package/dist/session/session-ready.js +52 -30
- package/dist/session/session-start.d.ts +13 -0
- package/dist/session/session-start.js +103 -9
- 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 +28 -0
- package/dist/session/verify-session-ritual.js +97 -5
- 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-cli.d.ts +7 -2
- package/dist/swarm/finalize-cohort-cli.js +165 -35
- package/dist/swarm/finalize-cohort.d.ts +1 -0
- package/dist/swarm/finalize-cohort.js +47 -4
- 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 +15 -3
- package/dist/swarm/routing-verify.d.ts +13 -1
- package/dist/swarm/routing-verify.js +60 -21
- 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/triage/bootstrap/index.js +6 -6
- package/dist/triage/help/registry-data.d.ts +1 -1
- package/dist/triage/help/registry-data.js +1 -1
- package/dist/triage/welcome/writers.d.ts +1 -1
- package/dist/triage/welcome/writers.js +10 -5
- 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/value/readback.d.ts +4 -1
- package/dist/value/readback.js +51 -12
- 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/vbrief-validate/schema.d.ts +4 -0
- package/dist/vbrief-validate/schema.js +1 -1
- 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-env/agent-hook-readiness.d.ts +2 -2
- package/dist/verify-env/agent-hook-readiness.js +12 -8
- package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
- package/dist/verify-env/agent-hooks-live-probe.js +26 -5
- package/dist/verify-env/agent-hooks.js +3 -4
- 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 +7 -3
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
4
|
+
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
5
|
+
import { readPlanSequence } from "../plan-sequence/store.js";
|
|
3
6
|
import { formatFrameworkCommand } from "../render/framework-commands.js";
|
|
4
7
|
import { checkActiveCliAgainstTarget, } from "./active-cli.js";
|
|
5
8
|
import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
|
|
@@ -17,6 +20,64 @@ export const GATED_ENTRYPOINT_COMMANDS = {
|
|
|
17
20
|
doctor: ["doctor"],
|
|
18
21
|
cache_fresh: ["verify:cache-fresh"],
|
|
19
22
|
};
|
|
23
|
+
/** Explicit skip flag threaded through the gated `cache_fresh` argv (#3507). */
|
|
24
|
+
export const SKIP_DRIFT_PROBE_FLAG = "--skip-drift-probe";
|
|
25
|
+
/** Explicit work-selection flag threaded through the gated `cache_fresh` argv (#3507). */
|
|
26
|
+
export const WORK_SELECTION_FLAG = "--work-selection";
|
|
27
|
+
/** Distinct ritual-state field — never `deferred_reason` (#3507). */
|
|
28
|
+
export const DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION = "skipped-no-work-selection";
|
|
29
|
+
function hasActiveStoryArtifact(projectRoot) {
|
|
30
|
+
for (const dirName of ["xbrief", "vbrief"]) {
|
|
31
|
+
const activeDir = join(projectRoot, dirName, "active");
|
|
32
|
+
try {
|
|
33
|
+
for (const entry of readdirSync(activeDir, { withFileTypes: true })) {
|
|
34
|
+
if (entry.isFile() && hasArtifactSuffix(entry.name))
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
/* missing or unreadable active folder is not work selection */
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
function hasUnexhaustedPlanSequence(projectRoot) {
|
|
45
|
+
try {
|
|
46
|
+
const sequence = readPlanSequence(projectRoot);
|
|
47
|
+
if (sequence === null || sequence.exhausted === true)
|
|
48
|
+
return null;
|
|
49
|
+
return sequence.sequence_kind === "triage" ? "triage-queue" : "ordered-plan";
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Default work-selection detector for the gated `cache_fresh` seam (#3507).
|
|
57
|
+
* Active story xBRIEF (scope:activate residue), unexhausted ordered-plan /
|
|
58
|
+
* triage sequence. `--for-issue` is a positive evaluate override, not this
|
|
59
|
+
* detector — the gated argv never carries it.
|
|
60
|
+
*/
|
|
61
|
+
export function detectWorkSelection(projectRoot) {
|
|
62
|
+
if (hasActiveStoryArtifact(projectRoot)) {
|
|
63
|
+
return { inPlay: true, kind: "active-story" };
|
|
64
|
+
}
|
|
65
|
+
const planKind = hasUnexhaustedPlanSequence(projectRoot);
|
|
66
|
+
if (planKind !== null) {
|
|
67
|
+
return { inPlay: true, kind: planKind };
|
|
68
|
+
}
|
|
69
|
+
return { inPlay: false, kind: "none" };
|
|
70
|
+
}
|
|
71
|
+
export function resolveGatedEntrypointCommand(stepName, projectRoot, detect = detectWorkSelection) {
|
|
72
|
+
const base = [...GATED_ENTRYPOINT_COMMANDS[stepName]];
|
|
73
|
+
if (stepName !== "cache_fresh")
|
|
74
|
+
return base;
|
|
75
|
+
const selection = detect(projectRoot);
|
|
76
|
+
if (selection.inPlay) {
|
|
77
|
+
return [...base, WORK_SELECTION_FLAG];
|
|
78
|
+
}
|
|
79
|
+
return [...base, SKIP_DRIFT_PROBE_FLAG];
|
|
80
|
+
}
|
|
20
81
|
function truthy(raw) {
|
|
21
82
|
return new Set(["1", "true", "yes", "on"]).has((raw ?? "").trim().toLowerCase());
|
|
22
83
|
}
|
|
@@ -25,6 +86,7 @@ function stepPasses(step) {
|
|
|
25
86
|
return false;
|
|
26
87
|
if (step.deferred_reason)
|
|
27
88
|
return true;
|
|
89
|
+
// `drift_probe` is a distinct diagnostic; it must never satisfy a step (#3507).
|
|
28
90
|
return step.ok === true;
|
|
29
91
|
}
|
|
30
92
|
function failedStepMessage(tierName, stepName, step) {
|
|
@@ -50,8 +112,31 @@ export function formatRitualRecoveryInstruction(tier = "cold") {
|
|
|
50
112
|
return (`Recovery: run ${ready} ` +
|
|
51
113
|
`(one-shot: session:start + gated ritual + cache recovery as needed).`);
|
|
52
114
|
}
|
|
53
|
-
|
|
54
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Sanctioned audited defer copy for gated `cache_fresh` failure (#3506 / #3507).
|
|
117
|
+
* Independent of the #3507 work-selection argv seam — defer is still the
|
|
118
|
+
* operator-audited escape, not the drift-probe skip.
|
|
119
|
+
*/
|
|
120
|
+
export function formatCacheFreshDeferSoftPath() {
|
|
121
|
+
const cmd = formatFrameworkCommand(["session:start", "--", "--defer", "cache_fresh=<reason>"]);
|
|
122
|
+
return `Soft path (audited): \`${cmd}\`.`;
|
|
123
|
+
}
|
|
124
|
+
function applyCacheFreshDriftProbeField(payload, command) {
|
|
125
|
+
if (command.includes(SKIP_DRIFT_PROBE_FLAG)) {
|
|
126
|
+
payload.drift_probe = DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (payload.drift_probe === DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION) {
|
|
130
|
+
delete payload.drift_probe;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function shouldRearmDriftProbe(payload, projectRoot, detect) {
|
|
134
|
+
if (payload.drift_probe !== DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION)
|
|
135
|
+
return false;
|
|
136
|
+
return detect(projectRoot).inPlay;
|
|
137
|
+
}
|
|
138
|
+
function runGatedStep(projectRoot, payload, stepName, runner, now, detect = detectWorkSelection) {
|
|
139
|
+
const command = resolveGatedEntrypointCommand(stepName, projectRoot, detect);
|
|
55
140
|
const { code, stdout, stderr } = runner(command, projectRoot);
|
|
56
141
|
const message = stdout.trim() || stderr.trim() || `${command[0]} exited ${code}`;
|
|
57
142
|
const gated = payload.gated_steps ?? {};
|
|
@@ -63,6 +148,9 @@ function runGatedStep(projectRoot, payload, stepName, runner, now) {
|
|
|
63
148
|
command,
|
|
64
149
|
});
|
|
65
150
|
payload.gated_steps = gated;
|
|
151
|
+
if (stepName === "cache_fresh") {
|
|
152
|
+
applyCacheFreshDriftProbeField(payload, command);
|
|
153
|
+
}
|
|
66
154
|
try {
|
|
67
155
|
writeRitualState(projectRoot, payload);
|
|
68
156
|
}
|
|
@@ -353,6 +441,7 @@ export function verifySessionRitual(projectRoot, options = {}) {
|
|
|
353
441
|
payload.gated_steps = gated;
|
|
354
442
|
const runCmd = options.runner ?? defaultRitualRunner;
|
|
355
443
|
const forced = new Set(options.forceGatedSteps ?? []);
|
|
444
|
+
const detect = options.detectWorkSelection ?? detectWorkSelection;
|
|
356
445
|
for (const stepName of GATED_STEPS) {
|
|
357
446
|
const step = gated[stepName];
|
|
358
447
|
if (step?.deferred_reason && stepName !== "agent_hooks")
|
|
@@ -360,9 +449,11 @@ export function verifySessionRitual(projectRoot, options = {}) {
|
|
|
360
449
|
// Hook readiness is a live mutation prerequisite, not a cacheable doctor
|
|
361
450
|
// result. Re-run it at every gated boundary so registration/shim drift
|
|
362
451
|
// cannot remain hidden behind an earlier green ritual record.
|
|
363
|
-
|
|
452
|
+
const rearmDrift = stepName === "cache_fresh" && shouldRearmDriftProbe(payload, projectRoot, detect);
|
|
453
|
+
if (stepName !== "agent_hooks" && stepPasses(step) && !forced.has(stepName) && !rearmDrift) {
|
|
364
454
|
continue;
|
|
365
|
-
|
|
455
|
+
}
|
|
456
|
+
const writeError = runGatedStep(projectRoot, payload, stepName, runCmd, instant, detect);
|
|
366
457
|
if (writeError !== null) {
|
|
367
458
|
return {
|
|
368
459
|
code: 2,
|
|
@@ -465,6 +556,7 @@ export function emitVerifyJson(result) {
|
|
|
465
556
|
would_fail_code: result.wouldFailCode,
|
|
466
557
|
posture: result.posture,
|
|
467
558
|
ritual_state_required: result.ritualStateRequired,
|
|
559
|
+
recovery_tier: result.recoveryTier ?? null,
|
|
468
560
|
});
|
|
469
561
|
}
|
|
470
562
|
export function emitBypassWarning(result) {
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ⊗ Fake project `.openclaw/commands/` file emitter.
|
|
12
12
|
*/
|
|
13
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync,
|
|
13
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join, resolve } from "node:path";
|
|
16
16
|
import { detectOpenClaw, isEscapingSkillSymlink, listInScopeSkillsDirs, } from "../doctor/openclaw-skills.js";
|
|
17
|
-
import { containedWrite } from "../fs/contained-write.js";
|
|
17
|
+
import { containedMkdir, containedRemove, containedRename, containedWrite, } from "../fs/contained-write.js";
|
|
18
|
+
import { isPortRecordMode } from "../fs/mutation-ledger.js";
|
|
18
19
|
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
19
20
|
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
20
21
|
import { generateOpenClawSkillArtifacts, isManagedOpenClawL2Skill, listOpenClawManagedSkillSlugs, } from "./openclaw-adapter.js";
|
|
@@ -122,7 +123,7 @@ function writeSkillArtifact(skillsDir, artifact) {
|
|
|
122
123
|
return "preserved";
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
+
containedMkdir({ root: skillsDir, target: skillDir });
|
|
126
127
|
// Re-check after mkdir: concurrent symlink plant is fail-closed.
|
|
127
128
|
if (skillDirLstatKind(skillDir) === "symlink" && isEscapingSkillSymlink(skillsDir, skillDir)) {
|
|
128
129
|
return "preserved";
|
|
@@ -137,11 +138,11 @@ function writeSkillArtifact(skillsDir, artifact) {
|
|
|
137
138
|
data: artifact.skillMarkdown,
|
|
138
139
|
mode: "replace",
|
|
139
140
|
});
|
|
140
|
-
|
|
141
|
+
containedRename({ root: skillsDir, from: tmp, to: skillFile, mutation: false });
|
|
141
142
|
}
|
|
142
143
|
catch (err) {
|
|
143
144
|
try {
|
|
144
|
-
|
|
145
|
+
containedRemove({ root: skillsDir, target: tmp, mutation: false });
|
|
145
146
|
}
|
|
146
147
|
catch {
|
|
147
148
|
/* best-effort */
|
|
@@ -172,8 +173,9 @@ function stripManagedSkills(skillsDir) {
|
|
|
172
173
|
if (!isManagedOpenClawL2Skill(raw))
|
|
173
174
|
continue;
|
|
174
175
|
try {
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
if (containedRemove({ root: skillsDir, target: skillDir, recursive: true }).removed) {
|
|
177
|
+
removed.push(skillDir);
|
|
178
|
+
}
|
|
177
179
|
}
|
|
178
180
|
catch {
|
|
179
181
|
/* best-effort */
|
|
@@ -221,7 +223,8 @@ export function depositOpenClawL2ProductCommands(options = {}) {
|
|
|
221
223
|
const preservedCustomPaths = [];
|
|
222
224
|
if (!policy) {
|
|
223
225
|
for (const skillsDir of skillsDirs) {
|
|
224
|
-
|
|
226
|
+
if (!isPortRecordMode())
|
|
227
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
225
228
|
removedPaths.push(...stripManagedSkills(skillsDir));
|
|
226
229
|
}
|
|
227
230
|
if (removedPaths.length > 0) {
|
|
@@ -242,7 +245,8 @@ export function depositOpenClawL2ProductCommands(options = {}) {
|
|
|
242
245
|
}
|
|
243
246
|
const artifacts = generateOpenClawSkillArtifacts();
|
|
244
247
|
for (const skillsDir of skillsDirs) {
|
|
245
|
-
|
|
248
|
+
if (!isPortRecordMode())
|
|
249
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
246
250
|
for (const artifact of artifacts) {
|
|
247
251
|
const outcome = writeSkillArtifact(skillsDir, artifact);
|
|
248
252
|
const abs = join(skillsDir, artifact.slug);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Default slices log path relative to project root. */
|
|
2
|
-
export declare const DEFAULT_SLICES_LOG_REL_PATH = "
|
|
1
|
+
/** Default slices log path relative to an xbrief-layout project root (#1703). */
|
|
2
|
+
export declare const DEFAULT_SLICES_LOG_REL_PATH = "xbrief/.triage-cache/slices.jsonl";
|
|
3
3
|
export declare const ENV_PROJECT_ROOT = "DEFT_PROJECT_ROOT";
|
|
4
4
|
export declare const ENV_PROJECT_REPO = "DEFT_PROJECT_REPO";
|
|
5
5
|
export declare const DEFAULT_ACTOR = "manual:operator";
|
package/dist/slice/constants.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Default slices log path relative to project root. */
|
|
2
|
-
export const DEFAULT_SLICES_LOG_REL_PATH = "
|
|
1
|
+
/** Default slices log path relative to an xbrief-layout project root (#1703). */
|
|
2
|
+
export const DEFAULT_SLICES_LOG_REL_PATH = "xbrief/.triage-cache/slices.jsonl";
|
|
3
3
|
export const ENV_PROJECT_ROOT = "DEFT_PROJECT_ROOT";
|
|
4
4
|
export const ENV_PROJECT_REPO = "DEFT_PROJECT_REPO";
|
|
5
5
|
export const DEFAULT_ACTOR = "manual:operator";
|
package/dist/slice/existing.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ContainedWriteError } from "../fs/contained-write.js";
|
|
2
2
|
import { call as scmCall } from "../scm/call.js";
|
|
3
3
|
import { pyRepr } from "../scm/py-format.js";
|
|
4
|
+
import { triageCacheRelPath } from "../triage/cache-path.js";
|
|
4
5
|
import { DEFAULT_ACTOR, DEFAULT_EXPECTED_CLOSE_SIGNAL, DEFAULT_ROLE, WAVE_FLAG_RE, } from "./constants.js";
|
|
5
6
|
import { IssueValidationError, SliceRecordError } from "./errors.js";
|
|
6
7
|
import { pythonJsonPretty } from "./json.js";
|
|
@@ -171,6 +172,10 @@ export function summariseWaves(waveMap, totalChildren) {
|
|
|
171
172
|
.map((waveN) => `wave-${waveN}=${placedByWave.get(waveN)}`);
|
|
172
173
|
return `${parts.length} wave(s): ${parts.join(", ")}`;
|
|
173
174
|
}
|
|
175
|
+
/** Layout-aware display path for the slices log actually read or written (#3386). */
|
|
176
|
+
function slicesLogDisplayPath(projectRoot) {
|
|
177
|
+
return triageCacheRelPath(projectRoot, "slices.jsonl");
|
|
178
|
+
}
|
|
174
179
|
function duplicateMessage(umbrella, duplicate) {
|
|
175
180
|
return (`slice:record-existing: umbrella #${umbrella} already has a matching record (slice_id=${String(duplicate.slice_id)}, ` +
|
|
176
181
|
`actor=${String(duplicate.actor)}). Re-run with --force to write a second record.`);
|
|
@@ -295,7 +300,7 @@ export function runRecordExisting(args, waveMap, deps = {}) {
|
|
|
295
300
|
const waveSummary = summariseWaves(waveMap, children.length);
|
|
296
301
|
return {
|
|
297
302
|
exitCode: 0,
|
|
298
|
-
stdout: `Wrote
|
|
303
|
+
stdout: `Wrote ${slicesLogDisplayPath(resolved.projectRoot)} entry for umbrella ` +
|
|
299
304
|
`#${args.umbrella} (${children.length} children, ${waveSummary}). ` +
|
|
300
305
|
`slice_id=${outcome.sliceId}\n`,
|
|
301
306
|
stderr: "",
|
|
@@ -320,6 +325,7 @@ export function runList(args) {
|
|
|
320
325
|
if (resolved.exitCode !== 0) {
|
|
321
326
|
return { exitCode: 2, stdout: "", stderr: `${formatMissingRootError()}\n` };
|
|
322
327
|
}
|
|
328
|
+
const logDisplay = slicesLogDisplayPath(resolved.projectRoot);
|
|
323
329
|
const records = readAll({ path: slicesPath(resolved.projectRoot) });
|
|
324
330
|
if (args.asJson) {
|
|
325
331
|
return { exitCode: 0, stdout: `${pythonJsonPretty(records)}\n`, stderr: "" };
|
|
@@ -327,11 +333,11 @@ export function runList(args) {
|
|
|
327
333
|
if (records.length === 0) {
|
|
328
334
|
return {
|
|
329
335
|
exitCode: 0,
|
|
330
|
-
stdout:
|
|
336
|
+
stdout: `slice:list: no records found in ${logDisplay} (file absent or empty).\n`,
|
|
331
337
|
stderr: "",
|
|
332
338
|
};
|
|
333
339
|
}
|
|
334
|
-
const lines = [`slice:list: ${records.length} record(s) in
|
|
340
|
+
const lines = [`slice:list: ${records.length} record(s) in ${logDisplay}`];
|
|
335
341
|
for (const record of records) {
|
|
336
342
|
const umbrella = record.umbrella ?? "?";
|
|
337
343
|
const actor = record.actor ?? "?";
|
package/dist/slice/lock.d.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
+
/** 10× occupancy heartbeat TTL. Off-Linux PID reuse / stalled holder reclaim. */
|
|
2
|
+
export declare const STALE_LOCK_HARD_CAP_MS: number;
|
|
1
3
|
export interface LockDeps {
|
|
2
4
|
readonly sleepMs?: (ms: number) => void;
|
|
3
5
|
readonly now?: () => number;
|
|
4
6
|
}
|
|
7
|
+
export interface HeldLock {
|
|
8
|
+
readonly lockPath: string;
|
|
9
|
+
readonly pid: number;
|
|
10
|
+
readonly token: string;
|
|
11
|
+
readonly acquiredAt: number;
|
|
12
|
+
}
|
|
13
|
+
export interface LockRecord {
|
|
14
|
+
readonly pid: number | null;
|
|
15
|
+
readonly token: string;
|
|
16
|
+
readonly acquiredAt: number | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function parseLockRecord(lockPath: string): LockRecord;
|
|
19
|
+
export declare function assertAppendLockOwned(held: HeldLock): void;
|
|
5
20
|
/** Serialise appenders across threads AND processes (sidecar lock file). */
|
|
6
|
-
export declare function withAppendLock<T>(logPath: string, fn: () => T, deps?: LockDeps): T;
|
|
21
|
+
export declare function withAppendLock<T>(logPath: string, fn: (held: HeldLock) => T, deps?: LockDeps): T;
|
|
7
22
|
/** Public alias mirroring Python append_lock. */
|
|
8
23
|
export declare const appendLock: typeof withAppendLock;
|
|
9
24
|
//# sourceMappingURL=lock.d.ts.map
|
package/dist/slice/lock.js
CHANGED
|
@@ -1,12 +1,127 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeSync, } from "node:fs";
|
|
2
3
|
import { dirname } from "node:path";
|
|
3
4
|
const threadLocked = { held: false };
|
|
5
|
+
/** 10× occupancy heartbeat TTL. Off-Linux PID reuse / stalled holder reclaim. */
|
|
6
|
+
export const STALE_LOCK_HARD_CAP_MS = 20 * 60 * 1000 * 10;
|
|
4
7
|
function defaultSleep(ms) {
|
|
5
8
|
const end = Date.now() + ms;
|
|
6
9
|
while (Date.now() < end) {
|
|
7
10
|
/* spin */
|
|
8
11
|
}
|
|
9
12
|
}
|
|
13
|
+
export function parseLockRecord(lockPath) {
|
|
14
|
+
try {
|
|
15
|
+
const lines = readFileSync(lockPath, { encoding: "utf8" }).trim().split(/\r?\n/);
|
|
16
|
+
const pidRaw = Number(lines[0]);
|
|
17
|
+
const pid = Number.isInteger(pidRaw) && pidRaw > 0 ? pidRaw : null;
|
|
18
|
+
if (lines.length >= 3) {
|
|
19
|
+
const acquiredRaw = Number(lines[2]);
|
|
20
|
+
return {
|
|
21
|
+
pid,
|
|
22
|
+
token: lines[1] ?? "",
|
|
23
|
+
acquiredAt: Number.isInteger(acquiredRaw) && acquiredRaw > 0 ? acquiredRaw : null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const acquiredRaw = Number(lines[1]);
|
|
27
|
+
return {
|
|
28
|
+
pid,
|
|
29
|
+
token: "",
|
|
30
|
+
acquiredAt: Number.isInteger(acquiredRaw) && acquiredRaw > 0 ? acquiredRaw : null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { pid: null, token: "", acquiredAt: null };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function assertAppendLockOwned(held) {
|
|
38
|
+
const rec = parseLockRecord(held.lockPath);
|
|
39
|
+
if (rec.pid !== held.pid || rec.token !== held.token || rec.token.length === 0) {
|
|
40
|
+
throw new Error(`lock compromised: ${held.lockPath}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function processExists(pid) {
|
|
44
|
+
try {
|
|
45
|
+
process.kill(pid, 0);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return err.code !== "ESRCH";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function linuxPidStartedAfter(pid, acquiredAt) {
|
|
53
|
+
try {
|
|
54
|
+
const stat = readFileSync(`/proc/${pid}/stat`, { encoding: "utf8" });
|
|
55
|
+
const closeParen = stat.lastIndexOf(")");
|
|
56
|
+
if (closeParen < 0)
|
|
57
|
+
return false;
|
|
58
|
+
const startTicks = Number(stat.slice(closeParen + 2).split(" ")[19]);
|
|
59
|
+
if (!Number.isFinite(startTicks))
|
|
60
|
+
return false;
|
|
61
|
+
const btime = /(?:^|\n)btime (\d+)/.exec(readFileSync("/proc/stat", { encoding: "utf8" }));
|
|
62
|
+
if (btime === null)
|
|
63
|
+
return false;
|
|
64
|
+
const startMs = Number(btime[1]) * 1000 + (startTicks * 1000) / 100;
|
|
65
|
+
return startMs > acquiredAt + 1000;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function lockMtimeMs(lockPath) {
|
|
72
|
+
try {
|
|
73
|
+
return statSync(lockPath).mtimeMs;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function isAbandoned(rec, now, lockPath) {
|
|
80
|
+
if (rec.pid === null) {
|
|
81
|
+
const mtime = lockMtimeMs(lockPath);
|
|
82
|
+
return mtime !== null && now - mtime > STALE_LOCK_HARD_CAP_MS;
|
|
83
|
+
}
|
|
84
|
+
if (!processExists(rec.pid))
|
|
85
|
+
return true;
|
|
86
|
+
if (rec.acquiredAt !== null && linuxPidStartedAfter(rec.pid, rec.acquiredAt))
|
|
87
|
+
return true;
|
|
88
|
+
if (rec.acquiredAt !== null && now - rec.acquiredAt > STALE_LOCK_HARD_CAP_MS)
|
|
89
|
+
return true;
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
function recordsMatch(a, b) {
|
|
93
|
+
return a.pid === b.pid && a.token === b.token && a.acquiredAt === b.acquiredAt;
|
|
94
|
+
}
|
|
95
|
+
/** Atomic rename reclaim: one waiter wins; losers see ENOENT. */
|
|
96
|
+
function tryReclaimAbandonedOwner(lockPath, now) {
|
|
97
|
+
const observed = parseLockRecord(lockPath);
|
|
98
|
+
if (!isAbandoned(observed, now, lockPath))
|
|
99
|
+
return false;
|
|
100
|
+
const quarantine = `${lockPath}.reclaim.${process.pid}.${randomUUID()}`;
|
|
101
|
+
try {
|
|
102
|
+
renameSync(lockPath, quarantine);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const quarantined = parseLockRecord(quarantine);
|
|
108
|
+
if (recordsMatch(observed, quarantined)) {
|
|
109
|
+
try {
|
|
110
|
+
unlinkSync(quarantine);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
/* already gone */
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
renameSync(quarantine, lockPath);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
/* best-effort restore */
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
10
125
|
/** Serialise appenders across threads AND processes (sidecar lock file). */
|
|
11
126
|
export function withAppendLock(logPath, fn, deps = {}) {
|
|
12
127
|
const sleepMs = deps.sleepMs ?? defaultSleep;
|
|
@@ -18,13 +133,18 @@ export function withAppendLock(logPath, fn, deps = {}) {
|
|
|
18
133
|
}
|
|
19
134
|
threadLocked.held = true;
|
|
20
135
|
let fd;
|
|
136
|
+
let held;
|
|
137
|
+
let reclaimedStale = false;
|
|
21
138
|
try {
|
|
22
139
|
const deadline = now() + 30_000;
|
|
23
140
|
while (true) {
|
|
24
141
|
try {
|
|
25
142
|
fd = openSync(lockPath, "wx");
|
|
26
|
-
|
|
27
|
-
|
|
143
|
+
const token = randomUUID();
|
|
144
|
+
const acquiredAt = now();
|
|
145
|
+
writeSync(fd, Buffer.from(`${process.pid}\n${token}\n${acquiredAt}\n`));
|
|
146
|
+
held = { lockPath, pid: process.pid, token, acquiredAt };
|
|
147
|
+
return fn(held);
|
|
28
148
|
}
|
|
29
149
|
catch (err) {
|
|
30
150
|
const code = err.code;
|
|
@@ -32,26 +152,32 @@ export function withAppendLock(logPath, fn, deps = {}) {
|
|
|
32
152
|
throw err;
|
|
33
153
|
}
|
|
34
154
|
if (now() > deadline) {
|
|
155
|
+
if (!reclaimedStale && tryReclaimAbandonedOwner(lockPath, now())) {
|
|
156
|
+
reclaimedStale = true;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
35
159
|
throw new Error(`timed out acquiring lock for ${logPath}`);
|
|
36
160
|
}
|
|
37
161
|
sleepMs(20);
|
|
38
162
|
}
|
|
39
163
|
}
|
|
40
|
-
return fn();
|
|
41
164
|
}
|
|
42
165
|
finally {
|
|
43
166
|
if (fd !== undefined) {
|
|
44
167
|
closeSync(fd);
|
|
45
168
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
169
|
+
if (held !== undefined) {
|
|
170
|
+
try {
|
|
171
|
+
assertAppendLockOwned(held);
|
|
172
|
+
if (existsSync(lockPath)) {
|
|
173
|
+
unlinkSync(lockPath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
/* not ours anymore */
|
|
50
178
|
}
|
|
51
179
|
}
|
|
52
|
-
|
|
53
|
-
/* best-effort */
|
|
54
|
-
}
|
|
180
|
+
threadLocked.held = false;
|
|
55
181
|
}
|
|
56
182
|
}
|
|
57
183
|
/** Public alias mirroring Python append_lock. */
|
|
@@ -20,6 +20,8 @@ export interface EvaluateOptions {
|
|
|
20
20
|
readonly projectRoot?: string;
|
|
21
21
|
/** Skip parent-lineage check (tests / opt-out). Default false. */
|
|
22
22
|
readonly skipParentLineage?: boolean;
|
|
23
|
+
/** Skip project-invariant coverage (#3425). Default false. */
|
|
24
|
+
readonly skipProjectInvariants?: boolean;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Pure evaluator — returns exit code + human message. Faithful to
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { evaluateProjectInvariantsGate, resolveProjectRootForInvariants, } from "../preflight/project-invariants-gate.js";
|
|
3
4
|
import { evaluateParentLineage, formatParentLineageLine, } from "../scope/parent-lineage.js";
|
|
4
5
|
import { parseAllocationSection, SOLO_KIND, SWARM_COHORT_KIND, VALID_DISPATCH_KINDS, } from "./allocation.js";
|
|
5
6
|
export const ACTIVE_FOLDER = "active";
|
|
@@ -168,6 +169,18 @@ export function evaluate(vbriefPath, options = {}) {
|
|
|
168
169
|
`\n${formatParentLineageLine(lineage)}`,
|
|
169
170
|
};
|
|
170
171
|
}
|
|
172
|
+
const invariants = evaluateProjectInvariantsGate(vbriefCheck.payload, {
|
|
173
|
+
projectRoot: resolveProjectRootForInvariants(vbriefCheck.path, options.projectRoot),
|
|
174
|
+
skip: options.skipProjectInvariants === true,
|
|
175
|
+
});
|
|
176
|
+
if (!invariants.ok) {
|
|
177
|
+
return {
|
|
178
|
+
exitCode: 1,
|
|
179
|
+
dispatchKind: null,
|
|
180
|
+
parentLineage: lineage,
|
|
181
|
+
message: `not ready: ${invariants.message}`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
171
184
|
const lineageNote = lineage.applicable
|
|
172
185
|
? `parent lineage OK (${lineage.parent_requirement_ids.length} req IDs` +
|
|
173
186
|
(lineage.negative_invariant_ids.length > 0
|
|
@@ -45,9 +45,12 @@ export declare function completeCohort(args: {
|
|
|
45
45
|
emitJson?: boolean;
|
|
46
46
|
/** Per-story delivery evidence; without it code-bearing stories fail closed (#3041). */
|
|
47
47
|
delivery?: CohortDeliveryContext | null;
|
|
48
|
+
/** Optional launch allocation plan id so close-out reads this cohort's occupancy slot. */
|
|
49
|
+
allocationPlanId?: string | null;
|
|
48
50
|
}): {
|
|
49
51
|
exitCode: number;
|
|
50
52
|
stdout: string;
|
|
51
53
|
stderr: string;
|
|
54
|
+
sweep: SweepResult | null;
|
|
52
55
|
};
|
|
53
56
|
//# sourceMappingURL=complete-cohort.d.ts.map
|