@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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespaced directive-config keys on a vBRIEF `plan` object (#1650 / #2034).
|
|
3
|
+
*
|
|
4
|
+
* "Category B" keys (`policy`, `completedNote`) are directive's OWN engine
|
|
5
|
+
* config -- not generic xbrief/consumer-extension data -- so they live under
|
|
6
|
+
* the `x-directive/` extension prefix that the conformance gate already
|
|
7
|
+
* accepts (#1620 EXTENSION_PREFIXES). Once the corpus is namespaced the
|
|
8
|
+
* conformance allow-list is removed, so any future bare `plan.policy` /
|
|
9
|
+
* `plan.completedNote` fails the gate.
|
|
10
|
+
*
|
|
11
|
+
* Read accessors accept the legacy bare key as a back-compat fallback so an
|
|
12
|
+
* un-migrated consumer PROJECT-DEFINITION keeps resolving while the operator
|
|
13
|
+
* migrates; writers always emit (and fold a legacy block into) the namespaced
|
|
14
|
+
* key.
|
|
15
|
+
*/
|
|
16
|
+
/** Namespaced directive policy block key. */
|
|
17
|
+
export const PLAN_POLICY_KEY = "x-directive/policy";
|
|
18
|
+
/** Namespaced directive scope-completion note key. */
|
|
19
|
+
export const PLAN_COMPLETED_NOTE_KEY = "x-directive/completedNote";
|
|
20
|
+
/** Legacy bare policy key (pre-#1650); read-side fallback / write-side migration source. */
|
|
21
|
+
export const LEGACY_PLAN_POLICY_KEY = "policy";
|
|
22
|
+
/** Legacy bare completedNote key (pre-#1650); read-side fallback / write-side migration source. */
|
|
23
|
+
export const LEGACY_PLAN_COMPLETED_NOTE_KEY = "completedNote";
|
|
24
|
+
function asPlanObject(plan) {
|
|
25
|
+
return typeof plan === "object" && plan !== null && !Array.isArray(plan)
|
|
26
|
+
? plan
|
|
27
|
+
: null;
|
|
28
|
+
}
|
|
29
|
+
function readPlanExtension(plan, namespacedKey, legacyKey) {
|
|
30
|
+
const planObj = asPlanObject(plan);
|
|
31
|
+
if (planObj === null) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const namespaced = planObj[namespacedKey];
|
|
35
|
+
return namespaced !== undefined ? namespaced : planObj[legacyKey];
|
|
36
|
+
}
|
|
37
|
+
/** Read the directive policy block from a plan (namespaced first, bare fallback). */
|
|
38
|
+
export function readPlanPolicy(plan) {
|
|
39
|
+
return readPlanExtension(plan, PLAN_POLICY_KEY, LEGACY_PLAN_POLICY_KEY);
|
|
40
|
+
}
|
|
41
|
+
/** Read the directive completedNote from a plan (namespaced first, bare fallback). */
|
|
42
|
+
export function readPlanCompletedNote(plan) {
|
|
43
|
+
return readPlanExtension(plan, PLAN_COMPLETED_NOTE_KEY, LEGACY_PLAN_COMPLETED_NOTE_KEY);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Rename a legacy bare `policy` key to the namespaced key in place when only
|
|
47
|
+
* the bare key is present. No-op when the namespaced key already exists or no
|
|
48
|
+
* policy key is present. Used by writers so a mutation never strands a legacy
|
|
49
|
+
* bare block alongside the namespaced one.
|
|
50
|
+
*/
|
|
51
|
+
export function migrateLegacyPolicyKey(plan) {
|
|
52
|
+
if (plan[PLAN_POLICY_KEY] === undefined && plan[LEGACY_PLAN_POLICY_KEY] !== undefined) {
|
|
53
|
+
plan[PLAN_POLICY_KEY] = plan[LEGACY_PLAN_POLICY_KEY];
|
|
54
|
+
delete plan[LEGACY_PLAN_POLICY_KEY];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=plan-extensions.js.map
|
package/dist/policy/resolve.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Filesystem-relative location of the project-definition vBRIEF. */
|
|
1
|
+
/** Filesystem-relative location of the project-definition vBRIEF (display/back-compat). */
|
|
2
2
|
export declare const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
3
3
|
/** Environment variable emergency bypass for branch protection (#747). */
|
|
4
4
|
export declare const ENV_BYPASS = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
|
|
@@ -13,7 +13,11 @@ export interface PolicyResult {
|
|
|
13
13
|
readonly deprecationWarning: string | null;
|
|
14
14
|
readonly error: string | null;
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Resolve absolute path to PROJECT-DEFINITION. Layout-aware (#2109 part 2a):
|
|
18
|
+
* resolves PROJECT-DEFINITION.xbrief.json when the xbrief layout is present,
|
|
19
|
+
* else PROJECT-DEFINITION.vbrief.json (unchanged on today's tree).
|
|
20
|
+
*/
|
|
17
21
|
export declare function projectDefinitionPath(projectRoot: string): string;
|
|
18
22
|
/** Best-effort coerce a legacy narrative value to a boolean. */
|
|
19
23
|
export declare function coerceLegacyNarrative(value: unknown): {
|
package/dist/policy/resolve.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve as pathResolve } from "node:path";
|
|
3
|
-
|
|
3
|
+
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
4
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
5
|
+
/** Filesystem-relative location of the project-definition vBRIEF (display/back-compat). */
|
|
4
6
|
export const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
5
7
|
/** Environment variable emergency bypass for branch protection (#747). */
|
|
6
8
|
export const ENV_BYPASS = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
|
|
@@ -9,9 +11,13 @@ export const LEGACY_NARRATIVE_KEY = "Allow direct commits to master";
|
|
|
9
11
|
/** Audit log relative path (#746). */
|
|
10
12
|
export const AUDIT_LOG_REL_PATH = "meta/policy-changes.log";
|
|
11
13
|
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
12
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Resolve absolute path to PROJECT-DEFINITION. Layout-aware (#2109 part 2a):
|
|
16
|
+
* resolves PROJECT-DEFINITION.xbrief.json when the xbrief layout is present,
|
|
17
|
+
* else PROJECT-DEFINITION.vbrief.json (unchanged on today's tree).
|
|
18
|
+
*/
|
|
13
19
|
export function projectDefinitionPath(projectRoot) {
|
|
14
|
-
return
|
|
20
|
+
return resolveProjectDefinitionPath(pathResolve(projectRoot));
|
|
15
21
|
}
|
|
16
22
|
function envBypassActive() {
|
|
17
23
|
const raw = process.env[ENV_BYPASS] ?? "";
|
|
@@ -109,7 +115,7 @@ export function resolvePolicy(projectRoot) {
|
|
|
109
115
|
};
|
|
110
116
|
}
|
|
111
117
|
const planObj = plan;
|
|
112
|
-
const policyBlock = planObj
|
|
118
|
+
const policyBlock = readPlanPolicy(planObj);
|
|
113
119
|
if (typeof policyBlock === "object" &&
|
|
114
120
|
policyBlock !== null &&
|
|
115
121
|
!Array.isArray(policyBlock) &&
|
|
@@ -188,15 +194,19 @@ export function setPolicy(projectRoot, options) {
|
|
|
188
194
|
}
|
|
189
195
|
}
|
|
190
196
|
const plan = data.plan;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
197
|
+
migrateLegacyPolicyKey(plan);
|
|
198
|
+
const existingPolicy = plan[PLAN_POLICY_KEY];
|
|
199
|
+
if (typeof existingPolicy !== "object" ||
|
|
200
|
+
existingPolicy === null ||
|
|
201
|
+
Array.isArray(existingPolicy)) {
|
|
202
|
+
if (existingPolicy === undefined) {
|
|
203
|
+
plan[PLAN_POLICY_KEY] = {};
|
|
194
204
|
}
|
|
195
205
|
else {
|
|
196
206
|
throw new Error("plan.policy is not an object");
|
|
197
207
|
}
|
|
198
208
|
}
|
|
199
|
-
const policyBlock = plan
|
|
209
|
+
const policyBlock = plan[PLAN_POLICY_KEY];
|
|
200
210
|
const previous = policyBlock.allowDirectCommitsToMaster;
|
|
201
211
|
policyBlock.allowDirectCommitsToMaster = Boolean(allowDirectCommits);
|
|
202
212
|
let legacyDropped = false;
|
package/dist/policy/wip.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
-
import { join
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
4
|
+
import { readPlanPolicy } from "./plan-extensions.js";
|
|
3
5
|
import { loadProjectDefinition } from "./resolve.js";
|
|
4
6
|
/** Framework default WIP cap (#1124 / umbrella #1119). */
|
|
5
7
|
export const DEFAULT_WIP_CAP = 10;
|
|
@@ -43,7 +45,7 @@ export function resolveWipCap(projectRoot) {
|
|
|
43
45
|
error: "PROJECT-DEFINITION 'plan' is not an object",
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
|
-
const policyBlock = plan
|
|
48
|
+
const policyBlock = readPlanPolicy(plan);
|
|
47
49
|
if (typeof policyBlock !== "object" ||
|
|
48
50
|
policyBlock === null ||
|
|
49
51
|
Array.isArray(policyBlock) ||
|
|
@@ -63,7 +65,7 @@ export function resolveWipCap(projectRoot) {
|
|
|
63
65
|
/** Count *.vbrief.json files in vbrief/pending/ + vbrief/active/ (#1124). */
|
|
64
66
|
export function countVbriefWip(projectRoot) {
|
|
65
67
|
let total = 0;
|
|
66
|
-
const vbriefRoot =
|
|
68
|
+
const vbriefRoot = resolveLifecycleRoot(projectRoot);
|
|
67
69
|
for (const sub of WIP_LIFECYCLE_DIRS) {
|
|
68
70
|
const folder = join(vbriefRoot, sub);
|
|
69
71
|
if (!existsSync(folder)) {
|
|
@@ -71,7 +73,7 @@ export function countVbriefWip(projectRoot) {
|
|
|
71
73
|
}
|
|
72
74
|
const entries = readdirSync(folder, { withFileTypes: true });
|
|
73
75
|
for (const entry of entries) {
|
|
74
|
-
if (entry.isFile() && entry.name
|
|
76
|
+
if (entry.isFile() && hasArtifactSuffix(entry.name)) {
|
|
75
77
|
total += 1;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
@@ -14,6 +14,8 @@ import { execFileSync } from "node:child_process";
|
|
|
14
14
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
15
15
|
import { join, relative } from "node:path";
|
|
16
16
|
import { probeCacheDrift } from "../cache/fetch.js";
|
|
17
|
+
import { resolveEvalPath, resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
18
|
+
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
17
19
|
import { latestDecisionForIssue as auditLatestDecisionForIssue } from "../triage/actions/candidates-log.js";
|
|
18
20
|
// ---------------------------------------------------------------------------
|
|
19
21
|
// Public constants (mirror preflight_cache.py)
|
|
@@ -176,7 +178,7 @@ function metaEntryIsTerminalClosed(metaPath) {
|
|
|
176
178
|
return false;
|
|
177
179
|
}
|
|
178
180
|
function loadScopeRules(projectRoot) {
|
|
179
|
-
const defPath =
|
|
181
|
+
const defPath = resolveProjectDefinitionPath(projectRoot);
|
|
180
182
|
if (!existsSync(defPath))
|
|
181
183
|
return null;
|
|
182
184
|
try {
|
|
@@ -186,7 +188,7 @@ function loadScopeRules(projectRoot) {
|
|
|
186
188
|
const plan = data.plan;
|
|
187
189
|
if (plan === null || typeof plan !== "object" || Array.isArray(plan))
|
|
188
190
|
return null;
|
|
189
|
-
const policy = plan
|
|
191
|
+
const policy = readPlanPolicy(plan);
|
|
190
192
|
if (policy === null || typeof policy !== "object" || Array.isArray(policy))
|
|
191
193
|
return null;
|
|
192
194
|
const scope = policy.triageScope;
|
|
@@ -325,7 +327,8 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
325
327
|
(envMaxAgeParsed !== null && !Number.isNaN(envMaxAgeParsed) ? envMaxAgeParsed : null) ??
|
|
326
328
|
DEFAULT_MAX_AGE_HOURS;
|
|
327
329
|
const cacheRoot = join(projectRoot, CACHE_DIR_NAME);
|
|
328
|
-
|
|
330
|
+
// Layout-aware (#2109 part 2a): resolve under xbrief/.eval when migrated.
|
|
331
|
+
const candidatesPath = resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
329
332
|
// Step 1: Resolve repo slug
|
|
330
333
|
const resolvedRepo = resolveRepo(projectRoot, cacheRoot, source, options.repo ?? null);
|
|
331
334
|
// Step 2: Check cache dir exists
|
|
@@ -357,7 +360,7 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
357
360
|
return {
|
|
358
361
|
code: 2,
|
|
359
362
|
message: [
|
|
360
|
-
`❌ deft cache-fresh: ${
|
|
363
|
+
`❌ deft cache-fresh: ${candidatesPath} not found.`,
|
|
361
364
|
REMEDIATION_NO_CANDIDATES,
|
|
362
365
|
].join("\n"),
|
|
363
366
|
};
|
|
@@ -511,7 +514,7 @@ function evaluateForIssue(repo, issueNumber, candidatesPath, cacheRoot, source,
|
|
|
511
514
|
return {
|
|
512
515
|
code: 1,
|
|
513
516
|
message: [
|
|
514
|
-
`❌ deft cache-fresh: issue #${issueNumber} has no triage decision in ${
|
|
517
|
+
`❌ deft cache-fresh: issue #${issueNumber} has no triage decision in ${candidatesPath}.`,
|
|
515
518
|
` Recovery: \`deft triage:accept -- --repo ${repo} --issue ${issueNumber}\` before dispatching an implementation agent.`,
|
|
516
519
|
].join("\n"),
|
|
517
520
|
};
|
|
@@ -6,11 +6,12 @@ import { spawnSync } from "node:child_process";
|
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { fetchIssueStates, isTerminalLifecyclePath, reconcile, scanVbriefDir, } from "../intake/reconcile-issues.js";
|
|
9
|
+
import { resolveLifecycleFolder, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
9
10
|
import { renderRoadmap } from "../render/roadmap-render.js";
|
|
10
11
|
const BUILD_DIST_RUNNER = join(dirname(fileURLToPath(import.meta.url)), "build-dist-runner.js");
|
|
11
12
|
export function refreshRoadmapNative(projectRoot) {
|
|
12
|
-
const pending =
|
|
13
|
-
const completed =
|
|
13
|
+
const pending = resolveLifecycleFolder(projectRoot, "pending");
|
|
14
|
+
const completed = resolveLifecycleFolder(projectRoot, "completed");
|
|
14
15
|
const roadmap = join(projectRoot, "ROADMAP.md");
|
|
15
16
|
const [ok, msg] = renderRoadmap(pending, roadmap, completed);
|
|
16
17
|
if (!ok) {
|
|
@@ -19,7 +20,7 @@ export function refreshRoadmapNative(projectRoot) {
|
|
|
19
20
|
return [true, "ROADMAP.md re-rendered"];
|
|
20
21
|
}
|
|
21
22
|
export function checkVbriefLifecycleSyncNative(projectRoot, repo) {
|
|
22
|
-
const vbriefDir =
|
|
23
|
+
const vbriefDir = resolveLifecycleRoot(projectRoot);
|
|
23
24
|
try {
|
|
24
25
|
const issueToVbriefs = scanVbriefDir(vbriefDir);
|
|
25
26
|
const issueStateMap = fetchIssueStates(repo, new Set(issueToVbriefs.keys()), {
|
|
@@ -3,6 +3,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { cmdDoctor } from "../doctor/main.js";
|
|
6
|
+
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
6
7
|
function spec(name, entrypoint, opts = {}) {
|
|
7
8
|
return { name, entrypoint, ...opts };
|
|
8
9
|
}
|
|
@@ -232,7 +233,7 @@ function argvForSpec(commandSpec, argv, projectRoot, frameworkRoot) {
|
|
|
232
233
|
if (commandSpec.frameworkRootArg)
|
|
233
234
|
resolved.push(commandSpec.frameworkRootArg, frameworkRoot);
|
|
234
235
|
if (commandSpec.vbriefDirArg)
|
|
235
|
-
resolved.push(commandSpec.vbriefDirArg,
|
|
236
|
+
resolved.push(commandSpec.vbriefDirArg, resolveLifecycleRoot(projectRoot));
|
|
236
237
|
if (commandSpec.rootArg)
|
|
237
238
|
resolved.push(commandSpec.rootArg, frameworkRoot);
|
|
238
239
|
resolved.push(...normalizeTaskSeparator(argv));
|
|
@@ -52,8 +52,8 @@ export function renderPrd(specPath, outputPath, options = {}) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
lines.push("---\n" +
|
|
55
|
-
"*This document is auto-generated from
|
|
56
|
-
"via `task prd:render`. Do not edit directly.*\n");
|
|
55
|
+
"*This document is auto-generated from the specification artifact " +
|
|
56
|
+
"(e.g. `xbrief/specification.xbrief.json`) via `task prd:render`. Do not edit directly.*\n");
|
|
57
57
|
writeFileSync(outputPath, lines.join("\n"), "utf8");
|
|
58
58
|
process.stdout.write(`PRD.md written to ${outputPath}\n`);
|
|
59
59
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
3
4
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
4
5
|
import { deriveRegistryItemStatus, registryMetadataReferencesFromScope, } from "../vbrief-validate/registry-status.js";
|
|
5
6
|
import { PROJECT_LIFECYCLE_FOLDERS, SKELETON_NARRATIVES } from "./constants.js";
|
|
@@ -13,7 +14,7 @@ export function scanLifecycleFolders(vbriefDir) {
|
|
|
13
14
|
let files;
|
|
14
15
|
try {
|
|
15
16
|
files = readdirSync(folder)
|
|
16
|
-
.filter((n) => n
|
|
17
|
+
.filter((n) => hasArtifactSuffix(n))
|
|
17
18
|
.sort();
|
|
18
19
|
}
|
|
19
20
|
catch {
|
|
@@ -260,11 +261,28 @@ export function acknowledgeProjectDefinitionStaleness(vbriefDir, options = {}) {
|
|
|
260
261
|
export function main(argv) {
|
|
261
262
|
const acknowledge = argv[0] === "--acknowledge-staleness";
|
|
262
263
|
const rest = acknowledge ? argv.slice(1) : argv;
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
let projectRoot;
|
|
265
|
+
const positional = [];
|
|
266
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
267
|
+
const arg = rest[i];
|
|
268
|
+
if (arg === "--project-root") {
|
|
269
|
+
projectRoot = rest[i + 1];
|
|
270
|
+
i += 1;
|
|
271
|
+
}
|
|
272
|
+
else if (arg.startsWith("--project-root=")) {
|
|
273
|
+
projectRoot = arg.slice("--project-root=".length);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
positional.push(arg);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (positional.length > 1) {
|
|
280
|
+
process.stderr.write("Usage: project-render [--acknowledge-staleness] [--project-root <dir>] [vbrief_dir]\n");
|
|
265
281
|
return 2;
|
|
266
282
|
}
|
|
267
|
-
const vbriefDir =
|
|
283
|
+
const vbriefDir = projectRoot !== undefined
|
|
284
|
+
? resolveLifecycleRoot(resolve(projectRoot))
|
|
285
|
+
: (positional[0] ?? "vbrief");
|
|
268
286
|
const [ok, message] = acknowledge
|
|
269
287
|
? acknowledgeProjectDefinitionStaleness(vbriefDir)
|
|
270
288
|
: renderProjectDefinition(vbriefDir);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
3
4
|
import { MIGRATOR_METADATA_KEY, ROADMAP_BANNER } from "./constants.js";
|
|
4
5
|
import { phaseSortKey } from "./text-utils.js";
|
|
5
6
|
function scopeMetadataRank(plan) {
|
|
@@ -32,7 +33,7 @@ function loadVbriefs(folder) {
|
|
|
32
33
|
let files;
|
|
33
34
|
try {
|
|
34
35
|
files = readdirSync(folder)
|
|
35
|
-
.filter((n) => n
|
|
36
|
+
.filter((n) => hasArtifactSuffix(n))
|
|
36
37
|
.sort();
|
|
37
38
|
}
|
|
38
39
|
catch {
|
|
@@ -363,10 +364,10 @@ export function renderRoadmap(pendingDir, outPath, completedDir) {
|
|
|
363
364
|
export function checkDrift(pendingDir, roadmapPath, completedDir) {
|
|
364
365
|
const expected = renderRoadmapToBuffer(pendingDir, completedDir);
|
|
365
366
|
if (!existsSync(roadmapPath)) {
|
|
366
|
-
const hasPending = existsSync(pendingDir) && readdirSync(pendingDir).some((n) => n
|
|
367
|
+
const hasPending = existsSync(pendingDir) && readdirSync(pendingDir).some((n) => hasArtifactSuffix(n));
|
|
367
368
|
const inferredCompleted = resolveCompletedDir(pendingDir, completedDir);
|
|
368
369
|
const hasCompleted = existsSync(inferredCompleted) &&
|
|
369
|
-
readdirSync(inferredCompleted).some((n) => n
|
|
370
|
+
readdirSync(inferredCompleted).some((n) => hasArtifactSuffix(n));
|
|
370
371
|
if (!hasPending && !hasCompleted) {
|
|
371
372
|
return [true, "✓ No ROADMAP.md needed (no pending or completed vBRIEFs)"];
|
|
372
373
|
}
|
|
@@ -379,10 +380,37 @@ export function checkDrift(pendingDir, roadmapPath, completedDir) {
|
|
|
379
380
|
}
|
|
380
381
|
/** CLI entry (mirrors ``scripts/roadmap_render.main``). */
|
|
381
382
|
export function main(argv) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
|
|
383
|
+
let projectRoot;
|
|
384
|
+
let check = false;
|
|
385
|
+
const positional = [];
|
|
386
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
387
|
+
const arg = argv[i];
|
|
388
|
+
if (arg === "--project-root") {
|
|
389
|
+
projectRoot = argv[i + 1];
|
|
390
|
+
i += 1;
|
|
391
|
+
}
|
|
392
|
+
else if (arg.startsWith("--project-root=")) {
|
|
393
|
+
projectRoot = arg.slice("--project-root=".length);
|
|
394
|
+
}
|
|
395
|
+
else if (arg === "--check") {
|
|
396
|
+
check = true;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
positional.push(arg);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
let pendingDir;
|
|
403
|
+
let outPath;
|
|
404
|
+
if (projectRoot !== undefined) {
|
|
405
|
+
const lifecycleRoot = resolveLifecycleRoot(resolve(projectRoot));
|
|
406
|
+
pendingDir = join(lifecycleRoot, "pending");
|
|
407
|
+
outPath = positional[0] ?? join(resolve(projectRoot), "ROADMAP.md");
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
pendingDir = positional[0] ?? join(process.cwd(), "vbrief", "pending");
|
|
411
|
+
outPath = positional[1] ?? join(process.cwd(), "ROADMAP.md");
|
|
412
|
+
}
|
|
413
|
+
if (check) {
|
|
386
414
|
const [ok, msg] = checkDrift(pendingDir, outPath);
|
|
387
415
|
process.stdout.write(`${msg}\n`);
|
|
388
416
|
return ok ? 0 : 1;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, stripArtifactSuffix } from "../layout/resolve.js";
|
|
3
4
|
import { PROPOSED_DISCLAIMER_LINES, PROPOSED_STATUS_FILTER } from "../spec-authority/constants.js";
|
|
4
5
|
import { SCOPE_SUMMARY_NARRATIVES } from "./constants.js";
|
|
5
6
|
function readEdgeEndpoints(edge) {
|
|
@@ -14,7 +15,7 @@ function loadScopeVbriefs(folder) {
|
|
|
14
15
|
let entries;
|
|
15
16
|
try {
|
|
16
17
|
entries = readdirSync(folder)
|
|
17
|
-
.filter((n) => n
|
|
18
|
+
.filter((n) => hasArtifactSuffix(n))
|
|
18
19
|
.sort();
|
|
19
20
|
}
|
|
20
21
|
catch {
|
|
@@ -24,7 +25,7 @@ function loadScopeVbriefs(folder) {
|
|
|
24
25
|
for (const name of entries) {
|
|
25
26
|
try {
|
|
26
27
|
const data = JSON.parse(readFileSync(join(folder, name), "utf8"));
|
|
27
|
-
const stem =
|
|
28
|
+
const stem = stripArtifactSuffix(name);
|
|
28
29
|
out.push([stem, data]);
|
|
29
30
|
}
|
|
30
31
|
catch {
|
package/dist/scope/audit-log.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync, writeSync, } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveEvalPath } from "../layout/resolve.js";
|
|
5
5
|
import { utcNowIso } from "./vbrief-json.js";
|
|
6
6
|
export class ScopeAuditLogError extends Error {
|
|
7
7
|
constructor(message) {
|
|
@@ -94,7 +94,8 @@ export function newDecisionId() {
|
|
|
94
94
|
return randomUUID();
|
|
95
95
|
}
|
|
96
96
|
export function canonicalLogPath(projectRoot) {
|
|
97
|
-
|
|
97
|
+
// Layout-aware (#2109 part 2a): xbrief/.eval when migrated, else vbrief/.eval.
|
|
98
|
+
return resolveEvalPath(resolve(projectRoot), "scope-lifecycle.jsonl");
|
|
98
99
|
}
|
|
99
100
|
function withAppendLock(logPath, fn) {
|
|
100
101
|
const lockPath = `${logPath}.lock`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
3
4
|
import { PROJECT_DEFINITION_REL_PATH } from "../policy/resolve.js";
|
|
4
5
|
function resolveDefaultCapacityBucket(projectRoot) {
|
|
5
6
|
try {
|
|
@@ -10,7 +11,7 @@ function resolveDefaultCapacityBucket(projectRoot) {
|
|
|
10
11
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
11
12
|
return "";
|
|
12
13
|
}
|
|
13
|
-
const policy = plan
|
|
14
|
+
const policy = readPlanPolicy(plan);
|
|
14
15
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
15
16
|
return "";
|
|
16
17
|
}
|
package/dist/scope/decompose.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { accessSync, constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
11
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
12
|
+
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
11
13
|
import { referenceWithDefaultTrust, slugify } from "../vbrief-build/build.js";
|
|
12
14
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
13
15
|
import { formatVbriefJson } from "./vbrief-json.js";
|
|
@@ -398,7 +400,7 @@ export function storyQualityIssues(opts) {
|
|
|
398
400
|
// Path helpers
|
|
399
401
|
// ---------------------------------------------------------------------------
|
|
400
402
|
function vbriefDir(projectRoot) {
|
|
401
|
-
return
|
|
403
|
+
return resolveLifecycleRoot(projectRoot);
|
|
402
404
|
}
|
|
403
405
|
function relToVbrief(vbriefDirPath, path) {
|
|
404
406
|
const resolvedPath = resolve(path);
|
|
@@ -507,7 +509,7 @@ function storyHasTraces(story, items, sw) {
|
|
|
507
509
|
if (Array.isArray(refs)) {
|
|
508
510
|
for (const ref of refs) {
|
|
509
511
|
if (typeof ref === "object" && ref !== null && !Array.isArray(ref)) {
|
|
510
|
-
if (ref.type
|
|
512
|
+
if (referenceTypeMatches(String(ref.type ?? ""), "spec-section"))
|
|
511
513
|
return true;
|
|
512
514
|
}
|
|
513
515
|
}
|
|
@@ -752,7 +754,7 @@ function storyNarratives(story) {
|
|
|
752
754
|
}
|
|
753
755
|
function childFilename(story, stId, title, date) {
|
|
754
756
|
const fn = story.filename;
|
|
755
|
-
if (typeof fn === "string" && fn
|
|
757
|
+
if (typeof fn === "string" && hasArtifactSuffix(fn))
|
|
756
758
|
return fn;
|
|
757
759
|
const sl = slugify(title) || slugify(stId) || "story";
|
|
758
760
|
return `${date}-${sl}.vbrief.json`;
|
|
@@ -891,7 +893,7 @@ export function applyDecomposition(opts) {
|
|
|
891
893
|
for (const { target, title } of childPaths) {
|
|
892
894
|
references.push(referenceWithDefaultTrust({
|
|
893
895
|
uri: relToVbrief(vbriefDirPath, target),
|
|
894
|
-
type: "x-
|
|
896
|
+
type: "x-xbrief/plan",
|
|
895
897
|
title,
|
|
896
898
|
}));
|
|
897
899
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, relative, resolve } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
3
4
|
import { formatVbriefJson } from "./vbrief-json.js";
|
|
4
5
|
import { collectChildUris, collectPlanRefs, resolveVbriefRef } from "./vbrief-ref.js";
|
|
5
6
|
function rewriteOnePlanRef(value, oldParentResolved, newParentRel, vbriefDir) {
|
|
@@ -36,7 +37,7 @@ function rewriteParentChildReference(parentPath, oldChildResolved, newChildRel,
|
|
|
36
37
|
continue;
|
|
37
38
|
}
|
|
38
39
|
const r = ref;
|
|
39
|
-
if (r.type
|
|
40
|
+
if (!referenceTypeMatches(String(r.type ?? ""), "plan")) {
|
|
40
41
|
continue;
|
|
41
42
|
}
|
|
42
43
|
const resolved = resolveVbriefRef(r.uri, vbriefDir);
|
package/dist/scope/demote.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleFolder } from "../layout/resolve.js";
|
|
3
4
|
import { stripTrailingPathSeparators } from "../text/redos-safe.js";
|
|
4
5
|
import { append, canonicalLogPath, latestForPath, newDecisionId } from "./audit-log.js";
|
|
5
6
|
import { resolveProjectRoot } from "./project-context.js";
|
|
@@ -35,10 +36,10 @@ export function demoteOne(filePath, projectRoot, reason, options = {}) {
|
|
|
35
36
|
return { ok: false, message: `File not found: ${resolved}`, auditEntry: null };
|
|
36
37
|
}
|
|
37
38
|
const basename = resolved.split(/[/\\]/).pop() ?? "";
|
|
38
|
-
if (!basename
|
|
39
|
+
if (!hasArtifactSuffix(basename)) {
|
|
39
40
|
return {
|
|
40
41
|
ok: false,
|
|
41
|
-
message: `Not a vBRIEF file (expected .vbrief.json): ${basename}`,
|
|
42
|
+
message: `Not a vBRIEF file (expected .vbrief.json or .xbrief.json): ${basename}`,
|
|
42
43
|
auditEntry: null,
|
|
43
44
|
};
|
|
44
45
|
}
|
|
@@ -114,7 +115,7 @@ export function batchDemote(projectRoot, olderThanDays, options = {}) {
|
|
|
114
115
|
throw new Error(`--older-than-days must be >= 0, got ${olderThanDays}`);
|
|
115
116
|
}
|
|
116
117
|
const now = options.now ?? new Date();
|
|
117
|
-
const pendingDir =
|
|
118
|
+
const pendingDir = resolveLifecycleFolder(projectRoot, SOURCE_FOLDER);
|
|
118
119
|
if (!existsSync(pendingDir)) {
|
|
119
120
|
return [0, [], []];
|
|
120
121
|
}
|
|
@@ -124,7 +125,7 @@ export function batchDemote(projectRoot, olderThanDays, options = {}) {
|
|
|
124
125
|
const skipped = [];
|
|
125
126
|
let demoted = 0;
|
|
126
127
|
const files = readdirSync(pendingDir)
|
|
127
|
-
.filter((name) => name
|
|
128
|
+
.filter((name) => hasArtifactSuffix(name))
|
|
128
129
|
.sort();
|
|
129
130
|
for (const name of files) {
|
|
130
131
|
const candidate = join(pendingDir, name);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
|
|
3
|
+
// Layout-aware (#2109 part 1): an xbrief-only tree is also a valid project root.
|
|
4
|
+
const PROJECT_ROOT_SENTINELS = ["xbrief", "vbrief", ".git"];
|
|
4
5
|
function isProjectRoot(candidate) {
|
|
5
6
|
return PROJECT_ROOT_SENTINELS.some((sentinel) => existsSync(join(candidate, sentinel)));
|
|
6
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
3
4
|
import { PROJECT_DEFINITION_REL_PATH } from "../policy/resolve.js";
|
|
4
5
|
import { formatVbriefJson } from "./vbrief-json.js";
|
|
5
6
|
import { relativeToVbrief, resolveVbriefRef, scopeIdsForFilename } from "./vbrief-ref.js";
|
|
@@ -8,7 +9,7 @@ function rewriteProjectDefinitionPlanReference(ref, oldResolved, newRel, vbriefR
|
|
|
8
9
|
return false;
|
|
9
10
|
}
|
|
10
11
|
const r = ref;
|
|
11
|
-
if (r.type
|
|
12
|
+
if (!referenceTypeMatches(String(r.type ?? ""), "plan")) {
|
|
12
13
|
return false;
|
|
13
14
|
}
|
|
14
15
|
const resolved = resolveVbriefRef(r.uri, vbriefRoot);
|
|
@@ -41,7 +42,7 @@ function projectItemReferencesScope(item, oldResolved, newResolved, vbriefRoot)
|
|
|
41
42
|
continue;
|
|
42
43
|
}
|
|
43
44
|
const r = ref;
|
|
44
|
-
if (r.type
|
|
45
|
+
if (!referenceTypeMatches(String(r.type ?? ""), "plan")) {
|
|
45
46
|
continue;
|
|
46
47
|
}
|
|
47
48
|
const resolved = resolveVbriefRef(r.uri, vbriefRoot);
|
|
@@ -58,7 +59,7 @@ function projectItemReferencesScope(item, oldResolved, newResolved, vbriefRoot)
|
|
|
58
59
|
continue;
|
|
59
60
|
}
|
|
60
61
|
const r = ref;
|
|
61
|
-
if (r.type
|
|
62
|
+
if (!referenceTypeMatches(String(r.type ?? ""), "plan")) {
|
|
62
63
|
continue;
|
|
63
64
|
}
|
|
64
65
|
const resolved = resolveVbriefRef(r.uri, vbriefRoot);
|
package/dist/scope/transition.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
3
4
|
import { append, canonicalLogPath, newDecisionId } from "./audit-log.js";
|
|
4
5
|
import { stampCompletionMetadata } from "./capacity-stamp.js";
|
|
5
6
|
import { LIFECYCLE_FOLDERS, MOVE_LABELS, STATUS_PRECONDITIONS, STAY_LABELS, TRANSITIONS, } from "./constants.js";
|
|
@@ -17,8 +18,11 @@ export function runTransition(action, filePath, now = new Date()) {
|
|
|
17
18
|
return { ok: false, message: `File not found: ${resolvedPath}` };
|
|
18
19
|
}
|
|
19
20
|
const basename = resolvedPath.split(/[/\\]/).pop() ?? "";
|
|
20
|
-
if (!basename
|
|
21
|
-
return {
|
|
21
|
+
if (!hasArtifactSuffix(basename)) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
message: `Not a vBRIEF file (expected .vbrief.json or .xbrief.json): ${basename}`,
|
|
25
|
+
};
|
|
22
26
|
}
|
|
23
27
|
const currentFolder = detectLifecycleFolder(resolvedPath);
|
|
24
28
|
if (currentFolder === null) {
|
package/dist/scope/undo.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, join, relative, resolve } from "node:path";
|
|
3
|
+
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
3
4
|
import { append, canonicalLogPath, newDecisionId, readAll } from "./audit-log.js";
|
|
4
5
|
import { REVERSIBLE_ACTIONS, TERMINAL_ACTIONS } from "./constants.js";
|
|
5
6
|
import { formatVbriefJson, utcNowIso } from "./vbrief-json.js";
|
|
6
7
|
function vbriefRoot(projectRoot) {
|
|
7
|
-
return
|
|
8
|
+
return resolveLifecycleRoot(projectRoot);
|
|
8
9
|
}
|
|
9
10
|
function absForEntryPath(projectRoot, vbriefPath) {
|
|
10
11
|
return resolve(projectRoot, vbriefPath);
|