@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.
Files changed (102) hide show
  1. package/dist/authz/actions.d.ts +6 -0
  2. package/dist/authz/actions.js +3 -0
  3. package/dist/authz/classify.js +157 -23
  4. package/dist/authz/decompose-apply.d.ts +67 -0
  5. package/dist/authz/decompose-apply.js +302 -0
  6. package/dist/authz/index.d.ts +3 -1
  7. package/dist/authz/index.js +3 -1
  8. package/dist/authz/store.d.ts +53 -0
  9. package/dist/authz/store.js +219 -1
  10. package/dist/authz/types.d.ts +19 -2
  11. package/dist/authz/types.js +5 -1
  12. package/dist/doctor/checks.d.ts +44 -0
  13. package/dist/doctor/checks.js +428 -2
  14. package/dist/doctor/main.d.ts +6 -7
  15. package/dist/doctor/main.js +72 -79
  16. package/dist/doctor/which.d.ts +20 -1
  17. package/dist/doctor/which.js +67 -1
  18. package/dist/finish-loop/pr-finish-loop.d.ts +13 -2
  19. package/dist/finish-loop/pr-finish-loop.js +116 -5
  20. package/dist/finish-loop/types.d.ts +1 -1
  21. package/dist/hooks/dispatcher.d.ts +9 -5
  22. package/dist/hooks/dispatcher.js +23 -15
  23. package/dist/hooks/readonly.d.ts +6 -3
  24. package/dist/hooks/readonly.js +44 -25
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +1 -0
  27. package/dist/init-deposit/agent-hooks.d.ts +20 -0
  28. package/dist/init-deposit/agent-hooks.js +51 -31
  29. package/dist/intake/issue-ingest.js +29 -0
  30. package/dist/lifecycle/completed-consistency.d.ts +60 -0
  31. package/dist/lifecycle/completed-consistency.js +443 -0
  32. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +47 -0
  33. package/dist/lifecycle/completed-tracked-on-delivery.js +415 -0
  34. package/dist/lifecycle/index.d.ts +2 -0
  35. package/dist/lifecycle/index.js +2 -0
  36. package/dist/literal-acceptance/capture.d.ts +52 -0
  37. package/dist/literal-acceptance/capture.js +658 -0
  38. package/dist/literal-acceptance/evaluate.d.ts +47 -0
  39. package/dist/literal-acceptance/evaluate.js +193 -0
  40. package/dist/literal-acceptance/index.d.ts +12 -0
  41. package/dist/literal-acceptance/index.js +12 -0
  42. package/dist/literal-acceptance/run.d.ts +38 -0
  43. package/dist/literal-acceptance/run.js +216 -0
  44. package/dist/literal-acceptance/safety.d.ts +24 -0
  45. package/dist/literal-acceptance/safety.js +251 -0
  46. package/dist/literal-acceptance/types.d.ts +82 -0
  47. package/dist/literal-acceptance/types.js +26 -0
  48. package/dist/policy/ceremony-dial.d.ts +67 -11
  49. package/dist/policy/ceremony-dial.js +174 -23
  50. package/dist/policy/index.d.ts +1 -0
  51. package/dist/policy/index.js +1 -0
  52. package/dist/policy/merge-approval-head.d.ts +129 -0
  53. package/dist/policy/merge-approval-head.js +461 -0
  54. package/dist/policy/require-human-merge.d.ts +5 -0
  55. package/dist/policy/require-human-merge.js +5 -0
  56. package/dist/pr-merge-readiness/ci-gate.d.ts +20 -3
  57. package/dist/pr-merge-readiness/ci-gate.js +65 -5
  58. package/dist/pr-merge-readiness/compute.d.ts +13 -0
  59. package/dist/pr-merge-readiness/compute.js +99 -14
  60. package/dist/pr-merge-readiness/gh.d.ts +49 -0
  61. package/dist/pr-merge-readiness/gh.js +268 -0
  62. package/dist/pr-merge-readiness/index.d.ts +2 -2
  63. package/dist/pr-merge-readiness/index.js +2 -2
  64. package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +4 -0
  65. package/dist/pr-wait-mergeable/cascade.d.ts +11 -0
  66. package/dist/pr-wait-mergeable/cascade.js +115 -1
  67. package/dist/pr-wait-mergeable/main.d.ts +4 -0
  68. package/dist/pr-wait-mergeable/main.js +4 -0
  69. package/dist/pr-wait-mergeable/types.d.ts +3 -1
  70. package/dist/pr-wait-mergeable/wrappers.d.ts +6 -1
  71. package/dist/pr-wait-mergeable/wrappers.js +7 -1
  72. package/dist/preflight/evaluate.d.ts +15 -1
  73. package/dist/preflight/evaluate.js +33 -2
  74. package/dist/render/framework-commands.js +4 -0
  75. package/dist/scope/acceptance-evidence.d.ts +76 -0
  76. package/dist/scope/acceptance-evidence.js +348 -0
  77. package/dist/scope/coverage-map.d.ts +98 -0
  78. package/dist/scope/coverage-map.js +558 -0
  79. package/dist/scope/decompose.js +125 -11
  80. package/dist/scope/index.d.ts +3 -0
  81. package/dist/scope/index.js +3 -0
  82. package/dist/scope/parent-lineage.d.ts +125 -0
  83. package/dist/scope/parent-lineage.js +626 -0
  84. package/dist/scope/transition.d.ts +8 -0
  85. package/dist/scope/transition.js +61 -2
  86. package/dist/session/active-cli.d.ts +79 -0
  87. package/dist/session/active-cli.js +382 -0
  88. package/dist/session/effort-budget.d.ts +130 -0
  89. package/dist/session/effort-budget.js +334 -0
  90. package/dist/session/index.d.ts +2 -0
  91. package/dist/session/index.js +2 -0
  92. package/dist/session/session-start.d.ts +6 -0
  93. package/dist/session/session-start.js +53 -2
  94. package/dist/session/verify-session-ritual.d.ts +14 -0
  95. package/dist/session/verify-session-ritual.js +33 -0
  96. package/dist/story-ready/evaluate.d.ts +11 -0
  97. package/dist/story-ready/evaluate.js +41 -3
  98. package/dist/triage/actions/candidates-log.d.ts +1 -2
  99. package/dist/triage/actions/candidates-log.js +37 -6
  100. package/dist/xbrief-migrate/migrate-project.d.ts +4 -0
  101. package/dist/xbrief-migrate/migrate-project.js +92 -3
  102. package/package.json +7 -3
@@ -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
- const draft = loadJson(draftPath);
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
- for (const { target } of childPaths) {
877
- mkdirSync(dirname(target), { recursive: true });
878
- }
879
- for (let i = 0; i < childPaths.length; i += 1) {
880
- // biome-ignore lint/style/noNonNullAssertion: loop bound ensures these exist
881
- writeJson(projectRoot, childPaths[i].target, childDocs[i]);
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
- writeJson(projectRoot, parentPath, parent);
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
  }
@@ -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";
@@ -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