@deftai/directive-core 0.65.0 → 0.66.1
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.d.ts +24 -0
- package/dist/intake/issue-ingest.js +113 -16
- 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,3 +1,4 @@
|
|
|
1
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
1
2
|
import { REFERENCED_BY_VBRIEF_SCOPES, SLICED_FROM_SCOPES, VALID_IGNORE_KEYS, VALID_IGNORE_RULES, VALID_RULE_TYPES, } from "./constants.js";
|
|
2
3
|
import { parseDurationMs } from "./duration.js";
|
|
3
4
|
import { validateMilestoneRule } from "./milestone.js";
|
|
@@ -238,9 +239,7 @@ function pointerForError(err, rawList) {
|
|
|
238
239
|
}
|
|
239
240
|
export function validateTriageScopeOnPlan(plan, filepath) {
|
|
240
241
|
const out = [];
|
|
241
|
-
const policy =
|
|
242
|
-
? plan.policy
|
|
243
|
-
: undefined;
|
|
242
|
+
const policy = readPlanPolicy(plan);
|
|
244
243
|
const rawScope = typeof policy === "object" && policy !== null && !Array.isArray(policy)
|
|
245
244
|
? policy.triageScope
|
|
246
245
|
: undefined;
|
|
@@ -254,9 +253,7 @@ export function validateTriageScopeOnPlan(plan, filepath) {
|
|
|
254
253
|
}
|
|
255
254
|
export function validateTriageScopeIgnoresOnPlan(plan, filepath) {
|
|
256
255
|
const out = [];
|
|
257
|
-
const policy =
|
|
258
|
-
? plan.policy
|
|
259
|
-
: undefined;
|
|
256
|
+
const policy = readPlanPolicy(plan);
|
|
260
257
|
const raw = typeof policy === "object" && policy !== null && !Array.isArray(policy)
|
|
261
258
|
? policy.triageScopeIgnores
|
|
262
259
|
: undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdtempSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
4
5
|
const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
5
6
|
function projectDefinitionPath(projectRoot) {
|
|
6
7
|
return join(resolve(projectRoot), PROJECT_DEFINITION_REL_PATH);
|
|
@@ -54,10 +55,11 @@ export function addIgnore(projectRoot, options) {
|
|
|
54
55
|
throw new Error(`PROJECT-DEFINITION at ${path} has a non-object 'plan' key`);
|
|
55
56
|
}
|
|
56
57
|
const planRec = plan;
|
|
57
|
-
|
|
58
|
+
migrateLegacyPolicyKey(planRec);
|
|
59
|
+
let policy = planRec[PLAN_POLICY_KEY];
|
|
58
60
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
59
61
|
policy = {};
|
|
60
|
-
planRec
|
|
62
|
+
planRec[PLAN_POLICY_KEY] = policy;
|
|
61
63
|
}
|
|
62
64
|
const policyRec = policy;
|
|
63
65
|
const raw = Array.isArray(policyRec.triageScopeIgnores)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
3
4
|
const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
4
5
|
const DEFAULT_TRIAGE_SCOPE = [{ rule: "all-open" }];
|
|
5
6
|
function loadProjectDefinition(projectRoot) {
|
|
@@ -25,7 +26,7 @@ export function resolveScopeRules(projectRoot, projectDefinition) {
|
|
|
25
26
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
26
27
|
return DEFAULT_TRIAGE_SCOPE.map((r) => ({ ...r }));
|
|
27
28
|
}
|
|
28
|
-
const policy = plan
|
|
29
|
+
const policy = readPlanPolicy(plan);
|
|
29
30
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
30
31
|
return DEFAULT_TRIAGE_SCOPE.map((r) => ({ ...r }));
|
|
31
32
|
}
|
|
@@ -47,7 +48,7 @@ export function resolveScopeIgnores(projectRoot, projectDefinition) {
|
|
|
47
48
|
const plan = data.plan;
|
|
48
49
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan))
|
|
49
50
|
return out;
|
|
50
|
-
const policy = plan
|
|
51
|
+
const policy = readPlanPolicy(plan);
|
|
51
52
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy))
|
|
52
53
|
return out;
|
|
53
54
|
const raw = policy.triageScopeIgnores;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
|
-
import { basename, join
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import { resolveEvalPath, resolveProjectDefinitionPath } from "../../layout/resolve.js";
|
|
5
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
4
6
|
export const SUBSCRIPTION_HISTORY_REL_PATH = "vbrief/.eval/subscription-history.jsonl";
|
|
5
7
|
export const SUBSCRIPTION_HISTORY_SCHEMA = "deft.triage.subscription-change.v1";
|
|
6
8
|
export const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
@@ -15,7 +17,7 @@ export class ProjectDefinitionIOError extends Error {
|
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
function projectDefinitionPath(projectRoot) {
|
|
18
|
-
return
|
|
20
|
+
return resolveProjectDefinitionPath(projectRoot);
|
|
19
21
|
}
|
|
20
22
|
export function loadProjectDefinitionForMutation(projectRoot) {
|
|
21
23
|
const path = projectDefinitionPath(projectRoot);
|
|
@@ -87,7 +89,7 @@ function resolveActor(actor) {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
export function recordSubscriptionChange(projectRoot, options) {
|
|
90
|
-
const historyPath =
|
|
92
|
+
const historyPath = resolveEvalPath(projectRoot, "subscription-history.jsonl");
|
|
91
93
|
const record = {
|
|
92
94
|
schema: SUBSCRIPTION_HISTORY_SCHEMA,
|
|
93
95
|
change_id: randomUUID(),
|
|
@@ -249,15 +251,19 @@ function mutate(projectRoot, options) {
|
|
|
249
251
|
throw new Error(`PROJECT-DEFINITION at ${path} has a non-object 'plan' key`);
|
|
250
252
|
}
|
|
251
253
|
const plan = data.plan;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
migrateLegacyPolicyKey(plan);
|
|
255
|
+
const existingPolicy = plan[PLAN_POLICY_KEY];
|
|
256
|
+
if (typeof existingPolicy !== "object" ||
|
|
257
|
+
existingPolicy === null ||
|
|
258
|
+
Array.isArray(existingPolicy)) {
|
|
259
|
+
if (existingPolicy === undefined) {
|
|
260
|
+
plan[PLAN_POLICY_KEY] = {};
|
|
255
261
|
}
|
|
256
262
|
else {
|
|
257
263
|
throw new Error(`PROJECT-DEFINITION at ${path} has a non-object 'plan.policy' key`);
|
|
258
264
|
}
|
|
259
265
|
}
|
|
260
|
-
const policy = plan
|
|
266
|
+
const policy = plan[PLAN_POLICY_KEY];
|
|
261
267
|
if (!Array.isArray(policy.triageScope)) {
|
|
262
268
|
if (policy.triageScope === undefined) {
|
|
263
269
|
policy.triageScope = [];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join, resolve as pathResolve } from "node:path";
|
|
4
|
+
import { hasArtifactSuffix, resolveEvalPath, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
5
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
4
6
|
import { loadProjectDefinition, PROJECT_DEFINITION_REL_PATH } from "../../policy/resolve.js";
|
|
5
7
|
import { countVbriefWip, DEFAULT_WIP_CAP, resolveWipCap } from "../../policy/wip.js";
|
|
6
8
|
import { AUDIT_LOG_REL_PATH, latestDecisions, readAuditLog } from "../actions/candidates-log.js";
|
|
@@ -93,13 +95,13 @@ export function iterCachedIssues(cacheRoot) {
|
|
|
93
95
|
// ---------------------------------------------------------------------------
|
|
94
96
|
/** Count filesystem-truth in-flight vBRIEFs (#1270). */
|
|
95
97
|
export function countFilesystemInFlight(projectRoot) {
|
|
96
|
-
const folder =
|
|
98
|
+
const folder = resolveLifecycleFolder(projectRoot, FILESYSTEM_IN_FLIGHT_FOLDER);
|
|
97
99
|
if (!existsSync(folder)) {
|
|
98
100
|
return 0;
|
|
99
101
|
}
|
|
100
102
|
let total = 0;
|
|
101
103
|
for (const entry of readdirSync(folder, { withFileTypes: true })) {
|
|
102
|
-
if (!entry.isFile() || !entry.name
|
|
104
|
+
if (!entry.isFile() || !hasArtifactSuffix(entry.name)) {
|
|
103
105
|
continue;
|
|
104
106
|
}
|
|
105
107
|
try {
|
|
@@ -124,7 +126,7 @@ export function countFilesystemInFlight(projectRoot) {
|
|
|
124
126
|
}
|
|
125
127
|
/** True iff `plan.policy.triageScope` is a non-empty list of dict rules. */
|
|
126
128
|
export function isTriageScopeExplicitlyConfigured(projectRoot) {
|
|
127
|
-
const path =
|
|
129
|
+
const path = resolveProjectDefinitionPath(projectRoot);
|
|
128
130
|
if (!existsSync(path)) {
|
|
129
131
|
return false;
|
|
130
132
|
}
|
|
@@ -137,7 +139,7 @@ export function isTriageScopeExplicitlyConfigured(projectRoot) {
|
|
|
137
139
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
138
140
|
return false;
|
|
139
141
|
}
|
|
140
|
-
const policy = plan
|
|
142
|
+
const policy = readPlanPolicy(plan);
|
|
141
143
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
142
144
|
return false;
|
|
143
145
|
}
|
|
@@ -187,7 +189,7 @@ export function isCachedIssueOpen(cacheRoot, repo, issueNumber) {
|
|
|
187
189
|
export function computeSummary(projectRoot, options = {}) {
|
|
188
190
|
const root = pathResolve(projectRoot);
|
|
189
191
|
const resolvedCacheRoot = options.cacheRoot ?? join(root, CACHE_DIR_NAME);
|
|
190
|
-
const resolvedLogPath = options.auditLogPath ??
|
|
192
|
+
const resolvedLogPath = options.auditLogPath ?? resolveEvalPath(root, "candidates.jsonl");
|
|
191
193
|
const cached = iterCachedIssues(resolvedCacheRoot);
|
|
192
194
|
const repos = [...new Set(cached.map(([repo]) => repo))].sort();
|
|
193
195
|
const wipCap = resolveWipCapInt(root);
|
|
@@ -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
|