@deftai/directive-core 0.99.0 → 0.100.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/actions.d.ts +6 -0
- package/dist/authz/actions.js +3 -0
- package/dist/authz/classify.js +157 -23
- package/dist/authz/decompose-apply.d.ts +67 -0
- package/dist/authz/decompose-apply.js +302 -0
- package/dist/authz/index.d.ts +3 -1
- package/dist/authz/index.js +3 -1
- package/dist/authz/store.d.ts +53 -0
- package/dist/authz/store.js +219 -1
- package/dist/authz/types.d.ts +19 -2
- package/dist/authz/types.js +5 -1
- package/dist/doctor/checks.d.ts +44 -0
- package/dist/doctor/checks.js +428 -2
- package/dist/doctor/main.d.ts +6 -7
- package/dist/doctor/main.js +72 -79
- package/dist/doctor/which.d.ts +20 -1
- package/dist/doctor/which.js +67 -1
- package/dist/finish-loop/pr-finish-loop.d.ts +13 -2
- package/dist/finish-loop/pr-finish-loop.js +116 -5
- package/dist/finish-loop/types.d.ts +1 -1
- package/dist/hooks/dispatcher.d.ts +9 -5
- package/dist/hooks/dispatcher.js +23 -15
- package/dist/hooks/readonly.d.ts +6 -3
- package/dist/hooks/readonly.js +44 -25
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init-deposit/agent-hooks.d.ts +20 -0
- package/dist/init-deposit/agent-hooks.js +51 -31
- package/dist/intake/issue-ingest.js +29 -0
- package/dist/lifecycle/completed-consistency.d.ts +60 -0
- package/dist/lifecycle/completed-consistency.js +443 -0
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +47 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +415 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +52 -0
- package/dist/literal-acceptance/capture.js +658 -0
- package/dist/literal-acceptance/evaluate.d.ts +47 -0
- package/dist/literal-acceptance/evaluate.js +193 -0
- package/dist/literal-acceptance/index.d.ts +12 -0
- package/dist/literal-acceptance/index.js +12 -0
- package/dist/literal-acceptance/run.d.ts +38 -0
- package/dist/literal-acceptance/run.js +216 -0
- package/dist/literal-acceptance/safety.d.ts +24 -0
- package/dist/literal-acceptance/safety.js +251 -0
- package/dist/literal-acceptance/types.d.ts +82 -0
- package/dist/literal-acceptance/types.js +26 -0
- package/dist/policy/ceremony-dial.d.ts +67 -11
- package/dist/policy/ceremony-dial.js +174 -23
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +1 -0
- package/dist/policy/merge-approval-head.d.ts +129 -0
- package/dist/policy/merge-approval-head.js +461 -0
- package/dist/policy/require-human-merge.d.ts +5 -0
- package/dist/policy/require-human-merge.js +5 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +20 -3
- package/dist/pr-merge-readiness/ci-gate.js +65 -5
- package/dist/pr-merge-readiness/compute.d.ts +13 -0
- package/dist/pr-merge-readiness/compute.js +99 -14
- package/dist/pr-merge-readiness/gh.d.ts +49 -0
- package/dist/pr-merge-readiness/gh.js +268 -0
- package/dist/pr-merge-readiness/index.d.ts +2 -2
- package/dist/pr-merge-readiness/index.js +2 -2
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +4 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +11 -0
- package/dist/pr-wait-mergeable/cascade.js +115 -1
- package/dist/pr-wait-mergeable/main.d.ts +4 -0
- package/dist/pr-wait-mergeable/main.js +4 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -1
- package/dist/pr-wait-mergeable/wrappers.js +7 -1
- package/dist/preflight/evaluate.d.ts +15 -1
- package/dist/preflight/evaluate.js +33 -2
- package/dist/render/framework-commands.js +4 -0
- package/dist/scope/acceptance-evidence.d.ts +76 -0
- package/dist/scope/acceptance-evidence.js +348 -0
- package/dist/scope/coverage-map.d.ts +98 -0
- package/dist/scope/coverage-map.js +558 -0
- package/dist/scope/decompose.js +125 -11
- package/dist/scope/index.d.ts +3 -0
- package/dist/scope/index.js +3 -0
- package/dist/scope/parent-lineage.d.ts +125 -0
- package/dist/scope/parent-lineage.js +626 -0
- package/dist/scope/transition.d.ts +8 -0
- package/dist/scope/transition.js +61 -2
- package/dist/session/active-cli.d.ts +79 -0
- package/dist/session/active-cli.js +382 -0
- package/dist/session/effort-budget.d.ts +130 -0
- package/dist/session/effort-budget.js +334 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/session-start.d.ts +6 -0
- package/dist/session/session-start.js +53 -2
- package/dist/session/verify-session-ritual.d.ts +14 -0
- package/dist/session/verify-session-ritual.js +33 -0
- package/dist/story-ready/evaluate.d.ts +11 -0
- package/dist/story-ready/evaluate.js +41 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -2
- package/dist/triage/actions/candidates-log.js +37 -6
- package/dist/xbrief-migrate/migrate-project.d.ts +4 -0
- package/dist/xbrief-migrate/migrate-project.js +92 -3
- package/package.json +7 -3
package/dist/scope/decompose.js
CHANGED
|
@@ -6,14 +6,18 @@
|
|
|
6
6
|
* A caller supplies a draft with child story definitions; this module validates
|
|
7
7
|
* that draft, writes the child story vBRIEFs, and updates the parent scope references.
|
|
8
8
|
*/
|
|
9
|
-
import { accessSync, constants, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
9
|
+
import { accessSync, constants, existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
10
10
|
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
11
11
|
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
12
|
+
import { evaluateDecomposeStructuralApply, sha256Hex } from "../authz/decompose-apply.js";
|
|
13
|
+
import { claimSingleUseGrantForApply } from "../authz/store.js";
|
|
12
14
|
import { containedWrite } from "../fs/contained-write.js";
|
|
13
15
|
import { ProjectionContainmentError } from "../fs/projection-containment.js";
|
|
14
16
|
import { hasArtifactSuffix, LEGACY_ARTIFACT_DIR, MIGRATED_ARTIFACT_DIR, resolveLifecycleRoot, } from "../layout/resolve.js";
|
|
15
17
|
import { referenceWithDefaultTrust, slugify } from "../vbrief-build/build.js";
|
|
16
18
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
19
|
+
import { formatCoverageReportLine, validateCoverageMap } from "./coverage-map.js";
|
|
20
|
+
import { buildParentLineageArtifact } from "./parent-lineage.js";
|
|
17
21
|
import { formatBriefJson } from "./vbrief-json.js";
|
|
18
22
|
// ---------------------------------------------------------------------------
|
|
19
23
|
// Constants
|
|
@@ -35,6 +39,13 @@ export class DecompositionError extends Error {
|
|
|
35
39
|
// File I/O helpers
|
|
36
40
|
// ---------------------------------------------------------------------------
|
|
37
41
|
function loadJson(path) {
|
|
42
|
+
return loadJsonWithRaw(path).data;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Single-read load: parse + return exact on-disk bytes used for #3239 digest binding.
|
|
46
|
+
* Avoids TOCTOU between validation snapshot and authz hash.
|
|
47
|
+
*/
|
|
48
|
+
function loadJsonWithRaw(path) {
|
|
38
49
|
let raw;
|
|
39
50
|
try {
|
|
40
51
|
raw = readFileSync(path, "utf8");
|
|
@@ -52,7 +63,7 @@ function loadJson(path) {
|
|
|
52
63
|
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
53
64
|
throw new DecompositionError(`${path}: expected a JSON object`);
|
|
54
65
|
}
|
|
55
|
-
return data;
|
|
66
|
+
return { data: data, raw };
|
|
56
67
|
}
|
|
57
68
|
function writeJson(projectRoot, path, data) {
|
|
58
69
|
// #2980 wave D: product write sink routes through containedWrite.
|
|
@@ -684,6 +695,10 @@ export function validateDraft(stories) {
|
|
|
684
695
|
if (parallelSafe !== true && parallelSafe !== false) {
|
|
685
696
|
issues.push("plan.metadata.swarm.parallel_safe");
|
|
686
697
|
}
|
|
698
|
+
// Parity with swarm:readiness (#3252): large work cannot be concurrent.
|
|
699
|
+
if (sw.size === "large" && parallelSafe === true) {
|
|
700
|
+
issues.push("size=large cannot be parallel_safe=true");
|
|
701
|
+
}
|
|
687
702
|
if (items.length === 0)
|
|
688
703
|
issues.push("plan.items");
|
|
689
704
|
if (items.length > 0 && !itemsHaveAcceptance(items)) {
|
|
@@ -777,7 +792,7 @@ function childFilename(story, stId, title, date) {
|
|
|
777
792
|
return `${date}-${sl}.vbrief.json`;
|
|
778
793
|
}
|
|
779
794
|
function buildChildVbrief(opts) {
|
|
780
|
-
const { story, storyId: stId, storyIndex, parent, parentRel, status } = opts;
|
|
795
|
+
const { story, storyId: stId, storyIndex, parent, parentRel, status, parentLineage } = opts;
|
|
781
796
|
const title = String(story.title ?? stId);
|
|
782
797
|
const sw = swarmMeta(story);
|
|
783
798
|
const items = itemsFromStory(stId, story);
|
|
@@ -786,6 +801,9 @@ function buildChildVbrief(opts) {
|
|
|
786
801
|
: {};
|
|
787
802
|
metadata.kind = "story";
|
|
788
803
|
metadata.swarm = sw;
|
|
804
|
+
if (parentLineage !== undefined && parentLineage !== null) {
|
|
805
|
+
metadata.parent_lineage = parentLineage;
|
|
806
|
+
}
|
|
789
807
|
const parentPlan = typeof parent.plan === "object" && parent.plan !== null && !Array.isArray(parent.plan)
|
|
790
808
|
? parent.plan
|
|
791
809
|
: {};
|
|
@@ -812,9 +830,17 @@ export function applyDecomposition(opts) {
|
|
|
812
830
|
const { projectRoot, parentPath, draftPath, checkOnly, date } = opts;
|
|
813
831
|
const vbriefDirPath = vbriefDir(projectRoot);
|
|
814
832
|
const parent = loadJson(parentPath);
|
|
815
|
-
|
|
833
|
+
// Single draft read: digest must bind the exact bytes used to build child docs (#3239 P1).
|
|
834
|
+
const { data: draft, raw: draftRaw } = loadJsonWithRaw(draftPath);
|
|
835
|
+
const draftDigest = sha256Hex(draftRaw);
|
|
816
836
|
const stories = storySpecs(draft);
|
|
817
837
|
const stIds = validateDraft(stories);
|
|
838
|
+
// #3238: semantic fidelity — parent requirement IDs + coverage map (structure only).
|
|
839
|
+
const coverage = validateCoverageMap({ parent, draft, storyIds: stIds });
|
|
840
|
+
if (!coverage.ok) {
|
|
841
|
+
const summary = coverage.errors.join("; ");
|
|
842
|
+
throw new DecompositionError(`semantic coverage failed: ${summary}\n${formatCoverageReportLine(coverage.report)}`);
|
|
843
|
+
}
|
|
818
844
|
let outputDir;
|
|
819
845
|
const draftOutputDir = draft.output_dir;
|
|
820
846
|
if (typeof draftOutputDir === "string" && draftOutputDir.trim().length > 0) {
|
|
@@ -843,6 +869,35 @@ export function applyDecomposition(opts) {
|
|
|
843
869
|
}
|
|
844
870
|
const parentRel = relToVbrief(vbriefDirPath, parentPath);
|
|
845
871
|
const actions = [`VALIDATED ${stories.length} story decomposition draft`];
|
|
872
|
+
// Machine-readable coverage report (always emit when parent authored IDs or map present).
|
|
873
|
+
if (coverage.report.parent_requirement_ids.length > 0 || coverage.report.entries.length > 0) {
|
|
874
|
+
actions.push(formatCoverageReportLine(coverage.report));
|
|
875
|
+
}
|
|
876
|
+
// #3241: stamp full parent coverage artifacts onto every child so story-ready /
|
|
877
|
+
// preflight can re-check lineage without re-reading the decompose draft.
|
|
878
|
+
// When parent authors requirement IDs, durable parent_plan_id is mandatory —
|
|
879
|
+
// omit/skip would let an exact-path replacement pass identity-unchecked.
|
|
880
|
+
const draftRec = draft;
|
|
881
|
+
const parentPlanRec = typeof parent.plan === "object" && parent.plan !== null && !Array.isArray(parent.plan)
|
|
882
|
+
? parent.plan
|
|
883
|
+
: {};
|
|
884
|
+
const parentPlanId = typeof parentPlanRec.id === "string" && parentPlanRec.id.trim().length > 0
|
|
885
|
+
? parentPlanRec.id.trim()
|
|
886
|
+
: undefined;
|
|
887
|
+
let parentLineageArtifact = null;
|
|
888
|
+
if (coverage.report.parent_requirement_ids.length > 0) {
|
|
889
|
+
if (parentPlanId === undefined) {
|
|
890
|
+
throw new DecompositionError("parent authors requirement IDs but has no non-empty plan.id — " +
|
|
891
|
+
"refuse decompose without durable parent identity for parent_plan_id stamp (#3241)");
|
|
892
|
+
}
|
|
893
|
+
parentLineageArtifact = buildParentLineageArtifact({
|
|
894
|
+
coverage_map: draftRec.coverage_map ?? draftRec.coverageMap,
|
|
895
|
+
behavioral_deltas: draftRec.behavioral_deltas ?? draftRec.behavioralDeltas,
|
|
896
|
+
parent_requirement_ids: coverage.report.parent_requirement_ids,
|
|
897
|
+
negative_invariant_ids: coverage.report.negative_invariant_ids,
|
|
898
|
+
parent_plan_id: parentPlanId,
|
|
899
|
+
});
|
|
900
|
+
}
|
|
846
901
|
const childPaths = [];
|
|
847
902
|
const childDocs = [];
|
|
848
903
|
for (let i = 0; i < stories.length; i += 1) {
|
|
@@ -865,6 +920,7 @@ export function applyDecomposition(opts) {
|
|
|
865
920
|
parent,
|
|
866
921
|
parentRel,
|
|
867
922
|
status: storyStatus,
|
|
923
|
+
parentLineage: parentLineageArtifact,
|
|
868
924
|
});
|
|
869
925
|
childPaths.push({ target, storyId: stId, title });
|
|
870
926
|
childDocs.push(child);
|
|
@@ -873,13 +929,20 @@ export function applyDecomposition(opts) {
|
|
|
873
929
|
}
|
|
874
930
|
if (checkOnly)
|
|
875
931
|
return actions;
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
932
|
+
// #3239: structural apply requires human-origin grant bound to exact draft digest
|
|
933
|
+
// from the single load above. --check is ungated (returned earlier).
|
|
934
|
+
const authz = evaluateDecomposeStructuralApply({
|
|
935
|
+
projectRoot,
|
|
936
|
+
parentPath,
|
|
937
|
+
draftPath,
|
|
938
|
+
draftDigest,
|
|
939
|
+
});
|
|
940
|
+
if (!authz.allowed) {
|
|
941
|
+
throw new DecompositionError(authz.reason);
|
|
882
942
|
}
|
|
943
|
+
actions.push(`AUTHZ grant=${authz.humanApprovalRef ?? "unknown"} digest=${draftDigest.slice(0, 12)}`);
|
|
944
|
+
// Parent mutation validation before claim/writes so invalid parents do not
|
|
945
|
+
// spend a single-use grant (#3239).
|
|
883
946
|
let parentPlan = parent.plan;
|
|
884
947
|
if (parentPlan === null || parentPlan === undefined) {
|
|
885
948
|
parentPlan = {};
|
|
@@ -918,7 +981,58 @@ export function applyDecomposition(opts) {
|
|
|
918
981
|
planObj.references = dedupeReferences(references
|
|
919
982
|
.filter((r) => typeof r === "object" && r !== null && !Array.isArray(r))
|
|
920
983
|
.map((r) => r));
|
|
921
|
-
|
|
984
|
+
// Exclusive claim: revalidate under lock → mark single-use usedAt → write all
|
|
985
|
+
// children + parent; apply throw rolls usedAt back for retry (#3239 residual).
|
|
986
|
+
// Partial child files created in this attempt are best-effort removed so retry
|
|
987
|
+
// is not blocked by "child already exists".
|
|
988
|
+
const writeProtected = () => {
|
|
989
|
+
const created = [];
|
|
990
|
+
try {
|
|
991
|
+
for (const { target } of childPaths) {
|
|
992
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
993
|
+
}
|
|
994
|
+
for (let i = 0; i < childPaths.length; i += 1) {
|
|
995
|
+
// biome-ignore lint/style/noNonNullAssertion: loop bound ensures these exist
|
|
996
|
+
const target = childPaths[i].target;
|
|
997
|
+
// biome-ignore lint/style/noNonNullAssertion: loop bound ensures these exist
|
|
998
|
+
writeJson(projectRoot, target, childDocs[i]);
|
|
999
|
+
created.push(target);
|
|
1000
|
+
}
|
|
1001
|
+
writeJson(projectRoot, parentPath, parent);
|
|
1002
|
+
}
|
|
1003
|
+
catch (err) {
|
|
1004
|
+
for (const path of created.reverse()) {
|
|
1005
|
+
try {
|
|
1006
|
+
rmSync(path, { force: true });
|
|
1007
|
+
}
|
|
1008
|
+
catch {
|
|
1009
|
+
/* best-effort rollback of this attempt's children */
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
throw err;
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
if (authz.humanApprovalRef !== null) {
|
|
1016
|
+
const claim = claimSingleUseGrantForApply(projectRoot, authz.humanApprovalRef, {
|
|
1017
|
+
revalidate: (grant) => {
|
|
1018
|
+
const again = evaluateDecomposeStructuralApply({
|
|
1019
|
+
projectRoot,
|
|
1020
|
+
parentPath,
|
|
1021
|
+
draftPath,
|
|
1022
|
+
draftDigest,
|
|
1023
|
+
grants: [grant],
|
|
1024
|
+
});
|
|
1025
|
+
return again.allowed ? { ok: true } : { ok: false, reason: again.reason };
|
|
1026
|
+
},
|
|
1027
|
+
apply: writeProtected,
|
|
1028
|
+
});
|
|
1029
|
+
if (!claim.ok) {
|
|
1030
|
+
throw new DecompositionError(claim.reason);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
writeProtected();
|
|
1035
|
+
}
|
|
922
1036
|
actions.push(`UPDATE ${parentRel} references`);
|
|
923
1037
|
return actions;
|
|
924
1038
|
}
|
package/dist/scope/index.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
export * from "./acceptance-evidence.js";
|
|
1
2
|
export * from "./audit-log.js";
|
|
2
3
|
export * from "./batch-promote.js";
|
|
3
4
|
export * from "./constants.js";
|
|
5
|
+
export * from "./coverage-map.js";
|
|
4
6
|
export * from "./decomposed-refs.js";
|
|
5
7
|
export * from "./delivery-evidence.js";
|
|
6
8
|
export * from "./demote.js";
|
|
7
9
|
export * from "./effort-activate-gate.js";
|
|
8
10
|
export * from "./main.js";
|
|
9
11
|
export * from "./open-umbrella-warning.js";
|
|
12
|
+
export * from "./parent-lineage.js";
|
|
10
13
|
export * from "./project-context.js";
|
|
11
14
|
export * from "./promote-from-issue.js";
|
|
12
15
|
export * from "./promote-path.js";
|
package/dist/scope/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
export * from "./acceptance-evidence.js";
|
|
1
2
|
export * from "./audit-log.js";
|
|
2
3
|
export * from "./batch-promote.js";
|
|
3
4
|
export * from "./constants.js";
|
|
5
|
+
export * from "./coverage-map.js";
|
|
4
6
|
export * from "./decomposed-refs.js";
|
|
5
7
|
export * from "./delivery-evidence.js";
|
|
6
8
|
export * from "./demote.js";
|
|
7
9
|
export * from "./effort-activate-gate.js";
|
|
8
10
|
export * from "./main.js";
|
|
9
11
|
export * from "./open-umbrella-warning.js";
|
|
12
|
+
export * from "./parent-lineage.js";
|
|
10
13
|
export * from "./project-context.js";
|
|
11
14
|
export * from "./promote-from-issue.js";
|
|
12
15
|
export * from "./promote-path.js";
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent-lineage gates for story-ready and pre-PR/preflight (#3241 / epic #3237).
|
|
3
|
+
*
|
|
4
|
+
* After #3238 coverage artifacts exist, a green child can still contradict parent
|
|
5
|
+
* invariants unless build/review gates re-check structure. This module:
|
|
6
|
+
* - resolves parent via plan.planRef (or plan references)
|
|
7
|
+
* - when parent authors requirement IDs, requires child coverage artifacts
|
|
8
|
+
* - reuses validateCoverageMap (structure-only; no LLM judgment)
|
|
9
|
+
* - classifies defects: child_spec | parent_child_drift
|
|
10
|
+
*
|
|
11
|
+
* Backward compatible: no parent link, or parent with zero requirement IDs → N/A pass.
|
|
12
|
+
*/
|
|
13
|
+
import { type CoverageReport } from "./coverage-map.js";
|
|
14
|
+
export declare const PARENT_LINEAGE_SCHEMA: "deft.scope.parent_lineage.v1";
|
|
15
|
+
/** Defect classes for gate output (#3241 AC: distinguish child-spec vs parent/child drift). */
|
|
16
|
+
export declare const LINEAGE_DEFECT_CLASSES: readonly ["child_spec", "parent_child_drift"];
|
|
17
|
+
export type LineageDefectClass = (typeof LINEAGE_DEFECT_CLASSES)[number];
|
|
18
|
+
export interface ParentLineageResult {
|
|
19
|
+
readonly ok: boolean;
|
|
20
|
+
/** False when lineage does not apply (no parent / parent has no requirement IDs). */
|
|
21
|
+
readonly applicable: boolean;
|
|
22
|
+
readonly defect_class: LineageDefectClass | null;
|
|
23
|
+
readonly message: string;
|
|
24
|
+
readonly parent_path: string | null;
|
|
25
|
+
readonly parent_requirement_ids: readonly string[];
|
|
26
|
+
readonly negative_invariant_ids: readonly string[];
|
|
27
|
+
readonly coverage_report: CoverageReport | null;
|
|
28
|
+
readonly errors: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
type JsonObj = Record<string, unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Walk up from a child artifact path to find the lifecycle root (xbrief/ or vbrief/).
|
|
33
|
+
*/
|
|
34
|
+
export declare function findLifecycleRootFromArtifact(artifactPath: string): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Resolve plan.planRef (or similar relative ref) against a lifecycle root.
|
|
37
|
+
* Rejects absolute paths and `..` traversal.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveParentPathFromRef(planRef: string, lifecycleRoot: string): {
|
|
40
|
+
path: string | null;
|
|
41
|
+
error: string | null;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Extract coverage draft surface from a child xBRIEF for validateCoverageMap.
|
|
45
|
+
* Prefer plan.metadata.parent_lineage, then plan.metadata, then plan/root.
|
|
46
|
+
*/
|
|
47
|
+
export declare function extractChildCoverageDraft(child: unknown): {
|
|
48
|
+
draft: JsonObj | null;
|
|
49
|
+
source: string | null;
|
|
50
|
+
hasCoverageMapKey: boolean;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Build the parent_lineage metadata block stamped onto decomposed children (#3241).
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildParentLineageArtifact(opts: {
|
|
56
|
+
coverage_map?: unknown;
|
|
57
|
+
behavioral_deltas?: unknown;
|
|
58
|
+
parent_requirement_ids?: readonly string[];
|
|
59
|
+
negative_invariant_ids?: readonly string[];
|
|
60
|
+
/** Stable parent plan.id for lifecycle-fallback identity (exact match). */
|
|
61
|
+
parent_plan_id?: string;
|
|
62
|
+
}): JsonObj;
|
|
63
|
+
/** Read plan.id from a loaded parent document (plan.id or root id). */
|
|
64
|
+
export declare function extractPlanId(doc: unknown): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Scan lifecycle folders for artifacts whose plan.id exactly equals parentPlanId.
|
|
67
|
+
* Returns every match (caller enforces uniqueness). Does not use basenames.
|
|
68
|
+
*/
|
|
69
|
+
export declare function findParentsByPlanId(lifecycleRoot: string, parentPlanId: string): Array<{
|
|
70
|
+
path: string;
|
|
71
|
+
data: JsonObj;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Load parent for lineage checks.
|
|
75
|
+
*
|
|
76
|
+
* 1. Prefer the exact planRef path when it exists; require plan.id match when
|
|
77
|
+
* the child stamps parent_plan_id (path replacement with a different plan is rejected).
|
|
78
|
+
* 2. When the exact path is missing AND the child stamps parent_plan_id: recover by
|
|
79
|
+
* scanning lifecycle folders for plan.id === parent_plan_id and accepting ONLY
|
|
80
|
+
* when exactly one artifact matches. This handles lifecycle moves when child
|
|
81
|
+
* planRef rewrite lagged — not silent basename recovery of unrelated files.
|
|
82
|
+
* 3. Without a durable stamp, stale/missing planRef fails closed (no basename guess).
|
|
83
|
+
*/
|
|
84
|
+
export declare function loadParentExact(parentPath: string, opts?: {
|
|
85
|
+
expectedParentPlanId?: string | null;
|
|
86
|
+
lifecycleRoot?: string | null;
|
|
87
|
+
}): {
|
|
88
|
+
ok: true;
|
|
89
|
+
data: JsonObj;
|
|
90
|
+
path: string;
|
|
91
|
+
} | {
|
|
92
|
+
ok: false;
|
|
93
|
+
error: string;
|
|
94
|
+
path: string;
|
|
95
|
+
};
|
|
96
|
+
/** Stamped parent_plan_id from child plan.metadata.parent_lineage (if any). */
|
|
97
|
+
export declare function extractStampedParentPlanId(child: unknown): string | null;
|
|
98
|
+
/**
|
|
99
|
+
* Pure parent-lineage evaluation for an in-memory child + optional parent.
|
|
100
|
+
* When parent is omitted, uses parentPath / project layout from options.
|
|
101
|
+
*/
|
|
102
|
+
export declare function evaluateParentLineage(opts: {
|
|
103
|
+
child: unknown;
|
|
104
|
+
/** Absolute path to the child artifact (used to locate lifecycle root). */
|
|
105
|
+
childPath?: string;
|
|
106
|
+
/** Pre-loaded parent document (skips disk load). */
|
|
107
|
+
parent?: unknown;
|
|
108
|
+
/** Explicit absolute parent path (also used for messaging). */
|
|
109
|
+
parentPath?: string;
|
|
110
|
+
/** Project root for resolving planRef when childPath is absent. */
|
|
111
|
+
projectRoot?: string;
|
|
112
|
+
/** When true, skip lineage (tests / opt-out). Default false. */
|
|
113
|
+
skip?: boolean;
|
|
114
|
+
}): ParentLineageResult;
|
|
115
|
+
/**
|
|
116
|
+
* Convenience: load child from path and evaluate lineage.
|
|
117
|
+
*/
|
|
118
|
+
export declare function evaluateParentLineageAtPath(childPath: string, options?: {
|
|
119
|
+
projectRoot?: string;
|
|
120
|
+
skip?: boolean;
|
|
121
|
+
}): ParentLineageResult;
|
|
122
|
+
/** One-line machine-readable lineage summary for gate stdout. */
|
|
123
|
+
export declare function formatParentLineageLine(result: ParentLineageResult): string;
|
|
124
|
+
export {};
|
|
125
|
+
//# sourceMappingURL=parent-lineage.d.ts.map
|