@deftai/directive-core 0.65.0 → 0.66.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/architecture/sor-preflight.js +6 -4
- package/dist/capacity/backfill.js +5 -3
- package/dist/capacity/show.js +5 -3
- package/dist/category-b-namespace/index.d.ts +39 -0
- package/dist/category-b-namespace/index.js +120 -0
- package/dist/codebase/default-extractor.js +2 -1
- package/dist/codebase/map.js +2 -1
- package/dist/codebase/provider.js +4 -2
- package/dist/content-contracts/standards/_helpers.js +1 -1
- package/dist/doctor/checks.js +2 -1
- package/dist/doctor/doctor-state.js +2 -2
- package/dist/encoding/patterns.js +19 -0
- package/dist/encoding/scan.js +3 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/refresh.js +2 -1
- package/dist/init-deposit/scaffold.js +2 -1
- package/dist/install-upgrade/index.d.ts +2 -0
- package/dist/install-upgrade/index.js +31 -2
- package/dist/intake/issue-emit.d.ts +1 -1
- package/dist/intake/issue-emit.js +3 -2
- package/dist/intake/issue-ingest.js +11 -8
- package/dist/intake/reconcile-issues.js +11 -4
- package/dist/layout/index.d.ts +2 -0
- package/dist/layout/index.js +2 -0
- package/dist/layout/resolve.d.ts +87 -0
- package/dist/layout/resolve.js +159 -0
- package/dist/lifecycle/lifecycle-hygiene.d.ts +1 -1
- package/dist/lifecycle/lifecycle-hygiene.js +8 -5
- package/dist/orchestration/judgment-policy.js +4 -6
- package/dist/orchestration/verify-judgment-gates.d.ts +1 -0
- package/dist/orchestration/verify-judgment-gates.js +4 -2
- package/dist/policy/autonomy.js +4 -6
- package/dist/policy/capacity.js +4 -6
- package/dist/policy/decisions.d.ts +1 -0
- package/dist/policy/decisions.js +4 -2
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +3 -1
- package/dist/policy/plan-extensions.d.ts +35 -0
- package/dist/policy/plan-extensions.js +57 -0
- package/dist/policy/resolve.d.ts +6 -2
- package/dist/policy/resolve.js +18 -8
- package/dist/policy/wip.js +6 -4
- package/dist/preflight-cache/evaluate.js +8 -5
- package/dist/release/native-steps.js +4 -3
- package/dist/render/framework-commands.js +2 -1
- package/dist/render/prd-render.js +2 -2
- package/dist/render/project-render.js +23 -5
- package/dist/render/roadmap-render.js +36 -8
- package/dist/render/scope-outlook.js +3 -2
- package/dist/scope/audit-log.js +3 -2
- package/dist/scope/capacity-stamp.js +2 -1
- package/dist/scope/decompose.js +6 -4
- package/dist/scope/decomposed-refs.js +2 -1
- package/dist/scope/demote.js +5 -4
- package/dist/scope/project-context.js +2 -1
- package/dist/scope/project-definition-sync.js +4 -3
- package/dist/scope/transition.js +6 -2
- package/dist/scope/undo.js +2 -1
- package/dist/scope/vbrief-ref.js +5 -3
- package/dist/session/resume-conditions.js +2 -1
- package/dist/session/ritual-sentinel.js +2 -1
- package/dist/session/staleness.js +2 -1
- package/dist/slice/record.js +4 -4
- package/dist/spec-authority/resolver.js +5 -5
- package/dist/swarm/complete-cohort.js +5 -4
- package/dist/swarm/launch.js +6 -7
- package/dist/swarm/readiness.js +22 -20
- package/dist/swarm/subagent-backend.js +2 -5
- package/dist/swarm/verify-review-clean.js +2 -1
- package/dist/triage/actions/candidates-log.d.ts +3 -0
- package/dist/triage/actions/candidates-log.js +8 -2
- package/dist/triage/bootstrap/gitignore.js +22 -13
- package/dist/triage/bootstrap/index.js +7 -4
- package/dist/triage/bulk/index.js +2 -1
- package/dist/triage/classify/index.js +13 -11
- package/dist/triage/queue/audit.js +3 -3
- package/dist/triage/queue/cache.js +3 -2
- package/dist/triage/queue/ranking-labels.js +2 -1
- package/dist/triage/queue/scope-walk.js +8 -6
- package/dist/triage/reconcile/audit.js +2 -1
- package/dist/triage/reconcile/parse-uri.js +2 -1
- package/dist/triage/reconcile/reconcile.js +8 -7
- package/dist/triage/refresh/extract.js +4 -2
- package/dist/triage/refresh/refresh.js +3 -2
- package/dist/triage/scope/mutations-core.js +13 -9
- package/dist/triage/scope/renderers.js +5 -3
- package/dist/triage/scope/resolve.js +6 -5
- package/dist/triage/scope/validate.js +3 -6
- package/dist/triage/scope-drift/add-ignore.js +4 -2
- package/dist/triage/scope-drift/scope-rules.js +3 -2
- package/dist/triage/subscribe/index.js +13 -7
- package/dist/triage/summary/index.js +7 -5
- package/dist/triage/summary/reconcilable.js +6 -4
- package/dist/triage/summary/scope-drift.js +2 -1
- package/dist/triage/welcome/prior-state.js +9 -9
- package/dist/triage/welcome/summary.js +9 -8
- package/dist/triage/welcome/writers.js +12 -10
- package/dist/validate-content/capacity-policy.js +5 -4
- package/dist/validate-content/capacity-show.js +5 -3
- package/dist/validate-content/filename.d.ts +1 -1
- package/dist/validate-content/filename.js +5 -4
- package/dist/validate-content/validate-strategy-output.js +14 -5
- package/dist/vbrief-build/build.js +1 -1
- package/dist/vbrief-build/constants.js +7 -0
- package/dist/vbrief-build/speckit.js +3 -2
- package/dist/vbrief-reconcile/graph.js +3 -2
- package/dist/vbrief-reconcile/labels.js +4 -3
- package/dist/vbrief-reconcile/reconciliation.js +5 -1
- package/dist/vbrief-reconcile/swarm-deps.js +6 -5
- package/dist/vbrief-reconcile/umbrellas.d.ts +1 -1
- package/dist/vbrief-reconcile/umbrellas.js +9 -7
- package/dist/vbrief-validate/conformance.d.ts +1 -2
- package/dist/vbrief-validate/conformance.js +29 -14
- package/dist/vbrief-validate/constants.d.ts +18 -1
- package/dist/vbrief-validate/constants.js +32 -3
- package/dist/vbrief-validate/epic-links.js +3 -2
- package/dist/vbrief-validate/filename.js +5 -3
- package/dist/vbrief-validate/index.d.ts +3 -1
- package/dist/vbrief-validate/index.js +2 -1
- package/dist/vbrief-validate/main.js +17 -6
- package/dist/vbrief-validate/origin.js +1 -1
- package/dist/vbrief-validate/paths.js +2 -1
- package/dist/vbrief-validate/plan-hooks.js +4 -3
- package/dist/vbrief-validate/registry-status.d.ts +1 -0
- package/dist/vbrief-validate/registry-status.js +3 -1
- package/dist/vbrief-validate/roundtrip.d.ts +36 -0
- package/dist/vbrief-validate/roundtrip.js +168 -0
- package/dist/vbrief-validate/schema.d.ts +1 -1
- package/dist/vbrief-validate/schema.js +62 -19
- package/dist/vbrief-validate/validate-all.js +12 -4
- package/dist/verify-source/code-structure-validate.js +8 -4
- package/dist/verify-source/contract-drift.d.ts +7 -3
- package/dist/verify-source/contract-drift.js +40 -22
- package/dist/xbrief-migrate/constants.d.ts +13 -0
- package/dist/xbrief-migrate/constants.js +14 -0
- package/dist/xbrief-migrate/detect.d.ts +11 -0
- package/dist/xbrief-migrate/detect.js +81 -0
- package/dist/xbrief-migrate/drift-gate.d.ts +56 -0
- package/dist/xbrief-migrate/drift-gate.js +205 -0
- package/dist/xbrief-migrate/index.d.ts +7 -0
- package/dist/xbrief-migrate/index.js +7 -0
- package/dist/xbrief-migrate/migrate-project.d.ts +33 -0
- package/dist/xbrief-migrate/migrate-project.js +189 -0
- package/dist/xbrief-migrate/signpost.d.ts +5 -0
- package/dist/xbrief-migrate/signpost.js +19 -0
- package/dist/xbrief-migrate/transforms.d.ts +46 -0
- package/dist/xbrief-migrate/transforms.js +182 -0
- package/dist/xbrief-migrate/version.d.ts +8 -0
- package/dist/xbrief-migrate/version.js +25 -0
- package/package.json +11 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
4
|
+
import { hasArtifactSuffix, resolveEvalPath, resolveLifecycleRoot } from "../../layout/resolve.js";
|
|
3
5
|
import { AUDIT_LOG_REL_PATH, readAuditLog } from "../actions/candidates-log.js";
|
|
4
6
|
/** Lifecycle folders scanned for reconcilable vBRIEFs (#1468). */
|
|
5
7
|
export const BACKFILL_FOLDERS = ["proposed", "pending", "active"];
|
|
@@ -36,7 +38,7 @@ function extractIssueRef(data) {
|
|
|
36
38
|
continue;
|
|
37
39
|
}
|
|
38
40
|
const obj = ref;
|
|
39
|
-
if (obj.type
|
|
41
|
+
if (!referenceTypeMatches(String(obj.type ?? ""), "github-issue")) {
|
|
40
42
|
continue;
|
|
41
43
|
}
|
|
42
44
|
const [repo, number] = parseGithubIssueUri(obj.uri);
|
|
@@ -61,11 +63,11 @@ function auditKey(repo, issueNumber) {
|
|
|
61
63
|
*/
|
|
62
64
|
export function countReconcilable(projectRoot, options = {}) {
|
|
63
65
|
try {
|
|
64
|
-
const auditPath = options.auditLogPath ??
|
|
66
|
+
const auditPath = options.auditLogPath ?? resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
65
67
|
const existing = existingAuditRefs(auditPath);
|
|
66
68
|
const defaultRepo = options.defaultRepo ?? null;
|
|
67
69
|
const keys = new Set();
|
|
68
|
-
const vbriefRoot =
|
|
70
|
+
const vbriefRoot = resolveLifecycleRoot(projectRoot);
|
|
69
71
|
for (const folderName of BACKFILL_FOLDERS) {
|
|
70
72
|
const folder = join(vbriefRoot, folderName);
|
|
71
73
|
if (!existsSync(folder)) {
|
|
@@ -73,7 +75,7 @@ export function countReconcilable(projectRoot, options = {}) {
|
|
|
73
75
|
}
|
|
74
76
|
const entries = readdirSync(folder, { withFileTypes: true });
|
|
75
77
|
for (const entry of entries) {
|
|
76
|
-
if (!entry.isFile() || !entry.name
|
|
78
|
+
if (!entry.isFile() || !hasArtifactSuffix(entry.name)) {
|
|
77
79
|
continue;
|
|
78
80
|
}
|
|
79
81
|
const path = join(folder, entry.name);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
3
4
|
import { loadProjectDefinition } from "../../policy/resolve.js";
|
|
4
5
|
const DRIFT_MIN_ISSUES = 3;
|
|
5
6
|
const CACHE_SOURCE = "github-issue";
|
|
@@ -88,7 +89,7 @@ function resolveScopeRules(projectRoot) {
|
|
|
88
89
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
89
90
|
return [{ rule: "all-open" }];
|
|
90
91
|
}
|
|
91
|
-
const policy = plan
|
|
92
|
+
const policy = readPlanPolicy(plan);
|
|
92
93
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
93
94
|
return [{ rule: "all-open" }];
|
|
94
95
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { hasArtifactSuffix, resolveAuditPath, resolveEvalPath, resolveLifecycleRoot, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
4
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
5
|
+
import { CACHE_DIR_NAME, CACHE_SOURCE, DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, WIP_LIFECYCLE_DIRS, } from "./constants.js";
|
|
4
6
|
function loadProjectDefinition(projectRoot) {
|
|
5
|
-
const path =
|
|
7
|
+
const path = resolveProjectDefinitionPath(projectRoot);
|
|
6
8
|
if (!existsSync(path))
|
|
7
9
|
return null;
|
|
8
10
|
try {
|
|
@@ -37,17 +39,17 @@ function countCacheEntries(projectRoot) {
|
|
|
37
39
|
return count;
|
|
38
40
|
}
|
|
39
41
|
export function candidatesLogPath(projectRoot) {
|
|
40
|
-
return
|
|
42
|
+
return resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
41
43
|
}
|
|
42
44
|
function countWip(projectRoot) {
|
|
43
45
|
let total = 0;
|
|
44
|
-
const root =
|
|
46
|
+
const root = resolveLifecycleRoot(projectRoot);
|
|
45
47
|
for (const sub of WIP_LIFECYCLE_DIRS) {
|
|
46
48
|
const folder = join(root, sub);
|
|
47
49
|
if (!existsSync(folder))
|
|
48
50
|
continue;
|
|
49
51
|
for (const child of readdirSync(folder, { withFileTypes: true })) {
|
|
50
|
-
if (child.isFile() && child.name
|
|
52
|
+
if (child.isFile() && hasArtifactSuffix(child.name))
|
|
51
53
|
total += 1;
|
|
52
54
|
}
|
|
53
55
|
}
|
|
@@ -80,7 +82,7 @@ function summarizeScope(rules) {
|
|
|
80
82
|
return [true, `custom (${rules.length} rule(s))`];
|
|
81
83
|
}
|
|
82
84
|
function countPendingDecisions(projectRoot) {
|
|
83
|
-
const logPath =
|
|
85
|
+
const logPath = resolveAuditPath(projectRoot, "pending-human-decisions.jsonl");
|
|
84
86
|
if (!existsSync(logPath))
|
|
85
87
|
return 0;
|
|
86
88
|
let text;
|
|
@@ -121,9 +123,7 @@ export function pendingDecisionsNudgeLine(count, threshold = 3) {
|
|
|
121
123
|
export function detectPriorState(projectRoot) {
|
|
122
124
|
const data = loadProjectDefinition(projectRoot) ?? {};
|
|
123
125
|
const plan = data.plan;
|
|
124
|
-
const policy =
|
|
125
|
-
? plan.policy
|
|
126
|
-
: null;
|
|
126
|
+
const policy = readPlanPolicy(plan);
|
|
127
127
|
const rawScope = typeof policy === "object" && policy !== null && !Array.isArray(policy)
|
|
128
128
|
? policy.triageScope
|
|
129
129
|
: null;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveEvalPath, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
4
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
3
5
|
import { countVbriefWip, DEFAULT_WIP_CAP, resolveWipCap } from "../../policy/wip.js";
|
|
4
6
|
import { countReconcilable } from "../reconcile/reconcile.js";
|
|
5
7
|
import { computeDrift } from "../scope-drift/compute.js";
|
|
6
|
-
import {
|
|
8
|
+
import { shouldSuppressD2Emission } from "../summary/index.js";
|
|
7
9
|
import { CACHE_DIR_NAME, CACHE_SOURCE, EMPTY_CACHE_LINE, MAX_LINE_CHARS, SUMMARY_HISTORY_SCHEMA, WIP_WARN_GLYPH, } from "./constants.js";
|
|
8
|
-
const CANDIDATES_LOG_REL_PATH = "vbrief/.eval/candidates.jsonl";
|
|
9
10
|
const FILESYSTEM_IN_FLIGHT_STATUS = "running";
|
|
10
11
|
const TRIAGED_DECISIONS = new Set([
|
|
11
12
|
"accept",
|
|
@@ -72,12 +73,12 @@ function latestDecisions(entries) {
|
|
|
72
73
|
return out;
|
|
73
74
|
}
|
|
74
75
|
function countFilesystemInFlight(projectRoot) {
|
|
75
|
-
const activeDir =
|
|
76
|
+
const activeDir = resolveLifecycleFolder(projectRoot, "active");
|
|
76
77
|
if (!existsSync(activeDir))
|
|
77
78
|
return 0;
|
|
78
79
|
let count = 0;
|
|
79
80
|
for (const name of readdirSync(activeDir)) {
|
|
80
|
-
if (!name
|
|
81
|
+
if (!hasArtifactSuffix(name))
|
|
81
82
|
continue;
|
|
82
83
|
try {
|
|
83
84
|
const data = JSON.parse(readFileSync(join(activeDir, name), "utf8"));
|
|
@@ -92,7 +93,7 @@ function countFilesystemInFlight(projectRoot) {
|
|
|
92
93
|
return count;
|
|
93
94
|
}
|
|
94
95
|
function isTriageScopeConfigured(projectRoot) {
|
|
95
|
-
const path =
|
|
96
|
+
const path = resolveProjectDefinitionPath(projectRoot);
|
|
96
97
|
if (!existsSync(path))
|
|
97
98
|
return false;
|
|
98
99
|
try {
|
|
@@ -100,7 +101,7 @@ function isTriageScopeConfigured(projectRoot) {
|
|
|
100
101
|
const plan = data.plan;
|
|
101
102
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan))
|
|
102
103
|
return false;
|
|
103
|
-
const scope = plan
|
|
104
|
+
const scope = readPlanPolicy(plan);
|
|
104
105
|
if (typeof scope !== "object" || scope === null || Array.isArray(scope))
|
|
105
106
|
return false;
|
|
106
107
|
const triageScope = scope.triageScope;
|
|
@@ -118,7 +119,7 @@ function utcIso() {
|
|
|
118
119
|
export function computeSummary(projectRoot) {
|
|
119
120
|
const root = resolve(projectRoot);
|
|
120
121
|
const cacheRoot = join(root, CACHE_DIR_NAME);
|
|
121
|
-
const logPath =
|
|
122
|
+
const logPath = resolveEvalPath(root, "candidates.jsonl");
|
|
122
123
|
const cached = iterCachedIssues(cacheRoot);
|
|
123
124
|
const repos = [...new Set(cached.map(([r]) => r))].sort().slice(0, 8);
|
|
124
125
|
const wipCapResult = resolveWipCap(root);
|
|
@@ -292,7 +293,7 @@ export function appendHistory(historyPath, result, line) {
|
|
|
292
293
|
export function emitOneliner(projectRoot, options = {}) {
|
|
293
294
|
const result = computeSummary(projectRoot);
|
|
294
295
|
const line = formatSummary(result);
|
|
295
|
-
const historyPath =
|
|
296
|
+
const historyPath = resolveEvalPath(projectRoot, "summary-history.jsonl");
|
|
296
297
|
const applySuppression = options.applyD2Suppression !== false;
|
|
297
298
|
if (applySuppression && shouldSuppressD2Emission(result, historyPath, { now: options.now })) {
|
|
298
299
|
return line;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
4
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
5
|
+
import { AUDIT_LOG_REL_PATH, DEFAULT_RELIEF_AGE_DAYS, DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, WELCOME_AUDIT_TAG, } from "./constants.js";
|
|
4
6
|
function projectDefinitionPath(projectRoot) {
|
|
5
|
-
return
|
|
7
|
+
return resolveProjectDefinitionPath(projectRoot);
|
|
6
8
|
}
|
|
7
9
|
function utcIso() {
|
|
8
10
|
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -34,10 +36,11 @@ export function writeTriageScope(projectRoot, rules, options = { presetLabel: "c
|
|
|
34
36
|
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
35
37
|
}
|
|
36
38
|
const planRec = plan;
|
|
37
|
-
|
|
39
|
+
migrateLegacyPolicyKey(planRec);
|
|
40
|
+
let policy = planRec[PLAN_POLICY_KEY];
|
|
38
41
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
39
42
|
policy = {};
|
|
40
|
-
planRec
|
|
43
|
+
planRec[PLAN_POLICY_KEY] = policy;
|
|
41
44
|
}
|
|
42
45
|
const policyRec = policy;
|
|
43
46
|
const previous = policyRec.triageScope;
|
|
@@ -68,10 +71,11 @@ export function writeWipCap(projectRoot, wipCap, options = {}) {
|
|
|
68
71
|
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
69
72
|
}
|
|
70
73
|
const planRec = plan;
|
|
71
|
-
|
|
74
|
+
migrateLegacyPolicyKey(planRec);
|
|
75
|
+
let policy = planRec[PLAN_POLICY_KEY];
|
|
72
76
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
73
77
|
policy = {};
|
|
74
|
-
planRec
|
|
78
|
+
planRec[PLAN_POLICY_KEY] = policy;
|
|
75
79
|
}
|
|
76
80
|
const policyRec = policy;
|
|
77
81
|
const previous = policyRec.wipCap;
|
|
@@ -124,16 +128,14 @@ function daysInPending(path, now) {
|
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
export function previewWipRelief(projectRoot, olderThanDays = DEFAULT_RELIEF_AGE_DAYS) {
|
|
127
|
-
const pendingDir =
|
|
131
|
+
const pendingDir = resolveLifecycleFolder(projectRoot, "pending");
|
|
128
132
|
if (!existsSync(pendingDir)) {
|
|
129
133
|
return { olderThanDays, eligibleCount: 0, eligibleFiles: [], skippedCount: 0 };
|
|
130
134
|
}
|
|
131
135
|
const now = new Date();
|
|
132
136
|
const eligible = [];
|
|
133
137
|
let skipped = 0;
|
|
134
|
-
for (const name of [...readdirSync(pendingDir)]
|
|
135
|
-
.filter((n) => n.endsWith(".vbrief.json"))
|
|
136
|
-
.sort()) {
|
|
138
|
+
for (const name of [...readdirSync(pendingDir)].filter((n) => hasArtifactSuffix(n)).sort()) {
|
|
137
139
|
const path = join(pendingDir, name);
|
|
138
140
|
const days = daysInPending(path, now);
|
|
139
141
|
if (days >= olderThanDays)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveAuditPath } from "../layout/resolve.js";
|
|
3
|
+
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
3
4
|
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
4
5
|
export const DEFAULT_CAPACITY_UNIT = "vbrief-count";
|
|
5
6
|
export const CAPACITY_UNIT_COST = "cost";
|
|
@@ -139,7 +140,7 @@ export function resolveCapacityAllocation(projectRoot) {
|
|
|
139
140
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
140
141
|
return defaultCapacityAllocation("default");
|
|
141
142
|
}
|
|
142
|
-
const policy = plan
|
|
143
|
+
const policy = readPlanPolicy(plan);
|
|
143
144
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
144
145
|
return defaultCapacityAllocation("default");
|
|
145
146
|
}
|
|
@@ -197,7 +198,7 @@ export function resolveAutonomy(projectRoot) {
|
|
|
197
198
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
198
199
|
return defaultAutonomyPolicy("default");
|
|
199
200
|
}
|
|
200
|
-
const policy = plan
|
|
201
|
+
const policy = readPlanPolicy(plan);
|
|
201
202
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
202
203
|
return defaultAutonomyPolicy("default");
|
|
203
204
|
}
|
|
@@ -243,7 +244,7 @@ function parseIsoTs(value) {
|
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
246
|
export function readDecisionEvents(projectRoot) {
|
|
246
|
-
const path =
|
|
247
|
+
const path = resolveAuditPath(projectRoot, "pending-human-decisions.jsonl");
|
|
247
248
|
if (!existsSync(path))
|
|
248
249
|
return [];
|
|
249
250
|
const out = [];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
4
|
+
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
3
5
|
import { CAPACITY_UNIT_COST, DEFAULT_CAPACITY_UNIT, DEFAULT_PENDING_DECISIONS_THRESHOLD, pendingDecisionsNudgeLine, recommendAutonomyLevel, resolveAutonomy, resolveCapacityAllocation, summarizeDecisionBacklog, } from "./capacity-policy.js";
|
|
4
6
|
export const FORWARD_FOLDERS = ["pending", "active"];
|
|
5
7
|
export const BACKWARD_FOLDER = "completed";
|
|
@@ -25,7 +27,7 @@ function planHasChildren(plan) {
|
|
|
25
27
|
return false;
|
|
26
28
|
return refs.some((ref) => typeof ref === "object" &&
|
|
27
29
|
ref !== null &&
|
|
28
|
-
ref.type
|
|
30
|
+
referenceTypeMatches(String(ref.type ?? ""), "plan"));
|
|
29
31
|
}
|
|
30
32
|
function coerceCost(value) {
|
|
31
33
|
if (typeof value === "boolean")
|
|
@@ -96,7 +98,7 @@ export function iterVbriefPlans(vbriefRoot) {
|
|
|
96
98
|
if (!existsSync(folderPath) || !statSync(folderPath).isDirectory())
|
|
97
99
|
continue;
|
|
98
100
|
for (const name of readdirSync(folderPath).sort()) {
|
|
99
|
-
if (!name
|
|
101
|
+
if (!hasArtifactSuffix(name))
|
|
100
102
|
continue;
|
|
101
103
|
const child = join(folderPath, name);
|
|
102
104
|
try {
|
|
@@ -145,7 +147,7 @@ export function bucketDeficit(report, tally) {
|
|
|
145
147
|
export function computeReport(projectRoot, options = {}) {
|
|
146
148
|
const now = options.now ?? new Date();
|
|
147
149
|
const allocation = options.allocation ?? resolveCapacityAllocation(projectRoot);
|
|
148
|
-
const vbriefRoot =
|
|
150
|
+
const vbriefRoot = resolveLifecycleRoot(projectRoot);
|
|
149
151
|
const records = iterVbriefPlans(vbriefRoot).map(({ folder, plan }) => classifyRecord(plan, folder, allocation, now));
|
|
150
152
|
const tallies = new Map();
|
|
151
153
|
for (const bucket of allocation.buckets) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/** Linear filename validator for YYYY-MM-DD-<slug>.vbrief.json (no nested regex). */
|
|
1
|
+
/** Linear filename validator for YYYY-MM-DD-<slug>.{vbrief,xbrief}.json (no nested regex). */
|
|
2
2
|
export declare function isDatePrefixedVbriefFilename(name: string): boolean;
|
|
3
3
|
//# sourceMappingURL=filename.d.ts.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { hasArtifactSuffix, stripArtifactSuffix } from "../layout/resolve.js";
|
|
2
|
+
/** Linear filename validator for YYYY-MM-DD-<slug>.{vbrief,xbrief}.json (no nested regex). */
|
|
2
3
|
export function isDatePrefixedVbriefFilename(name) {
|
|
3
|
-
|
|
4
|
-
if (!name
|
|
4
|
+
// Layout-aware (#2109 part 2a): accept either artifact suffix.
|
|
5
|
+
if (!hasArtifactSuffix(name))
|
|
5
6
|
return false;
|
|
6
|
-
const base = name
|
|
7
|
+
const base = stripArtifactSuffix(name);
|
|
7
8
|
if (base.length < 12)
|
|
8
9
|
return false;
|
|
9
10
|
const y = base.slice(0, 4);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleLayout, resolveProjectDefinitionPath, } from "../layout/resolve.js";
|
|
3
4
|
import { checkSpecMigrationFidelity } from "../spec-authority/migration-fidelity.js";
|
|
4
5
|
import { isFullSpecState } from "../spec-authority/resolver.js";
|
|
5
6
|
import { isDatePrefixedVbriefFilename } from "./filename.js";
|
|
@@ -26,7 +27,10 @@ function isDeftFrameworkRoot(projectRoot) {
|
|
|
26
27
|
export function validateStrategyOutput(projectRoot, strict = false) {
|
|
27
28
|
const root = resolve(projectRoot);
|
|
28
29
|
const errors = [];
|
|
29
|
-
|
|
30
|
+
// Layout-aware (#2109 part 2a): resolve the lifecycle dir/suffix dynamically;
|
|
31
|
+
// identical to vbrief/ on today's tree.
|
|
32
|
+
const layout = resolveLifecycleLayout(root);
|
|
33
|
+
const vbriefDir = layout.root;
|
|
30
34
|
if (!existsSync(vbriefDir)) {
|
|
31
35
|
if (strict) {
|
|
32
36
|
errors.push("vbrief/ directory missing entirely. v0.20 strategies must emit at least " +
|
|
@@ -34,13 +38,18 @@ export function validateStrategyOutput(projectRoot, strict = false) {
|
|
|
34
38
|
}
|
|
35
39
|
return errors;
|
|
36
40
|
}
|
|
37
|
-
const projDef =
|
|
41
|
+
const projDef = resolveProjectDefinitionPath(root);
|
|
38
42
|
if (!existsSync(projDef)) {
|
|
39
|
-
errors.push(
|
|
43
|
+
errors.push(`Missing ${layout.artifactDir}/PROJECT-DEFINITION${layout.artifactSuffix}. ` +
|
|
40
44
|
"All v0.20-conformant strategy output must include a complete project definition " +
|
|
41
45
|
"(see v0-20-contract.md and task project:render).");
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
// The legacy-spec guard intentionally stays pinned to the literal legacy
|
|
48
|
+
// path (#2109 part 2a): its job is to detect a stranded pre-v0.20
|
|
49
|
+
// vbrief/specification.vbrief.json, which is a legacy artifact regardless of
|
|
50
|
+
// the active layout -- routing it through the resolver would make it
|
|
51
|
+
// self-cancelling once an xbrief tree is live.
|
|
52
|
+
const specLegacy = join(root, "vbrief", "specification.vbrief.json");
|
|
44
53
|
if (existsSync(specLegacy) && !isDeftFrameworkRoot(root) && !isFullSpecState(root)) {
|
|
45
54
|
errors.push("Legacy artifact vbrief/specification.vbrief.json present. " +
|
|
46
55
|
"v0.20 strategies MUST NOT dual-write the old specification.vbrief.json " +
|
|
@@ -52,7 +61,7 @@ export function validateStrategyOutput(projectRoot, strict = false) {
|
|
|
52
61
|
if (!existsSync(dpath) || !statSync(dpath).isDirectory())
|
|
53
62
|
continue;
|
|
54
63
|
for (const name of readdirSync(dpath).sort()) {
|
|
55
|
-
if (!name
|
|
64
|
+
if (!hasArtifactSuffix(name))
|
|
56
65
|
continue;
|
|
57
66
|
if (!isDatePrefixedVbriefFilename(name)) {
|
|
58
67
|
errors.push(`Non-conformant filename in vbrief/${dname}/: ${name}. ` +
|
|
@@ -8,12 +8,19 @@ export const INTERNAL_REFERENCE_TYPES = new Set([
|
|
|
8
8
|
"x-vbrief/plan",
|
|
9
9
|
"x-vbrief/spec-section",
|
|
10
10
|
"x-vbrief/user-request",
|
|
11
|
+
"x-xbrief/plan",
|
|
12
|
+
"x-xbrief/spec-section",
|
|
13
|
+
"x-xbrief/user-request",
|
|
11
14
|
]);
|
|
12
15
|
export const EXTERNAL_REFERENCE_TYPES = new Set([
|
|
13
16
|
"x-vbrief/github-issue",
|
|
14
17
|
"x-vbrief/github-pr",
|
|
15
18
|
"x-vbrief/jira-ticket",
|
|
16
19
|
"x-vbrief/web-page",
|
|
20
|
+
"x-xbrief/github-issue",
|
|
21
|
+
"x-xbrief/github-pr",
|
|
22
|
+
"x-xbrief/jira-ticket",
|
|
23
|
+
"x-xbrief/web-page",
|
|
17
24
|
]);
|
|
18
25
|
export const FOLDER_TO_STATUSES = {
|
|
19
26
|
proposed: ["draft", "proposed"],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
3
4
|
import { referenceWithDefaultTrust, slugify } from "./build.js";
|
|
4
5
|
import { EMITTED_VBRIEF_VERSION } from "./constants.js";
|
|
5
6
|
import { pythonJsonPretty } from "./json.js";
|
|
@@ -99,7 +100,7 @@ export function createSpeckitScopeVbrief(item, options) {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
const references = [
|
|
102
|
-
referenceWithDefaultTrust({ type: "x-
|
|
103
|
+
referenceWithDefaultTrust({ type: "x-xbrief/plan", uri: specRef }),
|
|
103
104
|
];
|
|
104
105
|
const itemRefs = item.references;
|
|
105
106
|
if (Array.isArray(itemRefs)) {
|
|
@@ -107,7 +108,7 @@ export function createSpeckitScopeVbrief(item, options) {
|
|
|
107
108
|
if (typeof ref === "object" &&
|
|
108
109
|
ref !== null &&
|
|
109
110
|
!Array.isArray(ref) &&
|
|
110
|
-
ref.type
|
|
111
|
+
!referenceTypeMatches(String(ref.type ?? ""), "plan")) {
|
|
111
112
|
references.push(referenceWithDefaultTrust(ref));
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleFolder } from "../layout/resolve.js";
|
|
3
4
|
import { countVbriefWip, resolveWipCap } from "../policy/wip.js";
|
|
4
5
|
import { runTransition } from "../scope/transition.js";
|
|
5
6
|
import { allScopeIds, asStrList, candidateDepGraph, candidateFromPath, markCycles, } from "./swarm-deps.js";
|
|
@@ -27,7 +28,7 @@ function candidateInCycle(candidate) {
|
|
|
27
28
|
}
|
|
28
29
|
export function reconcileGraph(projectRoot, options = {}) {
|
|
29
30
|
const root = resolve(projectRoot);
|
|
30
|
-
const proposedDir =
|
|
31
|
+
const proposedDir = resolveLifecycleFolder(root, "proposed");
|
|
31
32
|
if (!existsSync(proposedDir)) {
|
|
32
33
|
return [
|
|
33
34
|
2,
|
|
@@ -45,7 +46,7 @@ export function reconcileGraph(projectRoot, options = {}) {
|
|
|
45
46
|
];
|
|
46
47
|
}
|
|
47
48
|
const candidatePaths = readdirSync(proposedDir)
|
|
48
|
-
.filter((f) => f
|
|
49
|
+
.filter((f) => hasArtifactSuffix(f))
|
|
49
50
|
.sort()
|
|
50
51
|
.map((f) => join(proposedDir, f));
|
|
51
52
|
const candidates = candidatePaths
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleRoot, stripArtifactSuffix } from "../layout/resolve.js";
|
|
3
4
|
import { call } from "../scm/call.js";
|
|
4
5
|
import { extractIssueRef } from "../triage/reconcile/parse-uri.js";
|
|
5
6
|
import { depResolved, RESOLVED_FOLDERS } from "./graph.js";
|
|
@@ -83,7 +84,7 @@ function hasUnresolvedDeps(swarm, knownIds) {
|
|
|
83
84
|
}
|
|
84
85
|
export function reconcileLabels(projectRoot, options = {}) {
|
|
85
86
|
const root = resolve(projectRoot);
|
|
86
|
-
const vbriefDir =
|
|
87
|
+
const vbriefDir = resolveLifecycleRoot(root);
|
|
87
88
|
if (!existsSync(vbriefDir)) {
|
|
88
89
|
return [
|
|
89
90
|
2,
|
|
@@ -111,7 +112,7 @@ export function reconcileLabels(projectRoot, options = {}) {
|
|
|
111
112
|
if (!existsSync(folderPath))
|
|
112
113
|
continue;
|
|
113
114
|
const files = readdirSync(folderPath)
|
|
114
|
-
.filter((f) => f
|
|
115
|
+
.filter((f) => hasArtifactSuffix(f))
|
|
115
116
|
.sort();
|
|
116
117
|
for (const file of files) {
|
|
117
118
|
const path = join(folderPath, file);
|
|
@@ -127,7 +128,7 @@ export function reconcileLabels(projectRoot, options = {}) {
|
|
|
127
128
|
const plan = typeof data.plan === "object" && data.plan !== null && !Array.isArray(data.plan)
|
|
128
129
|
? data.plan
|
|
129
130
|
: {};
|
|
130
|
-
const storyId = String(plan.id ?? file
|
|
131
|
+
const storyId = String(plan.id ?? stripArtifactSuffix(file));
|
|
131
132
|
const [refRepo, number] = extractIssueRef(data);
|
|
132
133
|
const effectiveRepo = refRepo ?? options.repo ?? null;
|
|
133
134
|
if (number === null || effectiveRepo === null) {
|
|
@@ -6,7 +6,11 @@ const WIP_MARKERS = ["[wip]", "[in progress]", "[in-progress]", "[running]", "[a
|
|
|
6
6
|
const BLOCKED_MARKERS = ["[blocked]"];
|
|
7
7
|
const CANCELLED_MARKERS = ["[cancelled]", "[canceled]"];
|
|
8
8
|
export const OVERRIDES_FILENAME = "migration-overrides.yaml";
|
|
9
|
-
const GITHUB_ISSUE_REF_TYPES = new Set([
|
|
9
|
+
const GITHUB_ISSUE_REF_TYPES = new Set([
|
|
10
|
+
"github-issue",
|
|
11
|
+
"x-vbrief/github-issue",
|
|
12
|
+
"x-xbrief/github-issue",
|
|
13
|
+
]);
|
|
10
14
|
const GITHUB_ISSUE_URI_RE = /https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/(\d+)/;
|
|
11
15
|
const PHASE_TITLE_RE = /^(Phase\s+\d|IP[-\s]\d|Milestone\s+\d)/i;
|
|
12
16
|
const STATUS_TO_FOLDER = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleRoot, stripArtifactSuffix } from "../layout/resolve.js";
|
|
3
4
|
import { pyRepr } from "./py-repr.js";
|
|
4
5
|
export const LIFECYCLE_FOLDERS = [
|
|
5
6
|
"proposed",
|
|
@@ -47,17 +48,17 @@ function storyId(path, plan) {
|
|
|
47
48
|
if (typeof value === "string" && value.trim())
|
|
48
49
|
return value.trim();
|
|
49
50
|
const name = path.split(/[/\\]/).pop() ?? "";
|
|
50
|
-
return name
|
|
51
|
+
return hasArtifactSuffix(name) ? stripArtifactSuffix(name) : name;
|
|
51
52
|
}
|
|
52
53
|
export function allScopeIds(projectRoot) {
|
|
53
54
|
const ids = {};
|
|
54
|
-
const vbriefDir =
|
|
55
|
+
const vbriefDir = resolveLifecycleRoot(projectRoot);
|
|
55
56
|
for (const folder of LIFECYCLE_FOLDERS) {
|
|
56
57
|
const folderPath = join(vbriefDir, folder);
|
|
57
58
|
if (!existsSync(folderPath))
|
|
58
59
|
continue;
|
|
59
60
|
const files = readdirSync(folderPath)
|
|
60
|
-
.filter((f) => f
|
|
61
|
+
.filter((f) => hasArtifactSuffix(f))
|
|
61
62
|
.sort();
|
|
62
63
|
for (const file of files) {
|
|
63
64
|
const path = join(folderPath, file);
|
|
@@ -68,7 +69,7 @@ export function allScopeIds(projectRoot) {
|
|
|
68
69
|
const scopeId = storyId(path, plan);
|
|
69
70
|
const status = String(plan.status ?? "");
|
|
70
71
|
ids[scopeId] = [path, status];
|
|
71
|
-
const stem = file
|
|
72
|
+
const stem = hasArtifactSuffix(file) ? stripArtifactSuffix(file) : file;
|
|
72
73
|
if (!ids[stem])
|
|
73
74
|
ids[stem] = [path, status];
|
|
74
75
|
}
|
|
@@ -2,7 +2,7 @@ import type { Child, ReconcileUmbrellasOutcome, UmbrellaClient } from "./types.j
|
|
|
2
2
|
export declare const OPEN_FOLDERS: readonly ["proposed", "pending", "active"];
|
|
3
3
|
export declare const CLOSED_FOLDERS: readonly ["completed", "cancelled"];
|
|
4
4
|
export declare const LIFECYCLE_FOLDERS: readonly ["proposed", "pending", "active", "completed", "cancelled"];
|
|
5
|
-
export declare const CHILD_REF_TYPE = "x-
|
|
5
|
+
export declare const CHILD_REF_TYPE = "x-xbrief/plan";
|
|
6
6
|
export declare class UmbrellaScmError extends Error {
|
|
7
7
|
name: string;
|
|
8
8
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
4
|
+
import { hasArtifactSuffix, resolveLifecycleRoot, stripArtifactSuffix } from "../layout/resolve.js";
|
|
3
5
|
import { call } from "../scm/call.js";
|
|
4
6
|
import { extractIssueRef } from "../triage/reconcile/parse-uri.js";
|
|
5
7
|
export const OPEN_FOLDERS = ["proposed", "pending", "active"];
|
|
6
8
|
export const CLOSED_FOLDERS = ["completed", "cancelled"];
|
|
7
9
|
export const LIFECYCLE_FOLDERS = [...OPEN_FOLDERS, ...CLOSED_FOLDERS];
|
|
8
|
-
export const CHILD_REF_TYPE = "x-
|
|
10
|
+
export const CHILD_REF_TYPE = "x-xbrief/plan";
|
|
9
11
|
const SCM_SOURCE = "github-issue";
|
|
10
12
|
const HEADER_RE = /^## Current shape \(as of pass-(\d+)\)/m;
|
|
11
13
|
// ReDoS-hardened (#1782 s4 / CodeQL js/polynomial-redos): the original
|
|
@@ -63,14 +65,14 @@ export function buildChildIndex(vbriefDir) {
|
|
|
63
65
|
if (!existsSync(folderPath))
|
|
64
66
|
continue;
|
|
65
67
|
const files = readdirSync(folderPath)
|
|
66
|
-
.filter((f) => f
|
|
68
|
+
.filter((f) => hasArtifactSuffix(f))
|
|
67
69
|
.sort();
|
|
68
70
|
for (const file of files) {
|
|
69
71
|
const path = join(folderPath, file);
|
|
70
72
|
const data = readJson(path);
|
|
71
73
|
if (!data)
|
|
72
74
|
continue;
|
|
73
|
-
const fallbackId = file
|
|
75
|
+
const fallbackId = stripArtifactSuffix(file);
|
|
74
76
|
index[file] = childFromData(data, folder, fallbackId);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
@@ -89,7 +91,7 @@ export function computeChildren(epicData, index) {
|
|
|
89
91
|
if (typeof ref !== "object" || ref === null || Array.isArray(ref))
|
|
90
92
|
continue;
|
|
91
93
|
const rec = ref;
|
|
92
|
-
if (rec.type
|
|
94
|
+
if (!referenceTypeMatches(String(rec.type ?? ""), "plan"))
|
|
93
95
|
continue;
|
|
94
96
|
const name = basename(String(rec.uri ?? ""));
|
|
95
97
|
const child = index[name];
|
|
@@ -331,7 +333,7 @@ export function nowIso(date = new Date()) {
|
|
|
331
333
|
}
|
|
332
334
|
export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
333
335
|
const root = resolve(projectRoot);
|
|
334
|
-
const vbriefDir =
|
|
336
|
+
const vbriefDir = resolveLifecycleRoot(root);
|
|
335
337
|
if (!existsSync(vbriefDir)) {
|
|
336
338
|
return [
|
|
337
339
|
2,
|
|
@@ -360,7 +362,7 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
|
360
362
|
if (!existsSync(folderPath))
|
|
361
363
|
continue;
|
|
362
364
|
const files = readdirSync(folderPath)
|
|
363
|
-
.filter((f) => f
|
|
365
|
+
.filter((f) => hasArtifactSuffix(f))
|
|
364
366
|
.sort();
|
|
365
367
|
for (const file of files) {
|
|
366
368
|
const path = join(folderPath, file);
|
|
@@ -375,7 +377,7 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
|
375
377
|
: {};
|
|
376
378
|
if (metadata.kind !== "epic")
|
|
377
379
|
continue;
|
|
378
|
-
const storyId = String(plan.id ?? file
|
|
380
|
+
const storyId = String(plan.id ?? stripArtifactSuffix(file));
|
|
379
381
|
const [refRepo, number] = extractIssueRef(data);
|
|
380
382
|
const effectiveRepo = refRepo ?? options.repo ?? null;
|
|
381
383
|
if (number === null || effectiveRepo === null) {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export declare const DOC_CORE: Set<string>;
|
|
2
2
|
export declare const PLAN_CORE: Set<string>;
|
|
3
3
|
export declare const ITEM_CORE: Set<string>;
|
|
4
|
-
export declare const EXTENSION_PREFIXES: readonly ["x-directive/", "x-vbrief/"];
|
|
5
|
-
export declare const ALLOW_LIST: Set<string>;
|
|
4
|
+
export declare const EXTENSION_PREFIXES: readonly ["x-directive/", "x-vbrief/", "x-xbrief/"];
|
|
6
5
|
export interface ConformanceFinding {
|
|
7
6
|
readonly path: string;
|
|
8
7
|
readonly level: string;
|