@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.
- package/dist/branch/evaluate.js +2 -1
- package/dist/check/orchestrator.d.ts +3 -0
- package/dist/check/orchestrator.js +2 -1
- package/dist/codebase/map.js +2 -0
- package/dist/deposit/copy-tree.js +31 -7
- package/dist/doctor/checks.d.ts +9 -0
- package/dist/doctor/checks.js +67 -0
- package/dist/doctor/main.js +2 -1
- package/dist/fs/projection-containment.d.ts +35 -0
- package/dist/fs/projection-containment.js +118 -0
- package/dist/init-deposit/hygiene.d.ts +6 -0
- package/dist/init-deposit/hygiene.js +5 -1
- package/dist/init-deposit/refresh.d.ts +16 -2
- package/dist/init-deposit/refresh.js +144 -50
- package/dist/init-deposit/scaffold.d.ts +21 -0
- package/dist/init-deposit/scaffold.js +84 -10
- package/dist/policy/disclosure.d.ts +1 -0
- package/dist/policy/disclosure.js +1 -0
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +1 -0
- package/dist/policy/policy-invocation.d.ts +5 -0
- package/dist/policy/policy-invocation.js +9 -0
- package/dist/policy/resolve.js +4 -2
- package/dist/policy/value-feedback.js +6 -3
- package/dist/pr-merge-readiness/constants.d.ts +4 -0
- package/dist/pr-merge-readiness/constants.js +4 -0
- package/dist/pr-merge-readiness/evaluate.js +3 -0
- package/dist/pr-merge-readiness/mergeability.d.ts +8 -6
- package/dist/pr-merge-readiness/mergeability.js +15 -10
- package/dist/pr-merge-readiness/output.js +12 -6
- package/dist/pr-merge-readiness/parse.d.ts +1 -0
- package/dist/pr-merge-readiness/parse.js +9 -1
- package/dist/pr-merge-readiness/types.d.ts +2 -0
- package/dist/preflight-cache/evaluate.d.ts +2 -0
- package/dist/preflight-cache/evaluate.js +14 -3
- package/dist/preflight-cache/index.d.ts +1 -1
- package/dist/preflight-cache/index.js +1 -1
- package/dist/release/constants.d.ts +2 -0
- package/dist/release/constants.js +2 -0
- package/dist/release/preflight.d.ts +2 -0
- package/dist/release/preflight.js +14 -1
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/main.js +24 -1
- package/dist/scope/open-umbrella-warning.d.ts +12 -0
- package/dist/scope/open-umbrella-warning.js +403 -0
- package/dist/swarm/constants.d.ts +1 -1
- package/dist/swarm/constants.js +2 -1
- package/dist/swarm/subagent-backend.js +2 -1
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +69 -58
- package/dist/ts-check-lane/run-lane.d.ts +4 -0
- package/dist/ts-check-lane/run-lane.js +20 -6
- package/dist/value/feedback-file.js +3 -2
- package/dist/value/readback.js +3 -2
- package/dist/vbrief-reconcile/umbrellas.js +12 -11
- package/dist/xbrief-migrate/migrate-project.js +9 -0
- 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
|
|
69
|
-
*
|
|
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
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
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
|
-
//
|
|
87
|
-
if (
|
|
88
|
+
// Excluded-author skip is an intentional N/A reviewer state (#2375).
|
|
89
|
+
if (verdict.excludedAuthor) {
|
|
88
90
|
return true;
|
|
89
91
|
}
|
|
90
|
-
//
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`
|
|
55
|
-
|
|
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
|
-
`
|
|
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
|
|
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
|
}
|
package/dist/scope/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/scope/index.js
CHANGED
|
@@ -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";
|
package/dist/scope/main.js
CHANGED
|
@@ -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
|