@deftai/directive-core 0.73.0 → 0.74.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 (58) hide show
  1. package/dist/branch/evaluate.js +2 -1
  2. package/dist/check/orchestrator.d.ts +3 -0
  3. package/dist/check/orchestrator.js +2 -1
  4. package/dist/codebase/map.js +2 -0
  5. package/dist/deposit/copy-tree.js +31 -7
  6. package/dist/doctor/checks.d.ts +9 -0
  7. package/dist/doctor/checks.js +67 -0
  8. package/dist/doctor/main.js +2 -1
  9. package/dist/fs/projection-containment.d.ts +35 -0
  10. package/dist/fs/projection-containment.js +118 -0
  11. package/dist/init-deposit/hygiene.d.ts +6 -0
  12. package/dist/init-deposit/hygiene.js +5 -1
  13. package/dist/init-deposit/refresh.d.ts +16 -2
  14. package/dist/init-deposit/refresh.js +144 -50
  15. package/dist/init-deposit/scaffold.d.ts +21 -0
  16. package/dist/init-deposit/scaffold.js +84 -10
  17. package/dist/policy/disclosure.d.ts +1 -0
  18. package/dist/policy/disclosure.js +1 -0
  19. package/dist/policy/index.d.ts +1 -0
  20. package/dist/policy/index.js +1 -0
  21. package/dist/policy/policy-invocation.d.ts +5 -0
  22. package/dist/policy/policy-invocation.js +9 -0
  23. package/dist/policy/resolve.js +4 -2
  24. package/dist/policy/value-feedback.js +6 -3
  25. package/dist/pr-merge-readiness/constants.d.ts +4 -0
  26. package/dist/pr-merge-readiness/constants.js +4 -0
  27. package/dist/pr-merge-readiness/evaluate.js +3 -0
  28. package/dist/pr-merge-readiness/mergeability.d.ts +8 -6
  29. package/dist/pr-merge-readiness/mergeability.js +15 -10
  30. package/dist/pr-merge-readiness/output.js +12 -6
  31. package/dist/pr-merge-readiness/parse.d.ts +1 -0
  32. package/dist/pr-merge-readiness/parse.js +9 -1
  33. package/dist/pr-merge-readiness/types.d.ts +2 -0
  34. package/dist/preflight-cache/evaluate.d.ts +2 -0
  35. package/dist/preflight-cache/evaluate.js +14 -3
  36. package/dist/preflight-cache/index.d.ts +1 -1
  37. package/dist/preflight-cache/index.js +1 -1
  38. package/dist/release/constants.d.ts +2 -0
  39. package/dist/release/constants.js +2 -0
  40. package/dist/release/preflight.d.ts +2 -0
  41. package/dist/release/preflight.js +14 -1
  42. package/dist/scope/index.d.ts +1 -0
  43. package/dist/scope/index.js +1 -0
  44. package/dist/scope/main.js +24 -1
  45. package/dist/scope/open-umbrella-warning.d.ts +12 -0
  46. package/dist/scope/open-umbrella-warning.js +403 -0
  47. package/dist/swarm/constants.d.ts +1 -1
  48. package/dist/swarm/constants.js +2 -1
  49. package/dist/swarm/subagent-backend.js +2 -1
  50. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  51. package/dist/triage/bootstrap/gitignore.js +69 -58
  52. package/dist/ts-check-lane/run-lane.d.ts +4 -0
  53. package/dist/ts-check-lane/run-lane.js +20 -6
  54. package/dist/value/feedback-file.js +3 -2
  55. package/dist/value/readback.js +3 -2
  56. package/dist/vbrief-reconcile/umbrellas.js +12 -11
  57. package/dist/xbrief-migrate/migrate-project.js +9 -0
  58. package/package.json +3 -3
