@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
|
@@ -175,7 +175,7 @@ export function verifyDeliveryAncestry(projectRoot, mergeCommit, deliveryBranch,
|
|
|
175
175
|
return {
|
|
176
176
|
ok: false,
|
|
177
177
|
error: `merge commit ${mergeCommit} is not an ancestor of refreshed remote delivery ref ` +
|
|
178
|
-
`${refresh.remoteRef} (${remoteTip})
|
|
178
|
+
`${refresh.remoteRef} (${remoteTip}).`,
|
|
179
179
|
remoteTip,
|
|
180
180
|
};
|
|
181
181
|
}
|
|
@@ -199,14 +199,21 @@ function buildProvenance(input) {
|
|
|
199
199
|
uatVerified: e.uatVerified ?? null,
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
+
function deliveryEvidenceRemediation(deliveryBranch) {
|
|
203
|
+
return (`Pass scope:complete -- --merge-commit <sha> (and --pr <n> if you have one). ` +
|
|
204
|
+
`When develop is the real delivery target, type plan.policy.deliveryBranch ` +
|
|
205
|
+
`(task policy:show --field=deliveryBranch). ` +
|
|
206
|
+
`When ${deliveryBranch} is delivery, wait until the merge commit is an ancestor of ` +
|
|
207
|
+
`origin/${deliveryBranch}, then complete. Do not use --non-delivery for work that shipped.`);
|
|
208
|
+
}
|
|
202
209
|
/**
|
|
203
|
-
* Gate delivered completion for a code-bearing scope (#3041).
|
|
210
|
+
* Gate delivered completion for a code-bearing scope (#3041 / #3380).
|
|
204
211
|
*
|
|
205
212
|
* Returns ok=true with provenance when:
|
|
206
213
|
* - scope is not code-bearing (provenance may be null or non-code note), OR
|
|
207
214
|
* - explicit non-delivery disposition is provided, OR
|
|
208
|
-
* -
|
|
209
|
-
*
|
|
215
|
+
* - merge commit is an ancestor of the refreshed remote delivery ref (prBase is
|
|
216
|
+
* provenance only; it need not equal deliveryBranch).
|
|
210
217
|
*/
|
|
211
218
|
export function evaluateDeliveryGate(options) {
|
|
212
219
|
const runGit = options.runGit ?? defaultGitRunner;
|
|
@@ -267,10 +274,8 @@ export function evaluateDeliveryGate(options) {
|
|
|
267
274
|
return {
|
|
268
275
|
ok: false,
|
|
269
276
|
message: `Delivery evidence required for code-bearing scope completion (#3041). ` +
|
|
270
|
-
`
|
|
271
|
-
`
|
|
272
|
-
`(${NON_DELIVERY_DISPOSITIONS.join("|")}). ` +
|
|
273
|
-
`Merged-to-integration alone is not delivery.`,
|
|
277
|
+
`A merge commit that is an ancestor of refreshed origin/${deliveryBranch} is delivery; ` +
|
|
278
|
+
`PR base is provenance only. ${deliveryEvidenceRemediation(deliveryBranch)}`,
|
|
274
279
|
provenance: null,
|
|
275
280
|
codeBearing: true,
|
|
276
281
|
};
|
|
@@ -294,27 +299,11 @@ export function evaluateDeliveryGate(options) {
|
|
|
294
299
|
};
|
|
295
300
|
}
|
|
296
301
|
}
|
|
297
|
-
if (prBase !== null && prBase !== deliveryBranch) {
|
|
298
|
-
const provenance = buildProvenance({
|
|
299
|
-
evidence,
|
|
300
|
-
deliveryBranch,
|
|
301
|
-
disposition: "merged_to_integration",
|
|
302
|
-
handoffState: "merged_to_integration",
|
|
303
|
-
nowIso: options.nowIso,
|
|
304
|
-
verifier,
|
|
305
|
-
});
|
|
306
|
-
return {
|
|
307
|
-
ok: false,
|
|
308
|
-
message: `PR base '${prBase}' is not the configured delivery branch '${deliveryBranch}'. ` +
|
|
309
|
-
`A merge into an intermediate/feature/integration branch is not delivery evidence (#3041).`,
|
|
310
|
-
provenance,
|
|
311
|
-
codeBearing: true,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
302
|
if (mergeCommit === null) {
|
|
315
303
|
return {
|
|
316
304
|
ok: false,
|
|
317
|
-
message: "Delivery evidence missing merge_commit_sha; cannot prove ancestry on delivery branch (#3041)."
|
|
305
|
+
message: "Delivery evidence missing merge_commit_sha; cannot prove ancestry on delivery branch (#3041). " +
|
|
306
|
+
deliveryEvidenceRemediation(deliveryBranch),
|
|
318
307
|
provenance: null,
|
|
319
308
|
codeBearing: true,
|
|
320
309
|
};
|
|
@@ -339,9 +328,15 @@ export function evaluateDeliveryGate(options) {
|
|
|
339
328
|
const ancestry = verifyDeliveryAncestry(options.projectRoot, mergeCommit, deliveryBranch, runGit);
|
|
340
329
|
if (!ancestry.ok) {
|
|
341
330
|
const integrationOnly = prBase !== null && prBase !== deliveryBranch;
|
|
331
|
+
const ancestryMsg = ancestry.error ?? "delivery ancestry check failed";
|
|
332
|
+
const rem = deliveryEvidenceRemediation(deliveryBranch);
|
|
333
|
+
const message = integrationOnly
|
|
334
|
+
? `${ancestryMsg} PR base '${prBase}' is provenance only; ` +
|
|
335
|
+
`the merge is not yet on origin/${deliveryBranch}. ${rem}`
|
|
336
|
+
: `${ancestryMsg} ${rem}`;
|
|
342
337
|
return {
|
|
343
338
|
ok: false,
|
|
344
|
-
message
|
|
339
|
+
message,
|
|
345
340
|
provenance: buildProvenance({
|
|
346
341
|
evidence,
|
|
347
342
|
deliveryBranch,
|
package/dist/scope/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./main.js";
|
|
|
12
12
|
export * from "./open-umbrella-warning.js";
|
|
13
13
|
export * from "./parent-lineage.js";
|
|
14
14
|
export * from "./project-context.js";
|
|
15
|
+
export * from "./project-invariant-coverage.js";
|
|
15
16
|
export * from "./promote-from-issue.js";
|
|
16
17
|
export * from "./promote-path.js";
|
|
17
18
|
export * from "./transition.js";
|
package/dist/scope/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./main.js";
|
|
|
12
12
|
export * from "./open-umbrella-warning.js";
|
|
13
13
|
export * from "./parent-lineage.js";
|
|
14
14
|
export * from "./project-context.js";
|
|
15
|
+
export * from "./project-invariant-coverage.js";
|
|
15
16
|
export * from "./promote-from-issue.js";
|
|
16
17
|
export * from "./promote-path.js";
|
|
17
18
|
export * from "./transition.js";
|
package/dist/scope/main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { maybeRunStalenessTickler } from "../staleness-tickler/run.js";
|
|
4
|
+
import { interceptHelp } from "../triage/help/index.js";
|
|
4
5
|
import { reconcileUmbrellas, renderUmbrellasReport } from "../vbrief-reconcile/umbrellas.js";
|
|
5
6
|
import { canonicalLogPath, readAll } from "./audit-log.js";
|
|
6
7
|
import { batchPromote } from "./batch-promote.js";
|
|
@@ -13,12 +14,11 @@ import { promoteFromIssue } from "./promote-from-issue.js";
|
|
|
13
14
|
import { promotePath } from "./promote-path.js";
|
|
14
15
|
import { runTransition } from "./transition.js";
|
|
15
16
|
import { findByDecisionId, isAlreadyUndone, REVERSIBLE_ACTIONS, undoBatch, undoOne, } from "./undo.js";
|
|
16
|
-
const LIFECYCLE_USAGE_STDERR = "usage:
|
|
17
|
-
" [--
|
|
17
|
+
const LIFECYCLE_USAGE_STDERR = "usage: deft scope:<action> [file] [--batch] [--from-issue N] [--repo OWNER/NAME]\n" +
|
|
18
|
+
" [--force] [--project-root PATH] [--strict] [--force-no-cache]\n" +
|
|
18
19
|
" [--path PATH]\n" +
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"scope_lifecycle.py: error: the following arguments are required: action, file\n" +
|
|
20
|
+
" actions: activate, block, cancel, complete, fail, promote, restore, unblock\n" +
|
|
21
|
+
"The verb already encodes the action (e.g. deft scope:promote <file>). Do not pass the action again.\n" +
|
|
22
22
|
"(promote --batch may omit file and promotes all proposed/ scopes; #3011)\n" +
|
|
23
23
|
"(promote --from-issue=N may omit file; #1136)\n";
|
|
24
24
|
function parseLifecycleArgv(argv) {
|
|
@@ -50,6 +50,8 @@ function parseLifecycleArgv(argv) {
|
|
|
50
50
|
if (arg === undefined) {
|
|
51
51
|
return { args: null, error: "usage" };
|
|
52
52
|
}
|
|
53
|
+
if (arg === "--")
|
|
54
|
+
continue;
|
|
53
55
|
if (arg === "--force") {
|
|
54
56
|
force = true;
|
|
55
57
|
}
|
|
@@ -236,6 +238,14 @@ function parseLifecycleArgv(argv) {
|
|
|
236
238
|
}
|
|
237
239
|
/** Main entry for scope_lifecycle.py parity. */
|
|
238
240
|
export function lifecycleMain(argv) {
|
|
241
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
242
|
+
const helpRc = interceptHelp("scope_lifecycle", argv);
|
|
243
|
+
if (helpRc !== null) {
|
|
244
|
+
return helpRc;
|
|
245
|
+
}
|
|
246
|
+
process.stdout.write(LIFECYCLE_USAGE_STDERR);
|
|
247
|
+
return 0;
|
|
248
|
+
}
|
|
239
249
|
const parsed = parseLifecycleArgv(argv);
|
|
240
250
|
if (parsed.args === null) {
|
|
241
251
|
if (parsed.error === "usage") {
|
|
@@ -98,20 +98,21 @@ export function extractChildCoverageDraft(child) {
|
|
|
98
98
|
candidates.push({ source: "plan", obj: plan });
|
|
99
99
|
candidates.push({ source: "root", obj: root });
|
|
100
100
|
for (const c of candidates) {
|
|
101
|
+
const nestedCoverage = asRecord(c.obj.coverage);
|
|
101
102
|
const hasMap = "coverage_map" in c.obj ||
|
|
102
103
|
"coverageMap" in c.obj ||
|
|
103
|
-
|
|
104
|
+
nestedCoverage?.coverage_map !== undefined ||
|
|
105
|
+
nestedCoverage?.coverageMap !== undefined;
|
|
104
106
|
if (!hasMap)
|
|
105
107
|
continue;
|
|
106
|
-
const coverageNested = asRecord(c.obj.coverage);
|
|
107
108
|
const coverage_map = c.obj.coverage_map ??
|
|
108
109
|
c.obj.coverageMap ??
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
nestedCoverage?.coverage_map ??
|
|
111
|
+
nestedCoverage?.coverageMap;
|
|
111
112
|
const behavioral_deltas = c.obj.behavioral_deltas ??
|
|
112
113
|
c.obj.behavioralDeltas ??
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
nestedCoverage?.behavioral_deltas ??
|
|
115
|
+
nestedCoverage?.behavioralDeltas;
|
|
115
116
|
return {
|
|
116
117
|
draft: {
|
|
117
118
|
coverage_map,
|
|
@@ -0,0 +1,54 @@
|
|
|
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 type { ProjectInvariant } from "../policy/project-invariants.js";
|
|
9
|
+
export declare const PROJECT_INVARIANT_DISPOSITIONS: readonly ["covered", "deferred", "behavioral_delta", "not_applicable"];
|
|
10
|
+
export type ProjectInvariantDisposition = (typeof PROJECT_INVARIANT_DISPOSITIONS)[number];
|
|
11
|
+
export interface ApplicableInvariant {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly reason: "path" | "module" | "unresolved-module";
|
|
14
|
+
}
|
|
15
|
+
export interface ProjectInvariantCoverageResult {
|
|
16
|
+
readonly ok: boolean;
|
|
17
|
+
readonly applicableIds: readonly string[];
|
|
18
|
+
readonly missingIds: readonly string[];
|
|
19
|
+
readonly errors: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export type GlobDepth = "exact" | "one" | "subtree";
|
|
22
|
+
export interface ClassifiedGlob {
|
|
23
|
+
readonly prefix: string;
|
|
24
|
+
readonly depth: GlobDepth;
|
|
25
|
+
readonly leadingDoubleStar: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Classify a path/glob without regular expressions (CodeQL js/polynomial-redos).
|
|
29
|
+
* Unstarred paths are directory prefixes (file_scope semantics).
|
|
30
|
+
*/
|
|
31
|
+
export declare function classifyGlob(glob: string): ClassifiedGlob;
|
|
32
|
+
/** Strip glob tails so prefix comparison stays path-shaped. */
|
|
33
|
+
export declare function normalizeGlobAsPath(glob: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* True when two path/glob strings can name a shared tree.
|
|
36
|
+
* `/*` is one segment; `/**` / unstarred file_scope paths are subtrees.
|
|
37
|
+
*/
|
|
38
|
+
export declare function pathGlobsIntersect(left: string, right: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Invariants whose contract surface intersects `fileScope`.
|
|
41
|
+
* Empty file_scope → empty intersection (no disposition required).
|
|
42
|
+
* Unresolved module ids (no pathGlobs mapping) are treated as applicable.
|
|
43
|
+
*/
|
|
44
|
+
export declare function applicableProjectInvariants(invariants: readonly ProjectInvariant[], fileScope: readonly string[], modulePathGlobs?: Readonly<Record<string, readonly string[]>>): ApplicableInvariant[];
|
|
45
|
+
/**
|
|
46
|
+
* Completeness of coverage_map dispositions for applicable project invariant IDs.
|
|
47
|
+
* Extra coverage_map keys (e.g. parent requirement IDs) are ignored.
|
|
48
|
+
* Empty applicable set is a no-op success.
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateProjectInvariantCoverage(opts: {
|
|
51
|
+
applicableIds: readonly string[];
|
|
52
|
+
draft: unknown;
|
|
53
|
+
}): ProjectInvariantCoverageResult;
|
|
54
|
+
//# sourceMappingURL=project-invariant-coverage.d.ts.map
|
|
@@ -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
|
|
@@ -195,7 +195,7 @@ export function promoteFromIssue(options) {
|
|
|
195
195
|
return {
|
|
196
196
|
ok: false,
|
|
197
197
|
message: `No triage-cache decision for #${n} (${repoSafe}). ` +
|
|
198
|
-
`Accept first:
|
|
198
|
+
`Accept first: deft triage:accept --issue ${n} --repo ${repoSafe} ` +
|
|
199
199
|
`(or omit --strict to soft-warn and proceed; --force-no-cache skips the gate).`,
|
|
200
200
|
exitCode: 1,
|
|
201
201
|
warnings,
|
|
@@ -212,7 +212,7 @@ export function promoteFromIssue(options) {
|
|
|
212
212
|
ok: false,
|
|
213
213
|
message: `Refusing promote for #${n} (${repoSafe}): latest triage decision is '${sanitizeMsg(latest.decision)}' ` +
|
|
214
214
|
`(decision_id=${latest.decision_id}). ` +
|
|
215
|
-
`Accept first:
|
|
215
|
+
`Accept first: deft triage:accept --issue ${n} --repo ${repoSafe} ` +
|
|
216
216
|
`or override with --force-no-cache.`,
|
|
217
217
|
exitCode: 1,
|
|
218
218
|
warnings,
|
|
@@ -315,8 +315,8 @@ export function promoteFromIssue(options) {
|
|
|
315
315
|
return {
|
|
316
316
|
ok: false,
|
|
317
317
|
message: `No proposed/ scope artifact found for issue #${n} (${repoSafe}). ` +
|
|
318
|
-
`Ingest first (
|
|
319
|
-
`or pass an explicit path:
|
|
318
|
+
`Ingest first (deft triage:accept --issue ${n} --repo ${repoSafe}) ` +
|
|
319
|
+
`or pass an explicit path: deft scope:promote <path>.`,
|
|
320
320
|
exitCode: 1,
|
|
321
321
|
warnings,
|
|
322
322
|
repo,
|
|
@@ -330,7 +330,7 @@ export function promoteFromIssue(options) {
|
|
|
330
330
|
return {
|
|
331
331
|
ok: false,
|
|
332
332
|
message: `Multiple proposed/ artifacts match issue #${n}; refuse to guess.\n${list}\n` +
|
|
333
|
-
`Promote one path explicitly:
|
|
333
|
+
`Promote one path explicitly: deft scope:promote <path> ` +
|
|
334
334
|
`or re-run with --path <one-of-the-above>.`,
|
|
335
335
|
exitCode: 1,
|
|
336
336
|
warnings,
|
package/dist/scope/transition.js
CHANGED
|
@@ -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
|