@deftai/directive-core 0.103.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 +682 -35
- package/dist/check/cached-orchestrator.d.ts +1 -1
- package/dist/check/cached-orchestrator.js +2 -2
- package/dist/check/gate-lists.d.ts +3 -2
- package/dist/check/gate-lists.js +5 -2
- package/dist/check/index.d.ts +1 -0
- package/dist/check/index.js +1 -0
- package/dist/check/named-cause.d.ts +1 -1
- package/dist/check/named-cause.js +33 -3
- package/dist/check/session-completed-ac.d.ts +39 -0
- package/dist/check/session-completed-ac.js +198 -0
- 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 -14
- package/dist/doctor/types.d.ts +9 -0
- package/dist/fs/contained-write.d.ts +115 -1
- package/dist/fs/contained-write.js +242 -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 +2 -0
- package/dist/index.js +3 -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 +74 -0
- package/dist/intake/clause-derivation.js +409 -0
- package/dist/intake/index.d.ts +1 -0
- package/dist/intake/index.js +1 -0
- package/dist/intake/issue-ingest.d.ts +3 -1
- package/dist/intake/issue-ingest.js +36 -43
- 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 +9 -0
- package/dist/preflight/evaluate.js +49 -1
- package/dist/preflight/index.d.ts +5 -1
- 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 +5 -2
- package/dist/product-first-done-gate/empty-resolution.js +13 -2
- package/dist/product-first-done-gate/evaluate.d.ts +29 -0
- package/dist/product-first-done-gate/evaluate.js +307 -33
- package/dist/product-first-done-gate/index.d.ts +3 -1
- package/dist/product-first-done-gate/index.js +2 -1
- package/dist/release/build-dist.d.ts +101 -0
- package/dist/release/build-dist.js +428 -46
- package/dist/run-summary/emit.d.ts +50 -4
- package/dist/run-summary/emit.js +262 -26
- 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/share.d.ts +1 -1
- package/dist/run-summary/share.js +2 -2
- package/dist/run-summary/types.d.ts +38 -6
- package/dist/run-summary/types.js +1 -0
- package/dist/scope/acceptance-activate-gate.d.ts +1 -1
- package/dist/scope/acceptance-activate-gate.js +4 -4
- package/dist/scope/acceptance-evidence.d.ts +27 -0
- package/dist/scope/acceptance-evidence.js +66 -0
- package/dist/scope/capacity-stamp.d.ts +2 -0
- package/dist/scope/capacity-stamp.js +4 -0
- package/dist/scope/delivery-evidence.d.ts +9 -3
- package/dist/scope/delivery-evidence.js +39 -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 +66 -4
- 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/ceremony-dial-evidence.d.ts +42 -0
- package/dist/session/ceremony-dial-evidence.js +194 -0
- package/dist/session/git.js +19 -4
- package/dist/session/index.d.ts +5 -0
- package/dist/session/index.js +5 -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 +88 -5
- 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 +21 -4
- package/dist/slice/lock.d.ts +16 -1
- package/dist/slice/lock.js +137 -11
- package/dist/slice/record.d.ts +8 -5
- package/dist/slice/record.js +21 -13
- 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/evaluate.d.ts +42 -0
- package/dist/telemetry-coverage/evaluate.js +148 -0
- package/dist/telemetry-coverage/fake-trial.d.ts +45 -0
- package/dist/telemetry-coverage/fake-trial.js +204 -0
- package/dist/telemetry-coverage/index.d.ts +5 -0
- package/dist/telemetry-coverage/index.js +5 -0
- package/dist/telemetry-coverage/kinds.d.ts +19 -0
- package/dist/telemetry-coverage/kinds.js +54 -0
- package/dist/telemetry-coverage/scan-callers.d.ts +25 -0
- package/dist/telemetry-coverage/scan-callers.js +168 -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 +3 -1
- package/dist/vbrief-reconcile/index.js +2 -1
- package/dist/vbrief-reconcile/origin-freshness.d.ts +44 -0
- package/dist/vbrief-reconcile/origin-freshness.js +221 -0
- 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/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -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
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-invariant coverage + applicability (#3425 Story A).
|
|
3
|
+
*
|
|
4
|
+
* Reuses the #3238 coverage_map parser and side-field rules. `split` is
|
|
5
|
+
* excluded at project level. Optional `not_applicable` requires a reason.
|
|
6
|
+
* Applicability is a pure intersection of contract surface × file_scope.
|
|
7
|
+
*/
|
|
8
|
+
import { matchPath, normalizePath } from "../orchestration/pathspec.js";
|
|
9
|
+
import { parseBehavioralDeltas, parseCoverageMap } from "./coverage-map.js";
|
|
10
|
+
export const PROJECT_INVARIANT_DISPOSITIONS = [
|
|
11
|
+
"covered",
|
|
12
|
+
"deferred",
|
|
13
|
+
"behavioral_delta",
|
|
14
|
+
"not_applicable",
|
|
15
|
+
];
|
|
16
|
+
function asRecord(value) {
|
|
17
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function isNonEmptyString(value) {
|
|
23
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Classify a path/glob without regular expressions (CodeQL js/polynomial-redos).
|
|
27
|
+
* Unstarred paths are directory prefixes (file_scope semantics).
|
|
28
|
+
*/
|
|
29
|
+
export function classifyGlob(glob) {
|
|
30
|
+
let p = normalizePath(glob).trim();
|
|
31
|
+
if (p.endsWith("/"))
|
|
32
|
+
p = p.slice(0, -1);
|
|
33
|
+
if (p === "**") {
|
|
34
|
+
return { prefix: "", depth: "subtree", leadingDoubleStar: true };
|
|
35
|
+
}
|
|
36
|
+
if (p.startsWith("**/")) {
|
|
37
|
+
let rest = p.slice(3);
|
|
38
|
+
if (rest.endsWith("/*"))
|
|
39
|
+
rest = rest.slice(0, -2);
|
|
40
|
+
const inner = rest.indexOf("/**");
|
|
41
|
+
if (inner >= 0)
|
|
42
|
+
rest = rest.slice(0, inner);
|
|
43
|
+
return { prefix: rest, depth: "subtree", leadingDoubleStar: true };
|
|
44
|
+
}
|
|
45
|
+
const starStar = p.indexOf("/**");
|
|
46
|
+
if (starStar >= 0) {
|
|
47
|
+
return { prefix: p.slice(0, starStar), depth: "subtree", leadingDoubleStar: false };
|
|
48
|
+
}
|
|
49
|
+
if (p.endsWith("/*")) {
|
|
50
|
+
return { prefix: p.slice(0, -2), depth: "one", leadingDoubleStar: false };
|
|
51
|
+
}
|
|
52
|
+
if (p.includes("*") || p.includes("?")) {
|
|
53
|
+
const slashStar = p.indexOf("/*");
|
|
54
|
+
if (slashStar >= 0) {
|
|
55
|
+
return { prefix: p.slice(0, slashStar), depth: "subtree", leadingDoubleStar: false };
|
|
56
|
+
}
|
|
57
|
+
return { prefix: p, depth: "subtree", leadingDoubleStar: false };
|
|
58
|
+
}
|
|
59
|
+
return { prefix: p, depth: "subtree", leadingDoubleStar: false };
|
|
60
|
+
}
|
|
61
|
+
/** Strip glob tails so prefix comparison stays path-shaped. */
|
|
62
|
+
export function normalizeGlobAsPath(glob) {
|
|
63
|
+
return classifyGlob(glob).prefix;
|
|
64
|
+
}
|
|
65
|
+
function remainderUnder(child, parent) {
|
|
66
|
+
if (child === parent)
|
|
67
|
+
return "";
|
|
68
|
+
const stem = parent.length === 0 ? "" : `${parent}/`;
|
|
69
|
+
if (parent.length === 0)
|
|
70
|
+
return child;
|
|
71
|
+
if (!child.startsWith(stem))
|
|
72
|
+
return null;
|
|
73
|
+
return child.slice(stem.length);
|
|
74
|
+
}
|
|
75
|
+
function depthAllows(depth, remainder) {
|
|
76
|
+
if (depth === "subtree")
|
|
77
|
+
return true;
|
|
78
|
+
if (depth === "exact")
|
|
79
|
+
return remainder.length === 0;
|
|
80
|
+
return remainder.length > 0 && !remainder.includes("/");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* True when two path/glob strings can name a shared tree.
|
|
84
|
+
* `/*` is one segment; `/**` / unstarred file_scope paths are subtrees.
|
|
85
|
+
*/
|
|
86
|
+
export function pathGlobsIntersect(left, right) {
|
|
87
|
+
const a = normalizePath(left).trim();
|
|
88
|
+
const b = normalizePath(right).trim();
|
|
89
|
+
if (a.length === 0 || b.length === 0)
|
|
90
|
+
return false;
|
|
91
|
+
if (a === b)
|
|
92
|
+
return true;
|
|
93
|
+
if (matchPath(a, b) || matchPath(b, a))
|
|
94
|
+
return true;
|
|
95
|
+
const ca = classifyGlob(a);
|
|
96
|
+
const cb = classifyGlob(b);
|
|
97
|
+
if (ca.prefix.length === 0 && cb.prefix.length === 0)
|
|
98
|
+
return true;
|
|
99
|
+
const aUnderB = remainderUnder(ca.prefix, cb.prefix);
|
|
100
|
+
if (aUnderB !== null && depthAllows(cb.depth, aUnderB))
|
|
101
|
+
return true;
|
|
102
|
+
const bUnderA = remainderUnder(cb.prefix, ca.prefix);
|
|
103
|
+
if (bUnderA !== null && depthAllows(ca.depth, bUnderA))
|
|
104
|
+
return true;
|
|
105
|
+
if (ca.leadingDoubleStar && suffixMatch(cb.prefix, ca.prefix))
|
|
106
|
+
return true;
|
|
107
|
+
if (cb.leadingDoubleStar && suffixMatch(ca.prefix, cb.prefix))
|
|
108
|
+
return true;
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function suffixMatch(path, suffix) {
|
|
112
|
+
if (suffix.length === 0 || path.length === 0)
|
|
113
|
+
return false;
|
|
114
|
+
return path === suffix || path.endsWith(`/${suffix}`);
|
|
115
|
+
}
|
|
116
|
+
function surfacePaths(invariant, modulePathGlobs) {
|
|
117
|
+
const paths = [...invariant.contractSurface.paths];
|
|
118
|
+
const unresolvedModuleIds = [];
|
|
119
|
+
for (const moduleId of invariant.contractSurface.moduleIds) {
|
|
120
|
+
const mapped = modulePathGlobs[moduleId];
|
|
121
|
+
if (mapped === undefined || mapped.length === 0) {
|
|
122
|
+
unresolvedModuleIds.push(moduleId);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
paths.push(...mapped);
|
|
126
|
+
}
|
|
127
|
+
return { paths, unresolvedModuleIds };
|
|
128
|
+
}
|
|
129
|
+
function anyPathOverlap(left, right) {
|
|
130
|
+
for (const a of left) {
|
|
131
|
+
for (const b of right) {
|
|
132
|
+
if (pathGlobsIntersect(a, b))
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Invariants whose contract surface intersects `fileScope`.
|
|
140
|
+
* Empty file_scope → empty intersection (no disposition required).
|
|
141
|
+
* Unresolved module ids (no pathGlobs mapping) are treated as applicable.
|
|
142
|
+
*/
|
|
143
|
+
export function applicableProjectInvariants(invariants, fileScope, modulePathGlobs = {}) {
|
|
144
|
+
const scope = fileScope.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
145
|
+
if (scope.length === 0)
|
|
146
|
+
return [];
|
|
147
|
+
const out = [];
|
|
148
|
+
for (const invariant of invariants) {
|
|
149
|
+
const { paths, unresolvedModuleIds } = surfacePaths(invariant, modulePathGlobs);
|
|
150
|
+
if (unresolvedModuleIds.length > 0) {
|
|
151
|
+
out.push({ id: invariant.id, reason: "unresolved-module" });
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (anyPathOverlap(paths, scope)) {
|
|
155
|
+
const viaModule = invariant.contractSurface.moduleIds.length > 0 && paths.length > 0;
|
|
156
|
+
out.push({
|
|
157
|
+
id: invariant.id,
|
|
158
|
+
reason: viaModule && invariant.contractSurface.paths.length === 0 ? "module" : "path",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
function peelNotApplicable(raw) {
|
|
165
|
+
const notApplicable = [];
|
|
166
|
+
const errors = [];
|
|
167
|
+
if (raw === null || raw === undefined) {
|
|
168
|
+
return { remainder: raw, notApplicable, errors };
|
|
169
|
+
}
|
|
170
|
+
const takeReason = (body) => {
|
|
171
|
+
const nested = asRecord(body.provenance);
|
|
172
|
+
const reason = nested?.reason ?? body.reason;
|
|
173
|
+
return isNonEmptyString(reason) ? reason.trim() : null;
|
|
174
|
+
};
|
|
175
|
+
if (Array.isArray(raw)) {
|
|
176
|
+
const remainder = [];
|
|
177
|
+
for (let i = 0; i < raw.length; i += 1) {
|
|
178
|
+
const item = raw[i];
|
|
179
|
+
const loc = `coverage_map[${i}]`;
|
|
180
|
+
const rec = asRecord(item);
|
|
181
|
+
if (rec === null) {
|
|
182
|
+
remainder.push(item);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const disposition = rec.disposition;
|
|
186
|
+
if (typeof disposition === "string" &&
|
|
187
|
+
disposition.trim().toLowerCase() === "not_applicable") {
|
|
188
|
+
const id = rec.parent_requirement_id ?? rec.parentRequirementId ?? rec.id;
|
|
189
|
+
if (!isNonEmptyString(id)) {
|
|
190
|
+
errors.push(`${loc}: parent_requirement_id is required`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const reason = takeReason(rec);
|
|
194
|
+
if (reason === null) {
|
|
195
|
+
errors.push(`${loc}: disposition not_applicable requires reason`);
|
|
196
|
+
}
|
|
197
|
+
notApplicable.push({ id: id.trim(), reason, loc });
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
remainder.push(item);
|
|
201
|
+
}
|
|
202
|
+
return { remainder, notApplicable, errors };
|
|
203
|
+
}
|
|
204
|
+
const map = asRecord(raw);
|
|
205
|
+
if (map === null) {
|
|
206
|
+
return { remainder: raw, notApplicable, errors };
|
|
207
|
+
}
|
|
208
|
+
const remainder = {};
|
|
209
|
+
for (const [key, value] of Object.entries(map)) {
|
|
210
|
+
const loc = `coverage_map[${key}]`;
|
|
211
|
+
const rec = asRecord(value);
|
|
212
|
+
if (rec === null) {
|
|
213
|
+
remainder[key] = value;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const disposition = rec.disposition;
|
|
217
|
+
if (typeof disposition === "string" && disposition.trim().toLowerCase() === "not_applicable") {
|
|
218
|
+
const id = rec.parent_requirement_id ?? rec.parentRequirementId ?? key;
|
|
219
|
+
if (!isNonEmptyString(id)) {
|
|
220
|
+
errors.push(`${loc}: parent_requirement_id is required`);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const reason = takeReason(rec);
|
|
224
|
+
if (reason === null) {
|
|
225
|
+
errors.push(`${loc}: disposition not_applicable requires reason`);
|
|
226
|
+
}
|
|
227
|
+
notApplicable.push({ id: String(id).trim(), reason, loc });
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
remainder[key] = value;
|
|
231
|
+
}
|
|
232
|
+
return { remainder, notApplicable, errors };
|
|
233
|
+
}
|
|
234
|
+
function extractCoverageRaw(draft) {
|
|
235
|
+
const rec = asRecord(draft);
|
|
236
|
+
if (rec === null)
|
|
237
|
+
return undefined;
|
|
238
|
+
if (rec.coverage_map !== undefined || rec.coverageMap !== undefined) {
|
|
239
|
+
return rec.coverage_map ?? rec.coverageMap;
|
|
240
|
+
}
|
|
241
|
+
const plan = asRecord(rec.plan);
|
|
242
|
+
if (plan === null)
|
|
243
|
+
return undefined;
|
|
244
|
+
if (plan.coverage_map !== undefined || plan.coverageMap !== undefined) {
|
|
245
|
+
return plan.coverage_map ?? plan.coverageMap;
|
|
246
|
+
}
|
|
247
|
+
const metadata = asRecord(plan.metadata);
|
|
248
|
+
if (metadata === null)
|
|
249
|
+
return undefined;
|
|
250
|
+
return metadata.coverage_map ?? metadata.coverageMap;
|
|
251
|
+
}
|
|
252
|
+
function extractDeltasRaw(draft) {
|
|
253
|
+
const rec = asRecord(draft);
|
|
254
|
+
if (rec === null)
|
|
255
|
+
return undefined;
|
|
256
|
+
if (rec.behavioral_deltas !== undefined || rec.behavioralDeltas !== undefined) {
|
|
257
|
+
return rec.behavioral_deltas ?? rec.behavioralDeltas;
|
|
258
|
+
}
|
|
259
|
+
const plan = asRecord(rec.plan);
|
|
260
|
+
if (plan === null)
|
|
261
|
+
return undefined;
|
|
262
|
+
return plan.behavioral_deltas ?? plan.behavioralDeltas;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Completeness of coverage_map dispositions for applicable project invariant IDs.
|
|
266
|
+
* Extra coverage_map keys (e.g. parent requirement IDs) are ignored.
|
|
267
|
+
* Empty applicable set is a no-op success.
|
|
268
|
+
*/
|
|
269
|
+
export function validateProjectInvariantCoverage(opts) {
|
|
270
|
+
const applicableIds = [...new Set(opts.applicableIds.map((id) => id.trim()).filter(Boolean))];
|
|
271
|
+
const errors = [];
|
|
272
|
+
const coverageRaw = extractCoverageRaw(opts.draft);
|
|
273
|
+
const peeled = peelNotApplicable(coverageRaw);
|
|
274
|
+
errors.push(...peeled.errors);
|
|
275
|
+
const { entries, errors: mapErrors } = parseCoverageMap(peeled.remainder);
|
|
276
|
+
errors.push(...mapErrors);
|
|
277
|
+
const { byId: deltasById, errors: deltaErrors } = parseBehavioralDeltas(extractDeltasRaw(opts.draft));
|
|
278
|
+
errors.push(...deltaErrors);
|
|
279
|
+
const applicableSet = new Set(applicableIds);
|
|
280
|
+
const byId = new Map();
|
|
281
|
+
for (const entry of entries) {
|
|
282
|
+
if (!applicableSet.has(entry.parent_requirement_id)) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (entry.disposition === "split") {
|
|
286
|
+
errors.push(`coverage_map[${entry.parent_requirement_id}]: disposition split is excluded at project level`);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (byId.has(entry.parent_requirement_id)) {
|
|
290
|
+
errors.push(`coverage_map[${entry.parent_requirement_id}]: duplicate coverage entries ` +
|
|
291
|
+
`(one disposition per project invariant ID)`);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
byId.set(entry.parent_requirement_id, entry);
|
|
295
|
+
if (entry.disposition === "behavioral_delta") {
|
|
296
|
+
const deltaId = entry.delta_id;
|
|
297
|
+
if (isNonEmptyString(deltaId) && !deltasById.has(deltaId)) {
|
|
298
|
+
errors.push(`coverage_map[${entry.parent_requirement_id}]: behavioral_delta delta_id '${deltaId}' ` +
|
|
299
|
+
`has no linked record in behavioral_deltas`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const addressed = new Set();
|
|
304
|
+
for (const na of peeled.notApplicable) {
|
|
305
|
+
addressed.add(na.id);
|
|
306
|
+
}
|
|
307
|
+
for (const id of byId.keys()) {
|
|
308
|
+
addressed.add(id);
|
|
309
|
+
}
|
|
310
|
+
const missingIds = applicableIds.filter((id) => !addressed.has(id));
|
|
311
|
+
if (missingIds.length > 0) {
|
|
312
|
+
errors.push(`uncovered applicable project invariant IDs: ${missingIds.join(", ")} ` +
|
|
313
|
+
`(declare coverage_map[<id>].disposition as ${PROJECT_INVARIANT_DISPOSITIONS.join("|")})`);
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
ok: errors.length === 0,
|
|
317
|
+
applicableIds,
|
|
318
|
+
missingIds,
|
|
319
|
+
errors,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=project-invariant-coverage.js.map
|
package/dist/scope/transition.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { SESSION_COMPLETED_AC_REMEDIATION, writeSessionCompletedMarker, } from "../check/session-completed-ac.js";
|
|
3
4
|
import { InstrumentedVbriefCrud, persistCrudMetrics } from "../eval/crud-telemetry.js";
|
|
4
5
|
import { assertProjectionContained, ProjectionContainmentError, } from "../fs/projection-containment.js";
|
|
6
|
+
import { applyClauseDerivationToPlan, maybeEmitAcceptanceStampFromChange, } from "../intake/clause-derivation.js";
|
|
5
7
|
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
6
8
|
import { evaluateCompletedPlanConsistency } from "../lifecycle/completed-consistency.js";
|
|
7
9
|
import { evaluateLiteralAcceptanceFromPlan } from "../literal-acceptance/index.js";
|
|
8
10
|
import { evaluateAcceptanceActivateGate } from "./acceptance-activate-gate.js";
|
|
9
|
-
import { evaluateAcceptanceEvidenceGate, formatAcceptanceCompletionListing, } from "./acceptance-evidence.js";
|
|
11
|
+
import { evaluateAcceptanceEvidenceGate, evaluateScopeCompleteAcceptanceWalk, formatAcceptanceCompletionListing, } from "./acceptance-evidence.js";
|
|
10
12
|
import { append, canonicalLogPath, newDecisionId } from "./audit-log.js";
|
|
11
13
|
import { atomicWriteBrief, formatBriefJson, readBriefForMutation } from "./brief-io.js";
|
|
12
14
|
import { stampCompletionMetadata } from "./capacity-stamp.js";
|
|
13
15
|
import { LIFECYCLE_FOLDERS, MOVE_LABELS, STATUS_PRECONDITIONS, STAY_LABELS, TRANSITIONS, } from "./constants.js";
|
|
14
16
|
import { detectLifecycleFolder, updateDecomposedChildBackReferences, updateDecomposedParentBackReferences, } from "./decomposed-refs.js";
|
|
15
|
-
import { classifyStoredDeliveryDisposition, evaluateDeliveryGate, stampDeliveryProvenance, } from "./delivery-evidence.js";
|
|
17
|
+
import { classifyStoredDeliveryDisposition, evaluateDeliveryGate, resolveCompletionSessionId, stampDeliveryProvenance, } from "./delivery-evidence.js";
|
|
16
18
|
import { evaluateEffortActivateGate } from "./effort-activate-gate.js";
|
|
17
19
|
import { syncProjectDefinitionAfterScopeMove } from "./project-definition-sync.js";
|
|
18
20
|
import { syncSpecificationAfterScopeMove } from "./specification-sync.js";
|
|
@@ -143,6 +145,19 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
const nowIso = utcNowIso(now);
|
|
148
|
+
// #3360: hand-authored briefs run #3323 clause derivation on activate/promote.
|
|
149
|
+
// #3355: stamp is state-observed (before vs after), not derivation-notice-bound.
|
|
150
|
+
const previousAcceptance = planObj.acceptance;
|
|
151
|
+
let derivationNotice = "";
|
|
152
|
+
if (act === "activate" || act === "promote") {
|
|
153
|
+
const derivation = applyClauseDerivationToPlan(planObj, {
|
|
154
|
+
projectRoot,
|
|
155
|
+
emitStamp: false,
|
|
156
|
+
});
|
|
157
|
+
if (derivation.notice.length > 0) {
|
|
158
|
+
derivationNotice = derivation.notice;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
146
161
|
// #1581: fail closed before activating a scope that still has XL plan items.
|
|
147
162
|
if (act === "activate") {
|
|
148
163
|
const effortGate = evaluateEffortActivateGate(planObj);
|
|
@@ -170,7 +185,10 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
170
185
|
return { ok: false, message: gate.message };
|
|
171
186
|
}
|
|
172
187
|
if (gate.provenance !== null) {
|
|
173
|
-
|
|
188
|
+
const sessionId = resolveCompletionSessionId(projectRoot);
|
|
189
|
+
stampDeliveryProvenance(planObj, sessionId !== null
|
|
190
|
+
? { ...gate.provenance, completedSessionId: sessionId }
|
|
191
|
+
: gate.provenance);
|
|
174
192
|
}
|
|
175
193
|
}
|
|
176
194
|
// #3240: per-criterion typed evidence or human-origin disposition before auto-advance.
|
|
@@ -207,6 +225,19 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
207
225
|
? `${acceptanceListing}\n${literalGate.message}`
|
|
208
226
|
: literalGate.message;
|
|
209
227
|
}
|
|
228
|
+
// #3357: verify:ac walk is a hard precondition. Disposition does not skip it.
|
|
229
|
+
const acWalk = evaluateScopeCompleteAcceptanceWalk(planObj, { projectRoot });
|
|
230
|
+
if (!acWalk.ok) {
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
message: acWalk.message,
|
|
234
|
+
acceptanceReports: acceptanceGate.reports,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (acWalk.message.length > 0) {
|
|
238
|
+
acceptanceListing =
|
|
239
|
+
acceptanceListing.length > 0 ? `${acceptanceListing}\n${acWalk.message}` : acWalk.message;
|
|
240
|
+
}
|
|
210
241
|
}
|
|
211
242
|
planObj.status = targetStatus;
|
|
212
243
|
planObj.updated = nowIso;
|
|
@@ -218,7 +249,9 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
218
249
|
advanceNonTerminalOwnItems(planObj.items, targetStatus);
|
|
219
250
|
}
|
|
220
251
|
if (act === "complete") {
|
|
221
|
-
stampCompletionMetadata(planObj, projectRoot, nowIso
|
|
252
|
+
stampCompletionMetadata(planObj, projectRoot, nowIso, {
|
|
253
|
+
completedSessionId: resolveCompletionSessionId(projectRoot),
|
|
254
|
+
});
|
|
222
255
|
// #3242 / epic #3237 Q4: after reconcile, completed lifecycle must match
|
|
223
256
|
// plan.status=completed and terminal plan.items (compose with #3240).
|
|
224
257
|
const consistency = evaluateCompletedPlanConsistency(planObj, {
|
|
@@ -249,6 +282,27 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
249
282
|
return { ok: false, message: writeResult.message };
|
|
250
283
|
}
|
|
251
284
|
crud.recordTrustedUpdate(destPath, formatted);
|
|
285
|
+
if (act === "complete") {
|
|
286
|
+
const sessionId = resolveCompletionSessionId(projectRoot);
|
|
287
|
+
if (sessionId !== null) {
|
|
288
|
+
try {
|
|
289
|
+
writeSessionCompletedMarker(projectRoot, {
|
|
290
|
+
path: destPath,
|
|
291
|
+
sessionId,
|
|
292
|
+
completedAt: nowIso,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
try {
|
|
297
|
+
unlinkSync(destPath);
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
/* dest rollback after marker failure */
|
|
301
|
+
}
|
|
302
|
+
return { ok: false, message: SESSION_COMPLETED_AC_REMEDIATION };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
252
306
|
try {
|
|
253
307
|
unlinkSync(resolvedPath);
|
|
254
308
|
}
|
|
@@ -280,7 +334,11 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
280
334
|
};
|
|
281
335
|
}
|
|
282
336
|
syncSpecificationAfterScopeMove(data, resolvedPath, destPath, vbriefRoot, targetStatus);
|
|
337
|
+
if (act === "activate" || act === "promote") {
|
|
338
|
+
maybeEmitAcceptanceStampFromChange(projectRoot, previousAcceptance, planObj.acceptance);
|
|
339
|
+
}
|
|
283
340
|
const moveMsg = `${actionLabel} ${basename}: ${currentFolder}/ -> ${targetFolder}/ (status: ${targetStatus})` +
|
|
341
|
+
(derivationNotice.length > 0 ? `\n${derivationNotice}` : "") +
|
|
284
342
|
(acceptanceListing.length > 0 ? `\n${acceptanceListing}` : "");
|
|
285
343
|
return {
|
|
286
344
|
ok: true,
|
|
@@ -300,7 +358,11 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
300
358
|
/* best-effort telemetry persistence */
|
|
301
359
|
}
|
|
302
360
|
const actionLabel = STAY_LABELS[act] ?? act.charAt(0).toUpperCase() + act.slice(1);
|
|
361
|
+
if (act === "activate" || act === "promote") {
|
|
362
|
+
maybeEmitAcceptanceStampFromChange(projectRoot, previousAcceptance, planObj.acceptance);
|
|
363
|
+
}
|
|
303
364
|
const stayMsg = `${actionLabel} ${basename}: stays in ${currentFolder}/ (status: ${targetStatus})` +
|
|
365
|
+
(derivationNotice.length > 0 ? `\n${derivationNotice}` : "") +
|
|
304
366
|
(acceptanceListing.length > 0 ? `\n${acceptanceListing}` : "");
|
|
305
367
|
return {
|
|
306
368
|
ok: true,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public compare entry for extracted intent (#3376 R2 / R5 / #3385 F1–F2).
|
|
3
|
+
*
|
|
4
|
+
* Scalars are byte-equal after canonicalize. Decisions and references[] are
|
|
5
|
+
* guarded-append. Unclassified key only when unknown AND absent from base.
|
|
6
|
+
*/
|
|
7
|
+
import { type IntentPreimage } from "./extract-intent.js";
|
|
8
|
+
export type IntentCompareKind = "intent-drift" | "unclassified-key" | "reference-mutation" | "decisions-mutation" | "repo-not-approved" | "ref-type-not-appendable" | "decision-pointer-missing";
|
|
9
|
+
export interface IntentCompareFinding {
|
|
10
|
+
readonly kind: IntentCompareKind;
|
|
11
|
+
readonly path: string;
|
|
12
|
+
readonly detail: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IntentCompareResult {
|
|
15
|
+
readonly ok: boolean;
|
|
16
|
+
readonly findings: readonly IntentCompareFinding[];
|
|
17
|
+
}
|
|
18
|
+
export interface CompareIntentOptions {
|
|
19
|
+
readonly changedFiles?: readonly string[];
|
|
20
|
+
readonly decisionExists?: (relPath: string) => boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compare a live extraction to the base-committed preimage.
|
|
24
|
+
* One entry point covers scalar equality and guarded append (R2 / R5).
|
|
25
|
+
*/
|
|
26
|
+
export declare function compareExtractedIntent(base: IntentPreimage, live: IntentPreimage, options?: CompareIntentOptions): IntentCompareResult;
|
|
27
|
+
//# sourceMappingURL=compare-intent.d.ts.map
|