@deftai/directive-core 0.72.0 → 0.73.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/branch/evaluate.js +2 -1
- package/dist/category-b-namespace/index.js +16 -2
- package/dist/codebase/default-extractor.js +9 -1
- package/dist/codebase/map.js +1 -1
- package/dist/deposit/copy-tree.js +31 -7
- package/dist/doctor/checks.d.ts +9 -0
- package/dist/doctor/checks.js +85 -5
- package/dist/doctor/main.js +2 -1
- package/dist/eval/health.js +7 -1
- package/dist/eval/readback.js +7 -1
- package/dist/init-deposit/gitignore.js +6 -0
- package/dist/init-deposit/hygiene.d.ts +30 -0
- package/dist/init-deposit/hygiene.js +60 -1
- package/dist/init-deposit/refresh.d.ts +16 -2
- package/dist/init-deposit/refresh.js +162 -52
- package/dist/init-deposit/scaffold.d.ts +11 -1
- package/dist/init-deposit/scaffold.js +9 -8
- package/dist/integration-e2e/helpers.js +7 -7
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.js +1 -1
- package/dist/layout/resolve.d.ts +47 -31
- package/dist/layout/resolve.js +93 -36
- package/dist/lifecycle/lifecycle-hygiene.d.ts +1 -1
- package/dist/lifecycle/lifecycle-hygiene.js +1 -1
- package/dist/migrate-preflight/index.js +10 -10
- package/dist/policy/disclosure.d.ts +1 -0
- package/dist/policy/disclosure.js +1 -0
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +1 -0
- package/dist/policy/policy-invocation.d.ts +5 -0
- package/dist/policy/policy-invocation.js +9 -0
- package/dist/policy/resolve.d.ts +2 -2
- package/dist/policy/resolve.js +15 -5
- package/dist/policy/value-feedback.js +6 -3
- package/dist/policy/wip.d.ts +1 -1
- package/dist/policy/wip.js +15 -3
- package/dist/preflight-cache/evaluate.js +1 -1
- package/dist/release/native-steps.js +14 -2
- package/dist/render/export-spec.js +2 -2
- package/dist/render/project-render.js +40 -9
- package/dist/scope/decompose.js +12 -2
- package/dist/session/resume-conditions.js +2 -2
- package/dist/session/ritual-sentinel.d.ts +1 -1
- package/dist/session/ritual-sentinel.js +6 -4
- package/dist/slice/constants.d.ts +1 -1
- package/dist/slice/constants.js +1 -1
- package/dist/slice/project-context.js +1 -1
- package/dist/spec-authority/resolver.js +9 -2
- package/dist/swarm/complete-cohort.js +14 -1
- package/dist/swarm/constants.d.ts +1 -1
- package/dist/swarm/constants.js +2 -1
- package/dist/swarm/readiness.js +33 -3
- package/dist/swarm/subagent-backend.js +2 -1
- package/dist/task-surface/index.js +3 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +22 -10
- package/dist/triage/bootstrap/index.d.ts +1 -1
- package/dist/triage/bootstrap/index.js +9 -3
- package/dist/triage/cache-path.d.ts +1 -1
- package/dist/triage/cache-path.js +32 -6
- package/dist/triage/classify/index.d.ts +1 -1
- package/dist/triage/classify/index.js +11 -4
- package/dist/triage/help/registry-data.d.ts +2 -2
- package/dist/triage/help/registry-data.js +2 -2
- package/dist/triage/queue/constants.d.ts +4 -4
- package/dist/triage/queue/constants.js +4 -4
- package/dist/triage/queue/scope-walk.js +7 -1
- package/dist/triage/reconcile/reconcile.js +23 -3
- package/dist/triage/reconcile/types.d.ts +1 -1
- package/dist/triage/reconcile/types.js +1 -1
- package/dist/triage/refresh/refresh.js +9 -2
- package/dist/triage/scope/constants.d.ts +2 -2
- package/dist/triage/scope/constants.js +2 -2
- package/dist/triage/scope/renderers.js +7 -1
- package/dist/triage/smoketest/index.js +9 -6
- package/dist/triage/subscribe/index.d.ts +2 -2
- package/dist/triage/subscribe/index.js +2 -2
- package/dist/triage/summary/index.d.ts +2 -2
- package/dist/triage/summary/index.js +1 -1
- package/dist/triage/summary/reconcilable.d.ts +1 -1
- package/dist/triage/welcome/constants.d.ts +2 -2
- package/dist/triage/welcome/constants.js +2 -2
- package/dist/triage/welcome/onboard.js +8 -1
- package/dist/triage/welcome/prior-state.js +14 -2
- package/dist/triage/welcome/summary.js +14 -2
- package/dist/triage/welcome/writers.js +7 -1
- package/dist/ts-check-lane/run-lane.d.ts +4 -0
- package/dist/ts-check-lane/run-lane.js +20 -6
- package/dist/validate-content/validate-strategy-output.js +11 -1
- package/dist/value/feedback-file.js +3 -2
- package/dist/value/readback.js +3 -2
- package/dist/vbrief-build/parity-scenarios.js +3 -3
- package/dist/vbrief-reconcile/labels.js +16 -1
- package/dist/vbrief-reconcile/parity-scenarios.js +10 -10
- package/dist/vbrief-reconcile/umbrellas.js +16 -1
- package/dist/vbrief-validate/main.js +13 -1
- package/dist/vbrief-validate/precutover.js +33 -5
- package/dist/vbrief-validate/staleness.d.ts +1 -1
- package/dist/vbrief-validate/staleness.js +5 -2
- package/dist/verify-source/code-structure-validate.js +7 -1
- package/dist/xbrief-migrate/signpost.d.ts +4 -4
- package/dist/xbrief-migrate/signpost.js +11 -15
- package/package.json +3 -3
package/dist/layout/resolve.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX,
|
|
4
|
-
export { LEGACY_ARTIFACT_DIR,
|
|
3
|
+
import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, } from "../xbrief-migrate/constants.js";
|
|
4
|
+
export { LEGACY_ARTIFACT_DIR, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, };
|
|
5
5
|
/**
|
|
6
|
-
* Lifecycle directory names recognized by the layout-aware resolver
|
|
7
|
-
*
|
|
6
|
+
* Lifecycle directory names recognized by the layout-aware resolver and path
|
|
7
|
+
* validators. Includes both `xbrief` (canonical) and `vbrief` (legacy) so that
|
|
8
|
+
* path-validation helpers remain accurate during and after migration.
|
|
8
9
|
*/
|
|
9
10
|
export const LIFECYCLE_DIR_NAMES = [MIGRATED_ARTIFACT_DIR, LEGACY_ARTIFACT_DIR];
|
|
10
11
|
/**
|
|
11
|
-
* Artifact filename suffixes recognized by the layout-aware resolver
|
|
12
|
-
*
|
|
12
|
+
* Artifact filename suffixes recognized by the layout-aware resolver and path
|
|
13
|
+
* validators. Includes both `.xbrief.json` (canonical) and `.vbrief.json`
|
|
14
|
+
* (legacy) so that path-validation helpers remain accurate during and after
|
|
15
|
+
* migration.
|
|
13
16
|
*/
|
|
14
17
|
export const ARTIFACT_SUFFIXES = [MIGRATED_ARTIFACT_SUFFIX, LEGACY_ARTIFACT_SUFFIX];
|
|
15
18
|
function isDirectory(path) {
|
|
@@ -20,6 +23,25 @@ function isDirectory(path) {
|
|
|
20
23
|
return false;
|
|
21
24
|
}
|
|
22
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns the layout root for non-layout-sensitive helpers (#2112).
|
|
28
|
+
*
|
|
29
|
+
* - If a valid `xbrief/` layout exists, returns its root.
|
|
30
|
+
* - If only `vbrief/` exists (legacy-only), throws so the caller surfaces the migrate hint.
|
|
31
|
+
* - If neither exists (empty/new project), falls back to `<projectRoot>/xbrief/` canonical path.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveLayoutRootOrCanonical(projectRoot) {
|
|
34
|
+
try {
|
|
35
|
+
return resolveLifecycleRoot(projectRoot);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (isDirectory(join(projectRoot, LEGACY_ARTIFACT_DIR)) &&
|
|
39
|
+
!isDirectory(join(projectRoot, MIGRATED_ARTIFACT_DIR))) {
|
|
40
|
+
throw err; // Legacy-only project: operator must run deft migrate:xbrief.
|
|
41
|
+
}
|
|
42
|
+
return join(projectRoot, MIGRATED_ARTIFACT_DIR); // New/empty project; use canonical path.
|
|
43
|
+
}
|
|
44
|
+
}
|
|
23
45
|
/** True when `name` ends with any recognized artifact suffix (.xbrief.json or .vbrief.json). */
|
|
24
46
|
export function hasArtifactSuffix(name) {
|
|
25
47
|
return ARTIFACT_SUFFIXES.some((suffix) => name.endsWith(suffix));
|
|
@@ -37,8 +59,10 @@ export function stripArtifactSuffix(name) {
|
|
|
37
59
|
return name;
|
|
38
60
|
}
|
|
39
61
|
/**
|
|
40
|
-
* True when a POSIX-style path is a lifecycle artifact
|
|
41
|
-
*
|
|
62
|
+
* True when a POSIX-style path is a lifecycle artifact path carrying a
|
|
63
|
+
* recognized lifecycle directory prefix (`xbrief/` or `vbrief/`) and a
|
|
64
|
+
* recognized artifact suffix. The `vbrief/` prefix and `.vbrief.json` suffix
|
|
65
|
+
* remain recognized for path-validation purposes during and after migration.
|
|
42
66
|
*/
|
|
43
67
|
export function isLifecycleArtifactPath(posix) {
|
|
44
68
|
const underLifecycleDir = LIFECYCLE_DIR_NAMES.some((dir) => posix.startsWith(`${dir}/`));
|
|
@@ -64,12 +88,16 @@ function containsMigratedArtifact(root) {
|
|
|
64
88
|
return false;
|
|
65
89
|
}
|
|
66
90
|
/**
|
|
67
|
-
* Resolve the active lifecycle layout for `projectRoot` (#2109 part 1).
|
|
91
|
+
* Resolve the active lifecycle layout for `projectRoot` (#2109 part 1 / #2112).
|
|
92
|
+
*
|
|
93
|
+
* Requires the migrated `xbrief/` layout: the `xbrief/` directory must exist
|
|
94
|
+
* AND contain at least one `.xbrief.json` artifact. If no such layout is found,
|
|
95
|
+
* throws with a clear error directing the operator to run `deft migrate:xbrief`.
|
|
68
96
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
97
|
+
* The legacy `vbrief/` read-path fallback was removed in #2112 (0.73.0 MINOR).
|
|
98
|
+
* Projects that have not migrated must run `deft migrate:xbrief` first.
|
|
99
|
+
*
|
|
100
|
+
* @throws {Error} When no `xbrief/` layout with `.xbrief.json` artifacts exists.
|
|
73
101
|
*/
|
|
74
102
|
export function resolveLifecycleLayout(projectRoot) {
|
|
75
103
|
const migratedRoot = join(projectRoot, MIGRATED_ARTIFACT_DIR);
|
|
@@ -82,28 +110,26 @@ export function resolveLifecycleLayout(projectRoot) {
|
|
|
82
110
|
migrated: true,
|
|
83
111
|
};
|
|
84
112
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
artifactSuffix: LEGACY_ARTIFACT_SUFFIX,
|
|
88
|
-
infoRootKey: LEGACY_INFO_ROOT_KEY,
|
|
89
|
-
root: join(projectRoot, LEGACY_ARTIFACT_DIR),
|
|
90
|
-
migrated: false,
|
|
91
|
-
};
|
|
113
|
+
throw new Error(`No xbrief/ layout found at ${projectRoot}. ` +
|
|
114
|
+
"Run `deft migrate:xbrief` to convert your project from the legacy vbrief/ layout.");
|
|
92
115
|
}
|
|
93
116
|
/** Convenience accessor for the absolute resolved lifecycle root directory. */
|
|
94
117
|
export function resolveLifecycleRoot(projectRoot) {
|
|
95
118
|
return resolveLifecycleLayout(projectRoot).root;
|
|
96
119
|
}
|
|
97
120
|
/**
|
|
98
|
-
* Absolute path to a lifecycle folder under the resolved layout root
|
|
99
|
-
* (e.g. `<root
|
|
121
|
+
* Absolute path to a lifecycle folder under the resolved `xbrief/` layout root
|
|
122
|
+
* (e.g. `<root>/xbrief/active`). Layout-aware (#2109 part 2a / #2112).
|
|
100
123
|
*/
|
|
101
124
|
export function resolveLifecycleFolder(projectRoot, folder) {
|
|
102
|
-
|
|
125
|
+
// Throws on a pure vbrief/-only tree (#2112). Falls back to xbrief/ when neither layout exists.
|
|
126
|
+
return join(resolveLayoutRootOrCanonical(projectRoot), folder);
|
|
103
127
|
}
|
|
104
128
|
/** Absolute path to the layout-aware `.eval/` directory for version-eval results (#1703). */
|
|
105
129
|
export function resolveEvalDir(projectRoot) {
|
|
106
|
-
|
|
130
|
+
// Throws on a pure vbrief/-only tree (#2112). Falls back to xbrief/ when neither layout exists.
|
|
131
|
+
const layoutRoot = resolveLayoutRootOrCanonical(projectRoot);
|
|
132
|
+
return join(layoutRoot, ".eval");
|
|
107
133
|
}
|
|
108
134
|
/** Absolute path under `.eval/` for version-eval artefacts (not triage working-set). */
|
|
109
135
|
export function resolveEvalPath(projectRoot, ...segments) {
|
|
@@ -111,7 +137,9 @@ export function resolveEvalPath(projectRoot, ...segments) {
|
|
|
111
137
|
}
|
|
112
138
|
/** Absolute path to the resolved lifecycle `.audit` directory (#2109 part 2a). */
|
|
113
139
|
export function resolveAuditDir(projectRoot) {
|
|
114
|
-
|
|
140
|
+
// Throws on a pure vbrief/-only tree (#2112). Falls back to xbrief/ when neither layout exists.
|
|
141
|
+
const layoutRoot = resolveLayoutRootOrCanonical(projectRoot);
|
|
142
|
+
return join(layoutRoot, ".audit");
|
|
115
143
|
}
|
|
116
144
|
/** Absolute path to a file or subpath under the resolved `.audit` directory. */
|
|
117
145
|
export function resolveAuditPath(projectRoot, ...segments) {
|
|
@@ -122,27 +150,56 @@ export function resolveAuditPath(projectRoot, ...segments) {
|
|
|
122
150
|
* (`PROJECT-DEFINITION.xbrief.json` when migrated, else `.vbrief.json`).
|
|
123
151
|
*/
|
|
124
152
|
export function projectDefinitionFilename(projectRoot) {
|
|
125
|
-
|
|
153
|
+
// Throws on a pure vbrief/-only tree (#2112). Falls back to xbrief/ when neither layout exists.
|
|
154
|
+
try {
|
|
155
|
+
return `PROJECT-DEFINITION${resolveLifecycleLayout(projectRoot).artifactSuffix}`;
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
if (isDirectory(join(projectRoot, LEGACY_ARTIFACT_DIR)) &&
|
|
159
|
+
!isDirectory(join(projectRoot, MIGRATED_ARTIFACT_DIR))) {
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
return `PROJECT-DEFINITION${MIGRATED_ARTIFACT_SUFFIX}`;
|
|
163
|
+
}
|
|
126
164
|
}
|
|
127
165
|
/** Absolute path to the resolved PROJECT-DEFINITION artifact (#2109 part 2a). */
|
|
128
166
|
export function resolveProjectDefinitionPath(projectRoot) {
|
|
129
|
-
|
|
130
|
-
|
|
167
|
+
// Throws on a pure vbrief/-only tree (#2112). Falls back to xbrief/ when neither layout exists.
|
|
168
|
+
try {
|
|
169
|
+
const layout = resolveLifecycleLayout(projectRoot);
|
|
170
|
+
return join(layout.root, `PROJECT-DEFINITION${layout.artifactSuffix}`);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
if (isDirectory(join(projectRoot, LEGACY_ARTIFACT_DIR)) &&
|
|
174
|
+
!isDirectory(join(projectRoot, MIGRATED_ARTIFACT_DIR))) {
|
|
175
|
+
throw err; // Legacy-only project: operator must run deft migrate:xbrief.
|
|
176
|
+
}
|
|
177
|
+
return join(projectRoot, MIGRATED_ARTIFACT_DIR, `PROJECT-DEFINITION${MIGRATED_ARTIFACT_SUFFIX}`);
|
|
178
|
+
}
|
|
131
179
|
}
|
|
132
180
|
/**
|
|
133
181
|
* POSIX-style display path to the resolved PROJECT-DEFINITION artifact relative
|
|
134
|
-
* to the project root (e.g. `
|
|
182
|
+
* to the project root (e.g. `xbrief/PROJECT-DEFINITION.xbrief.json`).
|
|
135
183
|
*/
|
|
136
184
|
export function projectDefinitionRelPath(projectRoot) {
|
|
137
|
-
|
|
138
|
-
|
|
185
|
+
// Throws on a pure vbrief/-only tree (#2112). Falls back to xbrief/ when neither layout exists.
|
|
186
|
+
try {
|
|
187
|
+
const layout = resolveLifecycleLayout(projectRoot);
|
|
188
|
+
return `${layout.artifactDir}/PROJECT-DEFINITION${layout.artifactSuffix}`;
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
if (isDirectory(join(projectRoot, LEGACY_ARTIFACT_DIR)) &&
|
|
192
|
+
!isDirectory(join(projectRoot, MIGRATED_ARTIFACT_DIR))) {
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
return `${MIGRATED_ARTIFACT_DIR}/PROJECT-DEFINITION${MIGRATED_ARTIFACT_SUFFIX}`;
|
|
196
|
+
}
|
|
139
197
|
}
|
|
140
198
|
/**
|
|
141
|
-
* Absolute path to the resolved specification artifact (#2132).
|
|
199
|
+
* Absolute path to the resolved specification artifact (#2132 / #2112).
|
|
142
200
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* layout resolver used by the #2109 Part-2a call-site sweep.
|
|
201
|
+
* Returns `xbrief/specification.xbrief.json`; throws when no migrated xbrief/
|
|
202
|
+
* layout is present (see `resolveLifecycleLayout`).
|
|
146
203
|
*/
|
|
147
204
|
export function resolveSpecArtifactPath(projectRoot) {
|
|
148
205
|
const layout = resolveLifecycleLayout(projectRoot);
|
|
@@ -150,7 +207,7 @@ export function resolveSpecArtifactPath(projectRoot) {
|
|
|
150
207
|
}
|
|
151
208
|
/**
|
|
152
209
|
* POSIX-style display path to the resolved specification artifact relative to
|
|
153
|
-
* the project root (
|
|
210
|
+
* the project root (`xbrief/specification.xbrief.json`).
|
|
154
211
|
*/
|
|
155
212
|
export function specArtifactRelPath(projectRoot) {
|
|
156
213
|
const layout = resolveLifecycleLayout(projectRoot);
|
|
@@ -11,7 +11,7 @@ export declare const TERMINAL_STATUSES: Set<string>;
|
|
|
11
11
|
/** Child reference type that marks an epic as decomposed. */
|
|
12
12
|
export declare const CHILD_REF_TYPE = "x-xbrief/plan";
|
|
13
13
|
/** Durable tech-debt acceptance ledger relative path segments. */
|
|
14
|
-
export declare const TECH_DEBT_LEDGER_RELPATH: readonly ["
|
|
14
|
+
export declare const TECH_DEBT_LEDGER_RELPATH: readonly ["xbrief", ".audit", "epic-tech-debt-accepted.jsonl"];
|
|
15
15
|
/** Session-start nudge tiers (rate-of-harm ranking). */
|
|
16
16
|
export declare const TIER_STRANDED = 1;
|
|
17
17
|
export declare const TIER_STALE_EPIC = 2;
|
|
@@ -26,7 +26,7 @@ export const TERMINAL_STATUSES = new Set(["completed", "cancelled", "failed"]);
|
|
|
26
26
|
export const CHILD_REF_TYPE = "x-xbrief/plan";
|
|
27
27
|
/** Durable tech-debt acceptance ledger relative path segments. */
|
|
28
28
|
export const TECH_DEBT_LEDGER_RELPATH = [
|
|
29
|
-
"
|
|
29
|
+
"xbrief",
|
|
30
30
|
".audit",
|
|
31
31
|
"epic-tech-debt-accepted.jsonl",
|
|
32
32
|
];
|
|
@@ -14,7 +14,7 @@ function resolveContentRoot(frameworkRoot) {
|
|
|
14
14
|
return frameworkRoot;
|
|
15
15
|
}
|
|
16
16
|
export function checkLayout(deftRoot, projectRoot) {
|
|
17
|
-
const schemasDir = join(resolveContentRoot(deftRoot), "
|
|
17
|
+
const schemasDir = join(resolveContentRoot(deftRoot), "xbrief", "schemas");
|
|
18
18
|
if (!existsSync(schemasDir) || !statSync(schemasDir).isDirectory()) {
|
|
19
19
|
return {
|
|
20
20
|
name: "layout",
|
|
@@ -22,18 +22,18 @@ export function checkLayout(deftRoot, projectRoot) {
|
|
|
22
22
|
message: `Framework schemas dir missing at ${schemasDir}. Refresh the deft checkout (see deft/QUICK-START.md).`,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
const
|
|
26
|
-
if (!existsSync(
|
|
25
|
+
const projectXbrief = join(projectRoot, "xbrief");
|
|
26
|
+
if (!existsSync(projectXbrief)) {
|
|
27
27
|
return {
|
|
28
28
|
name: "layout",
|
|
29
29
|
status: "WARN",
|
|
30
|
-
message: `Project
|
|
30
|
+
message: `Project xbrief/ not present at ${projectXbrief} -- expected for greenfield projects.`,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
return {
|
|
34
34
|
name: "layout",
|
|
35
35
|
status: "PASS",
|
|
36
|
-
message: `Framework schemas present; project
|
|
36
|
+
message: `Framework schemas present; project xbrief/ at ${projectXbrief}.`,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
export function checkGitClean(projectRoot) {
|
|
@@ -91,7 +91,7 @@ export function checkDocumentModel(projectRoot) {
|
|
|
91
91
|
return {
|
|
92
92
|
name: "document-model",
|
|
93
93
|
status: "FAIL",
|
|
94
|
-
message: `Generated SPECIFICATION.md detected (source:
|
|
94
|
+
message: `Generated SPECIFICATION.md detected (source: xbrief/specification.xbrief.json); repair missing lifecycle folder(s) instead of migrating: ${missing.join(", ")}.`,
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -99,18 +99,18 @@ export function checkDocumentModel(projectRoot) {
|
|
|
99
99
|
return {
|
|
100
100
|
name: "document-model",
|
|
101
101
|
status: "PASS",
|
|
102
|
-
message: "Current generated SPECIFICATION.md detected (source:
|
|
102
|
+
message: "Current generated SPECIFICATION.md detected (source: xbrief/specification.xbrief.json); pre-v0.20 migration is not needed.",
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
const
|
|
107
|
-
if (existsSync(
|
|
106
|
+
const xbriefRoot = join(projectRoot, "xbrief");
|
|
107
|
+
if (existsSync(xbriefRoot)) {
|
|
108
108
|
const missing = missingLifecycleFolders(projectRoot);
|
|
109
109
|
if (missing.length > 0) {
|
|
110
110
|
return {
|
|
111
111
|
name: "document-model",
|
|
112
112
|
status: "FAIL",
|
|
113
|
-
message: `Partial
|
|
113
|
+
message: `Partial xBRIEF layout detected; missing lifecycle folder(s): ${missing.join(", ")}. Create the folders or follow ${frozenPreCutoverMigrationGuidance()}`,
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PolicyResult } from "./resolve.js";
|
|
2
|
+
export { policyColonInvocation, policySetInvocation } from "./policy-invocation.js";
|
|
2
3
|
/** One-liner disclosure phrasing for AGENTS.md / setup interview echo. */
|
|
3
4
|
export declare function disclosureLine(result: PolicyResult): string;
|
|
4
5
|
//# sourceMappingURL=disclosure.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ENV_BYPASS } from "./resolve.js";
|
|
2
|
+
export { policyColonInvocation, policySetInvocation } from "./policy-invocation.js";
|
|
2
3
|
/** One-liner disclosure phrasing for AGENTS.md / setup interview echo. */
|
|
3
4
|
export function disclosureLine(result) {
|
|
4
5
|
if (result.allowDirectCommits) {
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./capacity.js";
|
|
|
4
4
|
export * from "./decisions.js";
|
|
5
5
|
export * from "./disclosure.js";
|
|
6
6
|
export * from "./plan-extensions.js";
|
|
7
|
+
export * from "./policy-invocation.js";
|
|
7
8
|
export * from "./resolve.js";
|
|
8
9
|
export * from "./value-feedback.js";
|
|
9
10
|
export * from "./wip.js";
|
package/dist/policy/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./capacity.js";
|
|
|
8
8
|
export * from "./decisions.js";
|
|
9
9
|
export * from "./disclosure.js";
|
|
10
10
|
export * from "./plan-extensions.js";
|
|
11
|
+
export * from "./policy-invocation.js";
|
|
11
12
|
export * from "./resolve.js";
|
|
12
13
|
export * from "./value-feedback.js";
|
|
13
14
|
export * from "./wip.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** User-facing colon-form policy verb for consumer `deft` disclosures (#2367). */
|
|
2
|
+
export declare function policyColonInvocation(subcommand: string, trailing?: string): string;
|
|
3
|
+
/** User-facing `deft policy set <cmd>` form for policy-set disclosures (#2367). */
|
|
4
|
+
export declare function policySetInvocation(subcommand: string, trailing?: string): string;
|
|
5
|
+
//# sourceMappingURL=policy-invocation.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** User-facing colon-form policy verb for consumer `deft` disclosures (#2367). */
|
|
2
|
+
export function policyColonInvocation(subcommand, trailing = "") {
|
|
3
|
+
return `deft policy:${subcommand}${trailing}`;
|
|
4
|
+
}
|
|
5
|
+
/** User-facing `deft policy set <cmd>` form for policy-set disclosures (#2367). */
|
|
6
|
+
export function policySetInvocation(subcommand, trailing = "") {
|
|
7
|
+
return `deft policy set ${subcommand}${trailing}`;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=policy-invocation.js.map
|
package/dist/policy/resolve.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Filesystem-relative location of the project-definition
|
|
2
|
-
export declare const PROJECT_DEFINITION_REL_PATH = "
|
|
1
|
+
/** Filesystem-relative location of the project-definition xBRIEF (display/back-compat). */
|
|
2
|
+
export declare const PROJECT_DEFINITION_REL_PATH = "xbrief/PROJECT-DEFINITION.xbrief.json";
|
|
3
3
|
/** Environment variable emergency bypass for branch protection (#747). */
|
|
4
4
|
export declare const ENV_BYPASS = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
|
|
5
5
|
/** Legacy narrative key replaced by the typed flag (#746). */
|
package/dist/policy/resolve.js
CHANGED
|
@@ -3,8 +3,9 @@ import { join, resolve as pathResolve } from "node:path";
|
|
|
3
3
|
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
4
4
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
5
5
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import { policyColonInvocation } from "./policy-invocation.js";
|
|
7
|
+
/** Filesystem-relative location of the project-definition xBRIEF (display/back-compat). */
|
|
8
|
+
export const PROJECT_DEFINITION_REL_PATH = "xbrief/PROJECT-DEFINITION.xbrief.json";
|
|
8
9
|
/** Environment variable emergency bypass for branch protection (#747). */
|
|
9
10
|
export const ENV_BYPASS = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
|
|
10
11
|
/** Legacy narrative key replaced by the typed flag (#746). */
|
|
@@ -18,7 +19,15 @@ const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
|
18
19
|
* else PROJECT-DEFINITION.vbrief.json (unchanged on today's tree).
|
|
19
20
|
*/
|
|
20
21
|
export function projectDefinitionPath(projectRoot) {
|
|
21
|
-
|
|
22
|
+
const root = pathResolve(projectRoot);
|
|
23
|
+
try {
|
|
24
|
+
return resolveProjectDefinitionPath(root);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// No xbrief/ layout; return canonical xbrief path so callers get a predictable
|
|
28
|
+
// "not found" result rather than a thrown error.
|
|
29
|
+
return join(root, PROJECT_DEFINITION_REL_PATH);
|
|
30
|
+
}
|
|
22
31
|
}
|
|
23
32
|
function envBypassActive() {
|
|
24
33
|
const raw = process.env[ENV_BYPASS] ?? "";
|
|
@@ -146,8 +155,9 @@ export function resolvePolicy(projectRoot) {
|
|
|
146
155
|
const warn = `DEPRECATED: PROJECT-DEFINITION uses the legacy narrative key ` +
|
|
147
156
|
`'${LEGACY_NARRATIVE_KEY}' (${pythonRepr(raw)}). Migrate to typed ` +
|
|
148
157
|
`plan.policy.allowDirectCommitsToMaster (#746). Run ` +
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
`\`${policyColonInvocation("enforce-branches")}\` or ` +
|
|
159
|
+
`\`${policyColonInvocation("allow-direct-commits", " -- --confirm")}\` ` +
|
|
160
|
+
`to set the typed flag explicitly.`;
|
|
151
161
|
return {
|
|
152
162
|
allowDirectCommits: allow,
|
|
153
163
|
source: "legacy-narrative",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
3
3
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
4
|
+
import { policyColonInvocation } from "./policy-invocation.js";
|
|
4
5
|
import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
|
|
5
6
|
import { isTrustedOrgAutoEnable } from "./value-feedback-autoenable.js";
|
|
6
7
|
/** Canonical registered policy field name (matches other FIELD_* dotted paths). */
|
|
@@ -21,7 +22,9 @@ export const VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE = "\u26a0 Capability-cost
|
|
|
21
22
|
" \u2022 A budgeted session one-liner may appear when concrete attributed value exists.\n" +
|
|
22
23
|
" \u2022 Upstream gap-escalation prompts stay OFF unless you explicitly enable " +
|
|
23
24
|
"`upstreamPrompt` (GitHub attention + token cost).\n" +
|
|
24
|
-
" \u2022 Inspect current state: `
|
|
25
|
+
" \u2022 Inspect current state: `" +
|
|
26
|
+
policyColonInvocation("show", " --field=valueFeedback") +
|
|
27
|
+
"`.\n" +
|
|
25
28
|
" \u2022 Reversible: set `enabled: false` under the typed policy block in PROJECT-DEFINITION.\n" +
|
|
26
29
|
" \u2022 Changes are recorded to meta/policy-changes.log for auditability.";
|
|
27
30
|
function defaultResolved(source, error = null) {
|
|
@@ -202,7 +205,7 @@ export function enableValueFeedback(projectRoot, options) {
|
|
|
202
205
|
return {
|
|
203
206
|
exitCode: 1,
|
|
204
207
|
stdout: `${VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE}\n\n` +
|
|
205
|
-
|
|
208
|
+
`Re-run with --confirm to apply: ${policyColonInvocation("enable-value-feedback", " -- --confirm")}\n`,
|
|
206
209
|
changed: false,
|
|
207
210
|
};
|
|
208
211
|
}
|
|
@@ -265,7 +268,7 @@ export function enableValueFeedback(projectRoot, options) {
|
|
|
265
268
|
if (changedFlag) {
|
|
266
269
|
atomicWriteProjectDefinition(path, data);
|
|
267
270
|
}
|
|
268
|
-
const actor = options.actor ?? "
|
|
271
|
+
const actor = options.actor ?? policyColonInvocation("enable-value-feedback");
|
|
269
272
|
const note = options.note ?? "";
|
|
270
273
|
const parts = [
|
|
271
274
|
`actor=${actor}`,
|
package/dist/policy/wip.d.ts
CHANGED
|
@@ -10,6 +10,6 @@ export interface WipCapResult {
|
|
|
10
10
|
}
|
|
11
11
|
/** Resolve plan.policy.wipCap from PROJECT-DEFINITION (#1124). */
|
|
12
12
|
export declare function resolveWipCap(projectRoot: string): WipCapResult;
|
|
13
|
-
/** Count *.
|
|
13
|
+
/** Count *.xbrief.json files in xbrief/pending/ + xbrief/active/ (#1124). */
|
|
14
14
|
export declare function countVbriefWip(projectRoot: string): number;
|
|
15
15
|
//# sourceMappingURL=wip.d.ts.map
|
package/dist/policy/wip.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
3
|
+
import { hasArtifactSuffix, LEGACY_ARTIFACT_DIR, MIGRATED_ARTIFACT_DIR, resolveLifecycleRoot, } from "../layout/resolve.js";
|
|
4
4
|
import { readPlanPolicy } from "./plan-extensions.js";
|
|
5
5
|
import { loadProjectDefinition } from "./resolve.js";
|
|
6
6
|
/** Framework default WIP cap (#2319; raised from 10 per #1124 / umbrella #1119). */
|
|
@@ -62,10 +62,22 @@ export function resolveWipCap(projectRoot) {
|
|
|
62
62
|
}
|
|
63
63
|
return { cap: raw, source: "typed", error: null };
|
|
64
64
|
}
|
|
65
|
-
/** Count *.
|
|
65
|
+
/** Count *.xbrief.json files in xbrief/pending/ + xbrief/active/ (#1124). */
|
|
66
66
|
export function countVbriefWip(projectRoot) {
|
|
67
67
|
let total = 0;
|
|
68
|
-
|
|
68
|
+
let vbriefRoot;
|
|
69
|
+
try {
|
|
70
|
+
vbriefRoot = resolveLifecycleRoot(projectRoot);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
// If xbrief/ dir exists (even without artifacts), there is no WIP to count.
|
|
74
|
+
// If only vbrief/ exists, this is a legacy project that must be migrated -- re-throw.
|
|
75
|
+
if (!existsSync(join(projectRoot, MIGRATED_ARTIFACT_DIR)) &&
|
|
76
|
+
existsSync(join(projectRoot, LEGACY_ARTIFACT_DIR))) {
|
|
77
|
+
throw err; // Legacy-only project: operator must run deft migrate:xbrief.
|
|
78
|
+
}
|
|
79
|
+
return 0; // No layout found at all; no WIP files to count.
|
|
80
|
+
}
|
|
69
81
|
for (const sub of WIP_LIFECYCLE_DIRS) {
|
|
70
82
|
const folder = join(vbriefRoot, sub);
|
|
71
83
|
if (!existsSync(folder)) {
|
|
@@ -23,7 +23,7 @@ import { resolveCandidatesLogPath } from "../triage/cache-path.js";
|
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
export const CACHE_DIR_NAME = ".deft-cache";
|
|
25
25
|
export const DEFAULT_SOURCE = "github-issue";
|
|
26
|
-
export const CANDIDATES_RELPATH = join("
|
|
26
|
+
export const CANDIDATES_RELPATH = join("xbrief", ".triage-cache", "candidates.jsonl");
|
|
27
27
|
export const DEFAULT_MAX_AGE_HOURS = 24;
|
|
28
28
|
export const ENV_MAX_AGE_HOURS = "DEFT_CACHE_MAX_AGE_HOURS";
|
|
29
29
|
export const ENV_TRIAGE_REPO = "DEFT_TRIAGE_REPO";
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* Replaces python-steps.ts (scripts/ roadmap_render, reconcile_issues, build_dist).
|
|
4
4
|
*/
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
6
7
|
import { dirname, join } from "node:path";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
import { fetchIssueStates, isTerminalLifecyclePath, reconcile, scanVbriefDir, } from "../intake/reconcile-issues.js";
|
|
9
|
-
import { resolveLifecycleFolder, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
10
|
+
import { LEGACY_ARTIFACT_DIR, MIGRATED_ARTIFACT_DIR, resolveLifecycleFolder, resolveLifecycleRoot, } from "../layout/resolve.js";
|
|
10
11
|
import { renderRoadmap } from "../render/roadmap-render.js";
|
|
11
12
|
const BUILD_DIST_RUNNER = join(dirname(fileURLToPath(import.meta.url)), "build-dist-runner.js");
|
|
12
13
|
export function refreshRoadmapNative(projectRoot) {
|
|
@@ -20,7 +21,18 @@ export function refreshRoadmapNative(projectRoot) {
|
|
|
20
21
|
return [true, "ROADMAP.md re-rendered"];
|
|
21
22
|
}
|
|
22
23
|
export function checkVbriefLifecycleSyncNative(projectRoot, repo) {
|
|
23
|
-
|
|
24
|
+
let vbriefDir;
|
|
25
|
+
try {
|
|
26
|
+
vbriefDir = resolveLifecycleRoot(projectRoot);
|
|
27
|
+
}
|
|
28
|
+
catch (_err) {
|
|
29
|
+
// Re-throw if this is a legacy-only vbrief/ project; fall back to canonical path otherwise.
|
|
30
|
+
if (existsSync(join(projectRoot, LEGACY_ARTIFACT_DIR)) &&
|
|
31
|
+
!existsSync(join(projectRoot, MIGRATED_ARTIFACT_DIR))) {
|
|
32
|
+
return [false, 0, "no xbrief/ layout found; skipping lifecycle sync check"];
|
|
33
|
+
}
|
|
34
|
+
vbriefDir = join(projectRoot, MIGRATED_ARTIFACT_DIR);
|
|
35
|
+
}
|
|
24
36
|
try {
|
|
25
37
|
const issueToVbriefs = scanVbriefDir(vbriefDir);
|
|
26
38
|
const issueStateMap = fetchIssueStates(repo, new Set(issueToVbriefs.keys()), {
|
|
@@ -26,7 +26,7 @@ export function exportSpec(options = {}) {
|
|
|
26
26
|
const includeProposed = audience === "internal";
|
|
27
27
|
const authority = resolveSpecAuthority(projectRoot);
|
|
28
28
|
if (!authority) {
|
|
29
|
-
return [false, "✗ Missing
|
|
29
|
+
return [false, "✗ Missing xbrief/PROJECT-DEFINITION.xbrief.json — cannot export spec."];
|
|
30
30
|
}
|
|
31
31
|
if (authority.kind === "full-spec" && authority.specPath) {
|
|
32
32
|
const [ok, msg] = validateSpec(authority.specPath);
|
|
@@ -36,7 +36,7 @@ export function exportSpec(options = {}) {
|
|
|
36
36
|
else if (!greenfieldOverviewNonEmpty(authority)) {
|
|
37
37
|
return [
|
|
38
38
|
false,
|
|
39
|
-
"⚠ PROJECT-DEFINITION.
|
|
39
|
+
"⚠ PROJECT-DEFINITION.xbrief.json Overview narrative is empty (D3). Populate Overview before export.",
|
|
40
40
|
];
|
|
41
41
|
}
|
|
42
42
|
const narratives = resolveExportNarratives(authority);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
|
-
import { hasArtifactSuffix,
|
|
3
|
+
import { hasArtifactSuffix, resolveLayoutRootOrCanonical, stripArtifactSuffix, } from "../layout/resolve.js";
|
|
4
4
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
5
5
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
6
6
|
import { deriveRegistryItemStatus, registryMetadataReferencesFromScope, } from "../vbrief-validate/registry-status.js";
|
|
@@ -317,30 +317,61 @@ export function acknowledgeProjectDefinitionStaleness(vbriefDir, options = {}) {
|
|
|
317
317
|
];
|
|
318
318
|
});
|
|
319
319
|
}
|
|
320
|
+
const USAGE = "Usage: project-render [--help] [--acknowledge-staleness] [--project-root <dir>]\n";
|
|
320
321
|
/** CLI entry (mirrors ``scripts/project_render.main``). */
|
|
321
322
|
export function main(argv) {
|
|
322
|
-
|
|
323
|
-
|
|
323
|
+
// Handle --help / -h before any other parsing so the flag is never treated
|
|
324
|
+
// as a positional output path and no stray ./--help/ directory is created
|
|
325
|
+
// (#2236).
|
|
326
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
327
|
+
process.stdout.write(USAGE);
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
// Parse all flags in a single positional-independent pass so that
|
|
331
|
+
// --acknowledge-staleness is recognised at any position (not only argv[0]).
|
|
332
|
+
let acknowledge = false;
|
|
324
333
|
let projectRoot;
|
|
325
334
|
const positional = [];
|
|
326
|
-
for (let i = 0; i <
|
|
327
|
-
const arg =
|
|
328
|
-
if (arg === "--
|
|
329
|
-
|
|
335
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
336
|
+
const arg = argv[i];
|
|
337
|
+
if (arg === "--acknowledge-staleness") {
|
|
338
|
+
acknowledge = true;
|
|
339
|
+
}
|
|
340
|
+
else if (arg === "--project-root") {
|
|
341
|
+
projectRoot = argv[i + 1];
|
|
330
342
|
i += 1;
|
|
331
343
|
}
|
|
332
344
|
else if (arg.startsWith("--project-root=")) {
|
|
333
345
|
projectRoot = arg.slice("--project-root=".length);
|
|
334
346
|
}
|
|
347
|
+
else if (arg.startsWith("-")) {
|
|
348
|
+
// Reject unknown flags rather than consuming them as positional output
|
|
349
|
+
// paths (would otherwise create stray directories like ./--help/).
|
|
350
|
+
process.stderr.write(`Unknown flag: ${arg}\n${USAGE}`);
|
|
351
|
+
return 2;
|
|
352
|
+
}
|
|
335
353
|
else {
|
|
336
354
|
positional.push(arg);
|
|
337
355
|
}
|
|
338
356
|
}
|
|
339
357
|
if (positional.length > 1) {
|
|
340
|
-
process.stderr.write(
|
|
358
|
+
process.stderr.write(USAGE);
|
|
341
359
|
return 2;
|
|
342
360
|
}
|
|
343
|
-
|
|
361
|
+
let vbriefDir;
|
|
362
|
+
if (positional[0] !== undefined) {
|
|
363
|
+
vbriefDir = positional[0];
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
const effectiveRoot = resolve(projectRoot !== undefined ? projectRoot : ".");
|
|
367
|
+
try {
|
|
368
|
+
vbriefDir = resolveLayoutRootOrCanonical(effectiveRoot);
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
process.stderr.write(`No xbrief/ layout found at ${effectiveRoot}. Run \`deft migrate:xbrief\` to convert your project from the legacy vbrief/ layout.\n`);
|
|
372
|
+
return 2;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
344
375
|
const [ok, message] = acknowledge
|
|
345
376
|
? acknowledgeProjectDefinitionStaleness(vbriefDir)
|
|
346
377
|
: renderProjectDefinition(vbriefDir);
|