@@ -65,13 +65,15 @@ export function verdictShaIsStale(verdict, headSha) {
65
65
  * Classify whether the verdict-based merge block is ONLY "soft" (#2260).
66
66
  *
67
67
  * A soft block means the review verdict is absent or pinned to a prior head
68
- * SHA (rebased staleness) -- i.e. the review has not spoken about the CURRENT
69
- * head. It is safe to reconcile a soft block against GitHub mergeability.
68
+ * SHA without carrying real blocker-class findings (rebased staleness with no
69
+ * P0/P1/errored/low-confidence) -- i.e. the review has not spoken about the
70
+ * CURRENT head in a blocking way. It is safe to reconcile a soft block against
71
+ * GitHub mergeability.
70
72
  *
71
- * A HARD block -- a genuine P0/P1 finding, an ERRORED review, or a low
72
- * confidence score on the current head -- is NEVER soft; those must keep
73
- * blocking regardless of GitHub mergeability (guardrail: do not merge a PR
74
- * with a real P0/P1 review finding).
73
+ * A HARD block -- a genuine P0/P1 finding (even on a stale SHA), an ERRORED
74
+ * review, or a low confidence score -- is NEVER soft; those must keep blocking
75
+ * regardless of GitHub mergeability (guardrail: do not merge a PR with a real
76
+ * P0/P1 review finding).
75
77
  */
76
78
  export function verdictBlockIsSoftOnly(verdict, headSha) {
77
79
  // Absent: no Greptile rolling-summary comment at all.
@@ -83,12 +85,11 @@ export function verdictBlockIsSoftOnly(verdict, headSha) {
83
85
  if (verdict.informalClean) {
84
86
  return false;
85
87
  }
86
- // Stale: verdict pinned to a prior head SHA -> its findings pertain to old code.
87
- if (verdictShaIsStale(verdict, headSha)) {
88
+ // Excluded-author skip is an intentional N/A reviewer state (#2375).
89
+ if (verdict.excludedAuthor) {
88
90
  return true;
89
91
  }
90
- // Verdict is current (or its SHA is unparseable). A genuine current-head
91
- // finding is a hard block and must not be overridden.
92
+ // Blocker-class signals apply even when the verdict SHA is stale (#2382).
92
93
  if (verdict.errored) {
93
94
  return false;
94
95
  }
@@ -98,6 +99,10 @@ export function verdictBlockIsSoftOnly(verdict, headSha) {
98
99
  if (verdict.p0Count > 0 || verdict.p1Count > 0) {
99
100
  return false;
100
101
  }
102
+ // Stale without blocker findings: review has not spoken about the current head.
103
+ if (verdictShaIsStale(verdict, headSha)) {
104
+ return true;
105
+ }
101
106
  // No genuine finding: the block is a missing-canonical-field / not-yet-posted
102
107
  // wait, which GitHub mergeability may resolve.
103
108
  return true;
@@ -10,6 +10,7 @@ function verdictToDict(verdict) {
10
10
  p1_count: verdict.p1Count,
11
11
  p2_count: verdict.p2Count,
12
12
  informal_clean: verdict.informalClean,
13
+ excluded_author: verdict.excludedAuthor,
13
14
  raw_body_excerpt: verdict.rawBodyExcerpt,
14
15
  };
15
16
  }
@@ -47,12 +48,17 @@ export function printHuman(result) {
47
48
  const lines = [];
48
49
  lines.push(`PR #${result.prNumber} merge-readiness check (via=${result.via})`);
49
50
  lines.push(` HEAD SHA: ${result.headSha ?? "<unknown>"}`);
50
- lines.push(` Greptile reviewed: ${result.verdict.lastReviewedSha ?? "<not parsed>"}`);
51
- const confidenceStr = result.verdict.confidence !== null ? String(result.verdict.confidence) : "<not parsed>";
52
- lines.push(` Confidence: ${confidenceStr}/5`);
53
- lines.push(` Findings: P0=${result.verdict.p0Count} ` +
54
- `P1=${result.verdict.p1Count} P2=${result.verdict.p2Count}`);
55
- lines.push(` Errored sentinel: ${result.verdict.errored ? "True" : "False"}`);
51
+ if (result.verdict.excludedAuthor) {
52
+ lines.push(" Greptile review: skipped (author excluded)");
53
+ }
54
+ else {
55
+ lines.push(` Greptile reviewed: ${result.verdict.lastReviewedSha ?? "<not parsed>"}`);
56
+ const confidenceStr = result.verdict.confidence !== null ? String(result.verdict.confidence) : "<not parsed>";
57
+ lines.push(` Confidence: ${confidenceStr}/5`);
58
+ lines.push(` Findings: P0=${result.verdict.p0Count} ` +
59
+ `P1=${result.verdict.p1Count} P2=${result.verdict.p2Count}`);
60
+ lines.push(` Errored sentinel: ${result.verdict.errored ? "True" : "False"}`);
61
+ }
56
62
  const ciBlock = result.partialData.ci;
57
63
  if (ciBlock !== null && typeof ciBlock === "object" && !Array.isArray(ciBlock)) {
58
64
  const ci = ciBlock;
@@ -1,5 +1,6 @@
1
1
  import type { GreptileVerdict } from "./types.js";
2
2
  export declare function emptyVerdict(): GreptileVerdict;
3
+ export declare function isGreptileExcludedAuthor(body: string): boolean;
3
4
  export declare function isInformalCleanMissingCanonicalFields(verdict: GreptileVerdict, body: string): boolean;
4
5
  /** Parse a Greptile rolling-summary comment body into a structured verdict. */
5
6
  export declare function parseGreptileBody(body: string): GreptileVerdict;
@@ -1,5 +1,5 @@
1
1
  import { findLastReviewedCommitSha } from "../text/redos-safe.js";
2
- import { CONFIDENCE_RE, GREPTILE_ERRORED_SENTINEL, INFORMAL_CLEAN_SIGNAL_RE, P0_BADGE, P1_BADGE, SECTION_RE, } from "./constants.js";
2
+ import { CONFIDENCE_RE, GREPTILE_ERRORED_SENTINEL, GREPTILE_EXCLUDED_AUTHOR_PHRASE, GREPTILE_STATUS_MARKER, INFORMAL_CLEAN_SIGNAL_RE, P0_BADGE, P1_BADGE, SECTION_RE, } from "./constants.js";
3
3
  export function emptyVerdict() {
4
4
  return {
5
5
  found: false,
@@ -10,9 +10,15 @@ export function emptyVerdict() {
10
10
  p1Count: 0,
11
11
  p2Count: 0,
12
12
  informalClean: false,
13
+ excludedAuthor: false,
13
14
  rawBodyExcerpt: "",
14
15
  };
15
16
  }
17
+ export function isGreptileExcludedAuthor(body) {
18
+ const lower = body.toLowerCase();
19
+ return (lower.includes(GREPTILE_EXCLUDED_AUTHOR_PHRASE) &&
20
+ (lower.includes("greptile-status") || body.includes(GREPTILE_STATUS_MARKER)));
21
+ }
16
22
  export function isInformalCleanMissingCanonicalFields(verdict, body) {
17
23
  if (!verdict.found || verdict.errored) {
18
24
  return false;
@@ -42,6 +48,7 @@ export function parseGreptileBody(body) {
42
48
  return emptyVerdict();
43
49
  }
44
50
  const errored = body.trim().startsWith(GREPTILE_ERRORED_SENTINEL);
51
+ const excludedAuthor = isGreptileExcludedAuthor(body);
45
52
  const lastReviewedSha = findLastReviewedCommitSha(body);
46
53
  const confMatch = CONFIDENCE_RE.exec(body);
47
54
  const confidence = confMatch?.groups?.score !== undefined ? Number(confMatch.groups.score) : null;
@@ -73,6 +80,7 @@ export function parseGreptileBody(body) {
73
80
  p1Count,
74
81
  p2Count,
75
82
  informalClean: false,
83
+ excludedAuthor,
76
84
  rawBodyExcerpt: body.slice(0, 200),
77
85
  };
78
86
  if (isInformalCleanMissingCanonicalFields(verdict, body)) {
@@ -7,6 +7,8 @@ export interface GreptileVerdict {
7
7
  readonly p1Count: number;
8
8
  readonly p2Count: number;
9
9
  readonly informalClean: boolean;
10
+ /** Greptile deliberately skipped review for an excluded PR author (#2375). */
11
+ readonly excludedAuthor: boolean;
10
12
  readonly rawBodyExcerpt: string;
11
13
  }
12
14
  export interface GateResult {
@@ -17,6 +17,8 @@ export declare const CANDIDATES_RELPATH: string;
17
17
  export declare const DEFAULT_MAX_AGE_HOURS = 24;
18
18
  export declare const ENV_MAX_AGE_HOURS = "DEFT_CACHE_MAX_AGE_HOURS";
19
19
  export declare const ENV_TRIAGE_REPO = "DEFT_TRIAGE_REPO";
20
+ /** Set by releaseSubprocessEnv() during task release Step 5 (#2386). */
21
+ export declare const ENV_RELEASE_PREFLIGHT = "DEFT_RELEASE_PREFLIGHT";
20
22
  export declare const REQUIRED_DECISION = "accept";
21
23
  export interface GateResult {
22
24
  readonly code: number;
@@ -27,7 +27,14 @@ export const CANDIDATES_RELPATH = join("xbrief", ".triage-cache", "candidates.js
27
27
  export const DEFAULT_MAX_AGE_HOURS = 24;
28
28
  export const ENV_MAX_AGE_HOURS = "DEFT_CACHE_MAX_AGE_HOURS";
29
29
  export const ENV_TRIAGE_REPO = "DEFT_TRIAGE_REPO";
30
+ /** Set by releaseSubprocessEnv() during task release Step 5 (#2386). */
31
+ export const ENV_RELEASE_PREFLIGHT = "DEFT_RELEASE_PREFLIGHT";
30
32
  export const REQUIRED_DECISION = "accept";
33
+ const TRUTHY = new Set(["1", "true", "yes", "on"]);
34
+ function releasePreflightBypassActive() {
35
+ const raw = process.env[ENV_RELEASE_PREFLIGHT] ?? "";
36
+ return TRUTHY.has(raw.trim().toLowerCase());
37
+ }
31
38
  // ---------------------------------------------------------------------------
32
39
  // Repo discovery helpers
33
40
  // ---------------------------------------------------------------------------
@@ -327,6 +334,7 @@ const REMEDIATION_NO_CANDIDATES = [
327
334
  export function evaluate(projectRoot, options = {}) {
328
335
  const source = options.source ?? DEFAULT_SOURCE;
329
336
  const allowStale = options.allowStale ?? false;
337
+ const releasePreflightStaleBypass = releasePreflightBypassActive();
330
338
  const allowMissingBootstrap = options.allowMissingBootstrap ?? false;
331
339
  const nowFn = options.nowFn ?? (() => new Date());
332
340
  const envMaxAge = process.env[ENV_MAX_AGE_HOURS];
@@ -423,11 +431,14 @@ export function evaluate(projectRoot, options = {}) {
423
431
  // empty-cache case (no entries at all) preserves its prior staleness result.
424
432
  const allCandidatesClosed = minFetchedAt === null && rawCandidatePaths.length > 0;
425
433
  const stale = allCandidatesClosed ? false : ageH > maxAgeHours;
426
- // Step 5b: --allow-stale bypass
427
- if (stale && allowStale) {
434
+ // Step 5b: --allow-stale bypass (or release Step-5 age bypass only, #2386)
435
+ if (stale && (allowStale || releasePreflightStaleBypass)) {
436
+ const bypassReason = options.allowStale
437
+ ? "--allow-stale is set"
438
+ : `${ENV_RELEASE_PREFLIGHT} is set (release pre-flight)`;
428
439
  const warning = [
429
440
  `⚠ deft cache-fresh: cache is ${ageH.toFixed(1)}h old (max-age=${maxAgeHours}h) but`,
430
- ` --allow-stale is set -- proceeding with audit-trail warning.`,
441
+ ` ${bypassReason} -- proceeding with audit-trail warning.`,
431
442
  ].join("\n");
432
443
  if (options.forIssue !== undefined && options.forIssue !== null) {
433
444
  const forResult = evaluateForIssue(resolvedRepo, options.forIssue, candidatesPath, cacheRoot, source, scopeRules, projectRoot);
@@ -1,3 +1,3 @@
1
1
  export type { EvaluateOptions, GateResult } from "./evaluate.js";
2
- export { CACHE_DIR_NAME, CANDIDATES_RELPATH, DEFAULT_MAX_AGE_HOURS, DEFAULT_SOURCE, ENV_MAX_AGE_HOURS, ENV_TRIAGE_REPO, evaluate, REQUIRED_DECISION, } from "./evaluate.js";
2
+ export { CACHE_DIR_NAME, CANDIDATES_RELPATH, DEFAULT_MAX_AGE_HOURS, DEFAULT_SOURCE, ENV_MAX_AGE_HOURS, ENV_RELEASE_PREFLIGHT, ENV_TRIAGE_REPO, evaluate, REQUIRED_DECISION, } from "./evaluate.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- export { CACHE_DIR_NAME, CANDIDATES_RELPATH, DEFAULT_MAX_AGE_HOURS, DEFAULT_SOURCE, ENV_MAX_AGE_HOURS, ENV_TRIAGE_REPO, evaluate, REQUIRED_DECISION, } from "./evaluate.js";
1
+ export { CACHE_DIR_NAME, CANDIDATES_RELPATH, DEFAULT_MAX_AGE_HOURS, DEFAULT_SOURCE, ENV_MAX_AGE_HOURS, ENV_RELEASE_PREFLIGHT, ENV_TRIAGE_REPO, evaluate, REQUIRED_DECISION, } from "./evaluate.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -16,6 +16,8 @@ export declare const VERIFY_DRAFT_INTERVAL_SECONDS = 1;
16
16
  export declare const RELEASE_ARTIFACTS: readonly ["CHANGELOG.md", "ROADMAP.md"];
17
17
  export declare const BRANCH_GATE_BYPASS_ENV = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
18
18
  export declare const DESTRUCTIVE_GH_GATE_BYPASS_ENV = "DEFT_ALLOW_DESTRUCTIVE_GH_VERBS";
19
+ /** Set by releaseSubprocessEnv() so Step-5 pre-flight skips triage-cache staleness (#2386). */
20
+ export declare const RELEASE_PREFLIGHT_ENV = "DEFT_RELEASE_PREFLIGHT";
19
21
  export declare const PYPROJECT_VERSION_LINE_RE: RegExp;
20
22
  /** Byte-identical argparse --help from scripts/release.py (Python 3.12). */
21
23
  export declare const RELEASE_HELP: string;
@@ -24,6 +24,8 @@ export const VERIFY_DRAFT_INTERVAL_SECONDS = 1.0;
24
24
  export const RELEASE_ARTIFACTS = ["CHANGELOG.md", "ROADMAP.md"];
25
25
  export const BRANCH_GATE_BYPASS_ENV = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
26
26
  export const DESTRUCTIVE_GH_GATE_BYPASS_ENV = "DEFT_ALLOW_DESTRUCTIVE_GH_VERBS";
27
+ /** Set by releaseSubprocessEnv() so Step-5 pre-flight skips triage-cache staleness (#2386). */
28
+ export const RELEASE_PREFLIGHT_ENV = "DEFT_RELEASE_PREFLIGHT";
27
29
  export const PYPROJECT_VERSION_LINE_RE = /version\s*=\s*"[^"]*"/;
28
30
  /** Byte-identical argparse --help from scripts/release.py (Python 3.12). */
29
31
  export const RELEASE_HELP = "usage: release [-h] [--dry-run] [--skip-tag] [--skip-release] [--allow-dirty]\n" +
@@ -8,6 +8,8 @@
8
8
  * dispatches `check:framework-source`.
9
9
  */
10
10
  import { type CheckOrchestratorSeams } from "../check/orchestrator.js";
11
+ /** Step-5-only env: branch bypass plus release pre-flight cache staleness tolerance (#2386). */
12
+ export declare function releaseCheckEnv(base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
11
13
  /** Seams for test isolation of the native release pre-flight. */
12
14
  export interface ReleasePreflightSeams {
13
15
  /** Override the check dispatcher (default: dispatchTaskCheck from check/orchestrator). */
@@ -8,6 +8,15 @@
8
8
  * dispatches `check:framework-source`.
9
9
  */
10
10
  import { dispatchTaskCheck } from "../check/orchestrator.js";
11
+ import { RELEASE_PREFLIGHT_ENV } from "./constants.js";
12
+ import { releaseSubprocessEnv } from "./git.js";
13
+ /** Step-5-only env: branch bypass plus release pre-flight cache staleness tolerance (#2386). */
14
+ export function releaseCheckEnv(base = process.env) {
15
+ return {
16
+ ...releaseSubprocessEnv(base),
17
+ [RELEASE_PREFLIGHT_ENV]: "1",
18
+ };
19
+ }
11
20
  /**
12
21
  * Run the native TypeScript `task check` as the release pre-flight.
13
22
  *
@@ -18,7 +27,11 @@ import { dispatchTaskCheck } from "../check/orchestrator.js";
18
27
  */
19
28
  export function runReleaseCheck(projectRoot, seams = {}) {
20
29
  const dispatch = seams.dispatchCheck ?? dispatchTaskCheck;
21
- const code = dispatch(projectRoot, projectRoot, seams.checkSeams);
30
+ const checkSeams = {
31
+ ...seams.checkSeams,
32
+ env: releaseCheckEnv(seams.checkSeams?.env ?? process.env),
33
+ };
34
+ const code = dispatch(projectRoot, projectRoot, checkSeams);
22
35
  if (code === 0) {
23
36
  return [true, "ran native TypeScript task check"];
24
37
  }
@@ -3,6 +3,7 @@ export * from "./constants.js";
3
3
  export * from "./decomposed-refs.js";
4
4
  export * from "./demote.js";
5
5
  export * from "./main.js";
6
+ export * from "./open-umbrella-warning.js";
6
7
  export * from "./project-context.js";
7
8
  export * from "./transition.js";
8
9
  export * from "./undo.js";
@@ -3,6 +3,7 @@ export * from "./constants.js";
3
3
  export * from "./decomposed-refs.js";
4
4
  export * from "./demote.js";
5
5
  export * from "./main.js";
6
+ export * from "./open-umbrella-warning.js";
6
7
  export * from "./project-context.js";
7
8
  export * from "./transition.js";
8
9
  export * from "./undo.js";
@@ -1,8 +1,10 @@
1
1
  import { existsSync } from "node:fs";
2
- import { join } from "node:path";
2
+ import { dirname, join } from "node:path";
3
+ import { reconcileUmbrellas, renderUmbrellasReport } from "../vbrief-reconcile/umbrellas.js";
3
4
  import { canonicalLogPath, readAll } from "./audit-log.js";
4
5
  import { TRANSITIONS } from "./constants.js";
5
6
  import { batchDemote, DEFAULT_OLDER_THAN_DAYS, demoteOne, resolveDemoteFilePath, resolveFilePath, resolveProjectRootStrict, } from "./demote.js";
7
+ import { completedPathForScopeMove, findOpenUmbrellaReferences, renderOpenUmbrellaWarning, } from "./open-umbrella-warning.js";
6
8
  import { resolveProjectRoot } from "./project-context.js";
7
9
  import { recordWipCapOverride, runTransition } from "./transition.js";
8
10
  import { findByDecisionId, isAlreadyUndone, REVERSIBLE_ACTIONS, undoBatch, undoOne, } from "./undo.js";
@@ -88,6 +90,27 @@ export function lifecycleMain(argv) {
88
90
  "audit: vbrief/.eval/scope-lifecycle.jsonl entry tagged wip_cap_override (#1124).\n");
89
91
  }
90
92
  process.stdout.write(`${result.message}\n`);
93
+ if (action === "complete") {
94
+ try {
95
+ const completedPath = completedPathForScopeMove(filePath);
96
+ const rootForWarning = resolveProjectRoot(projectRoot) ?? dirname(dirname(dirname(completedPath)));
97
+ const refs = findOpenUmbrellaReferences(rootForWarning, completedPath);
98
+ const warning = renderOpenUmbrellaWarning(refs);
99
+ if (warning.length > 0) {
100
+ process.stderr.write(`${warning}\n`);
101
+ const [, outcome] = reconcileUmbrellas(rootForWarning);
102
+ // Only surface a report when reconcile actually changed something or
103
+ // recorded per-epic errors — skip vacuous setup failures (exit 2 with
104
+ // empty buckets) so a missing lifecycle root does not spam stderr.
105
+ if (outcome.changed.length > 0 || outcome.errors.length > 0) {
106
+ process.stderr.write(`${renderUmbrellasReport(outcome)}\n`);
107
+ }
108
+ }
109
+ }
110
+ catch {
111
+ /* best-effort drift warning + reconcile; lifecycle success remains authoritative */
112
+ }
113
+ }
91
114
  return 0;
92
115
  }
93
116
  process.stderr.write(`Error: ${result.message}\n`);
@@ -0,0 +1,12 @@
1
+ export interface OpenUmbrellaReference {
2
+ readonly repo: string | null;
3
+ readonly issueNumber: number | null;
4
+ readonly title: string;
5
+ readonly path: string | null;
6
+ readonly sources: readonly string[];
7
+ }
8
+ /** Find open umbrella/tracker surfaces that still mention a just-completed scope. */
9
+ export declare function findOpenUmbrellaReferences(projectRoot: string, completedScopePath: string): OpenUmbrellaReference[];
10
+ export declare function renderOpenUmbrellaWarning(refs: readonly OpenUmbrellaReference[]): string;
11
+ export declare function completedPathForScopeMove(filePath: string): string;
12
+ //# sourceMappingURL=open-umbrella-warning.d.ts.map