@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
package/dist/scope/vbrief-ref.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
3
|
+
import { hasArtifactSuffix, stripArtifactSuffix } from "../layout/resolve.js";
|
|
2
4
|
/** Resolve a vBRIEF reference URI to an absolute path, or null. */
|
|
3
5
|
export function resolveVbriefRef(uri, vbriefDir) {
|
|
4
6
|
if (typeof uri !== "string" || uri.length === 0) {
|
|
@@ -48,7 +50,7 @@ export function collectChildUris(plan) {
|
|
|
48
50
|
continue;
|
|
49
51
|
}
|
|
50
52
|
const r = ref;
|
|
51
|
-
if (r.type
|
|
53
|
+
if (!referenceTypeMatches(String(r.type ?? ""), "plan")) {
|
|
52
54
|
continue;
|
|
53
55
|
}
|
|
54
56
|
const uri = r.uri;
|
|
@@ -61,8 +63,8 @@ export function collectChildUris(plan) {
|
|
|
61
63
|
/** Return registry IDs that may name a vBRIEF filename. */
|
|
62
64
|
export function scopeIdsForFilename(filename) {
|
|
63
65
|
let fullId;
|
|
64
|
-
if (filename
|
|
65
|
-
fullId = filename
|
|
66
|
+
if (hasArtifactSuffix(filename)) {
|
|
67
|
+
fullId = stripArtifactSuffix(filename);
|
|
66
68
|
}
|
|
67
69
|
else {
|
|
68
70
|
fullId = filename.replace(/\.[^.]+$/, "");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
+
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
4
5
|
export const RESUME_ELIGIBLE_DECISION = "resume-eligible";
|
|
5
6
|
export const EVALUATOR_ACTOR = "agent:resume-evaluator";
|
|
6
7
|
export const CACHE_DIR_NAME = ".deft-cache";
|
|
@@ -270,7 +271,7 @@ function countPending(projectRoot) {
|
|
|
270
271
|
for (const name of readdirSync(folder)) {
|
|
271
272
|
const full = join(folder, name);
|
|
272
273
|
try {
|
|
273
|
-
if (statSync(full).isFile() && name
|
|
274
|
+
if (statSync(full).isFile() && hasArtifactSuffix(name)) {
|
|
274
275
|
count += 1;
|
|
275
276
|
}
|
|
276
277
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { closeSync, existsSync, fdatasyncSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, statSync, writeSync, } from "node:fs";
|
|
2
2
|
import { join, relative, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
3
4
|
import { stableJson } from "./json.js";
|
|
4
5
|
import { parseTimestamp, timestampIso } from "./time.js";
|
|
5
6
|
export const SCHEMA_VERSION = 1;
|
|
@@ -307,7 +308,7 @@ export function detectLatestActiveVbrief(projectRoot) {
|
|
|
307
308
|
}
|
|
308
309
|
const candidates = [];
|
|
309
310
|
for (const name of children) {
|
|
310
|
-
if (!name
|
|
311
|
+
if (!hasArtifactSuffix(name)) {
|
|
311
312
|
continue;
|
|
312
313
|
}
|
|
313
314
|
const full = join(activeDir, name);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
1
2
|
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
2
3
|
const DEFAULT_SESSION_RITUAL_STALENESS_HOURS = 4;
|
|
3
4
|
function pythonTypeName(value) {
|
|
@@ -59,7 +60,7 @@ export function resolveSessionRitualStalenessHours(projectRoot) {
|
|
|
59
60
|
error: "PROJECT-DEFINITION 'plan' is not an object",
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
|
-
const policyBlock = plan
|
|
63
|
+
const policyBlock = readPlanPolicy(plan);
|
|
63
64
|
if (typeof policyBlock !== "object" ||
|
|
64
65
|
policyBlock === null ||
|
|
65
66
|
Array.isArray(policyBlock) ||
|
package/dist/slice/record.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { appendFileSync, closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, } from "node:fs";
|
|
3
|
-
import { dirname,
|
|
4
|
-
import {
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { resolveEvalPath } from "../layout/resolve.js";
|
|
5
5
|
import { pythonJsonStringify } from "./json.js";
|
|
6
6
|
import { withAppendLock } from "./lock.js";
|
|
7
7
|
import { validateRecord } from "./validate.js";
|
|
@@ -16,7 +16,7 @@ const defaultDeps = {
|
|
|
16
16
|
withLock: withAppendLock,
|
|
17
17
|
};
|
|
18
18
|
function resolvePath(path) {
|
|
19
|
-
return path !== undefined ? resolve(path) :
|
|
19
|
+
return path !== undefined ? resolve(path) : resolveEvalPath(process.cwd(), "slices.jsonl");
|
|
20
20
|
}
|
|
21
21
|
export function newSliceId() {
|
|
22
22
|
return randomUUID();
|
|
@@ -134,6 +134,6 @@ export function findByUmbrella(umbrella, options = {}) {
|
|
|
134
134
|
return readAll(options).filter((record) => record.umbrella === umbrella);
|
|
135
135
|
}
|
|
136
136
|
export function slicesPath(projectRoot) {
|
|
137
|
-
return
|
|
137
|
+
return resolveEvalPath(projectRoot, "slices.jsonl");
|
|
138
138
|
}
|
|
139
139
|
//# sourceMappingURL=record.js.map
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { resolveLifecycleLayout, resolveLifecycleRoot, resolveProjectDefinitionPath, } from "../layout/resolve.js";
|
|
3
4
|
import { SPEC_RENDER_BANNER } from "../render/constants.js";
|
|
4
5
|
import { EXPORT_SPEC_PD_BANNER, GENERATED_SPEC_PURPOSE, GENERATED_SPEC_SOURCE_PD, GENERATED_SPEC_SOURCE_SPEC, } from "./constants.js";
|
|
5
|
-
const SPEC_REL = join("vbrief", "specification.vbrief.json");
|
|
6
|
-
const PD_REL = join("vbrief", "PROJECT-DEFINITION.vbrief.json");
|
|
7
6
|
export function resolveSpecAuthority(projectRoot) {
|
|
8
7
|
const root = projectRoot;
|
|
9
|
-
const
|
|
10
|
-
const
|
|
8
|
+
const layout = resolveLifecycleLayout(root);
|
|
9
|
+
const vbriefDir = resolveLifecycleRoot(root);
|
|
10
|
+
const projectDefPath = resolveProjectDefinitionPath(root);
|
|
11
11
|
if (!existsSync(projectDefPath))
|
|
12
12
|
return null;
|
|
13
|
-
const specPath = join(
|
|
13
|
+
const specPath = join(vbriefDir, `specification${layout.artifactSuffix}`);
|
|
14
14
|
const hasSpec = existsSync(specPath);
|
|
15
15
|
const kind = hasSpec ? "full-spec" : "greenfield";
|
|
16
16
|
const banner = kind === "full-spec" ? SPEC_RENDER_BANNER : EXPORT_SPEC_PD_BANNER;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
3
4
|
import { detectLifecycleFolder } from "../scope/decomposed-refs.js";
|
|
4
5
|
import { runTransition } from "../scope/transition.js";
|
|
5
6
|
import { collectChildUris, collectPlanRefs, resolveVbriefRef } from "../scope/vbrief-ref.js";
|
|
@@ -41,7 +42,7 @@ function globResolve(pattern, projectRoot) {
|
|
|
41
42
|
return [];
|
|
42
43
|
}
|
|
43
44
|
return readdirSync(dir)
|
|
44
|
-
.filter((name) => name
|
|
45
|
+
.filter((name) => hasArtifactSuffix(name) || glob === name)
|
|
45
46
|
.map((name) => resolve(dir, name))
|
|
46
47
|
.filter((p) => existsSync(p));
|
|
47
48
|
}
|
|
@@ -229,7 +230,7 @@ function completeParent(parentPath, vbriefDir, projectRoot, settled, dryRun) {
|
|
|
229
230
|
};
|
|
230
231
|
}
|
|
231
232
|
export function sweepCohort(storyPaths, projectRoot, dryRun) {
|
|
232
|
-
const vbriefDir =
|
|
233
|
+
const vbriefDir = resolveLifecycleRoot(projectRoot);
|
|
233
234
|
const result = {
|
|
234
235
|
project_root: resolve(projectRoot),
|
|
235
236
|
dry_run: dryRun,
|
|
@@ -243,7 +244,7 @@ export function sweepCohort(storyPaths, projectRoot, dryRun) {
|
|
|
243
244
|
const termDir = join(vbriefDir, term);
|
|
244
245
|
if (existsSync(termDir)) {
|
|
245
246
|
for (const name of readdirSync(termDir)) {
|
|
246
|
-
if (name
|
|
247
|
+
if (hasArtifactSuffix(name)) {
|
|
247
248
|
settled.add(resolve(join(termDir, name)));
|
|
248
249
|
}
|
|
249
250
|
}
|
|
@@ -358,7 +359,7 @@ export function completeCohort(args) {
|
|
|
358
359
|
stderr: `Error: project root does not exist: ${projectRoot}\n`,
|
|
359
360
|
};
|
|
360
361
|
}
|
|
361
|
-
if (!existsSync(
|
|
362
|
+
if (!existsSync(resolveLifecycleRoot(projectRoot))) {
|
|
362
363
|
return {
|
|
363
364
|
exitCode: 2,
|
|
364
365
|
stdout: "",
|
package/dist/swarm/launch.js
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
3
|
import { inferGithubAuthMode } from "../intake/github-auth-modes.js";
|
|
4
4
|
import { getPlatformCapabilities } from "../intake/platform-capabilities.js";
|
|
5
|
+
import { hasArtifactSuffix, resolveLifecycleFolder, stripArtifactSuffix, } from "../layout/resolve.js";
|
|
5
6
|
import { evaluate as preflightEvaluate } from "../preflight/evaluate.js";
|
|
6
7
|
import { issueNumbersFromPlan, scopeMetadataRank } from "../triage/queue/scope-walk.js";
|
|
7
8
|
import { selectionOrderingKey } from "../triage/queue/selection.js";
|
|
@@ -45,9 +46,7 @@ function storyId(path, plan) {
|
|
|
45
46
|
return value.trim();
|
|
46
47
|
}
|
|
47
48
|
const name = basename(path);
|
|
48
|
-
return name.
|
|
49
|
-
? name.slice(0, -".vbrief.json".length)
|
|
50
|
-
: name.replace(/\.[^.]+$/, "");
|
|
49
|
+
return hasArtifactSuffix(name) ? stripArtifactSuffix(name) : name.replace(/\.[^.]+$/, "");
|
|
51
50
|
}
|
|
52
51
|
function extractHashNumbers(text) {
|
|
53
52
|
const out = [];
|
|
@@ -128,13 +127,13 @@ function projectRel(projectRoot, path) {
|
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
129
|
function indexActiveStories(projectRoot) {
|
|
131
|
-
const activeDir =
|
|
130
|
+
const activeDir = resolveLifecycleFolder(projectRoot, "active");
|
|
132
131
|
const index = [];
|
|
133
132
|
if (!existsSync(activeDir)) {
|
|
134
133
|
return index;
|
|
135
134
|
}
|
|
136
135
|
for (const name of readdirSync(activeDir).sort()) {
|
|
137
|
-
if (!name
|
|
136
|
+
if (!hasArtifactSuffix(name)) {
|
|
138
137
|
continue;
|
|
139
138
|
}
|
|
140
139
|
const path = join(activeDir, name);
|
|
@@ -151,7 +150,7 @@ export function looksLikePath(token) {
|
|
|
151
150
|
return (token.endsWith(".json") ||
|
|
152
151
|
token.includes("/") ||
|
|
153
152
|
token.includes("\\") ||
|
|
154
|
-
(existsSync(token) && basename(token)
|
|
153
|
+
(existsSync(token) && hasArtifactSuffix(basename(token))));
|
|
155
154
|
}
|
|
156
155
|
function resolveOne(token, projectRoot, idMap, issueMap) {
|
|
157
156
|
if (looksLikePath(token)) {
|
|
@@ -411,7 +410,7 @@ export function swarmLaunch(args) {
|
|
|
411
410
|
stderr: "Error: no stories supplied. Pass --stories <ids|paths> and/or --paths <paths>.\n",
|
|
412
411
|
};
|
|
413
412
|
}
|
|
414
|
-
if (!existsSync(
|
|
413
|
+
if (!existsSync(resolveLifecycleFolder(projectRoot, "active"))) {
|
|
415
414
|
return {
|
|
416
415
|
exitCode: EXIT_CONFIG_ERROR,
|
|
417
416
|
stdout: "",
|
package/dist/swarm/readiness.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
4
|
+
import { ARTIFACT_SUFFIXES, hasArtifactSuffix, LIFECYCLE_DIR_NAMES, resolveLifecycleLayout, resolveLifecycleRoot, stripArtifactSuffix, } from "../layout/resolve.js";
|
|
3
5
|
import { acceptanceTextsFromItems, asStrList, deprecatedSubitemsIssues, itemHasTraces, itemsHaveAcceptance, missingRequiredSwarmFields, storyQualityIssues, } from "../vbrief-validation/story-quality.js";
|
|
4
6
|
import { LIFECYCLE_FOLDERS, READY } from "./constants.js";
|
|
5
7
|
function loadJson(path) {
|
|
@@ -24,7 +26,8 @@ function projectRel(projectRoot, path) {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
function expandPaths(projectRoot, patterns) {
|
|
27
|
-
const
|
|
29
|
+
const layout = resolveLifecycleLayout(projectRoot);
|
|
30
|
+
const usePatterns = patterns.length > 0 ? patterns : [`${layout.artifactDir}/active/*${layout.artifactSuffix}`];
|
|
28
31
|
const out = [];
|
|
29
32
|
const seen = new Set();
|
|
30
33
|
for (const pattern of usePatterns) {
|
|
@@ -33,13 +36,14 @@ function expandPaths(projectRoot, patterns) {
|
|
|
33
36
|
const globPart = pattern.includes("/")
|
|
34
37
|
? pattern.slice(pattern.lastIndexOf("/") + 1)
|
|
35
38
|
: pattern;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const firstSeg = pattern.split("/")[0] ?? "";
|
|
40
|
+
const secondSeg = pattern.split("/")[1] ?? "";
|
|
41
|
+
const lifecyclePattern = LIFECYCLE_DIR_NAMES.includes(firstSeg);
|
|
42
|
+
if (lifecyclePattern && existsSync(join(projectRoot, firstSeg, secondSeg))) {
|
|
43
|
+
const activeDir = join(projectRoot, firstSeg, secondSeg);
|
|
44
|
+
for (const name of readdirSync(activeDir)) {
|
|
45
|
+
if (matchGlob(name, globPart)) {
|
|
46
|
+
addPath(join(activeDir, name), seen, out);
|
|
43
47
|
}
|
|
44
48
|
}
|
|
45
49
|
}
|
|
@@ -69,8 +73,10 @@ function dirnamePattern(projectRoot, pattern) {
|
|
|
69
73
|
return idx >= 0 ? join(projectRoot, pattern.slice(0, idx)) : projectRoot;
|
|
70
74
|
}
|
|
71
75
|
function matchGlob(name, glob) {
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
// Layout-aware (#2109 part 1): an artifact-suffix glob matches either suffix,
|
|
77
|
+
// so a mixed/either lifecycle tree resolves the same way.
|
|
78
|
+
if (ARTIFACT_SUFFIXES.some((suffix) => glob === `*${suffix}`)) {
|
|
79
|
+
return hasArtifactSuffix(name);
|
|
74
80
|
}
|
|
75
81
|
if (glob.startsWith("*.")) {
|
|
76
82
|
return name.endsWith(glob.slice(1));
|
|
@@ -115,7 +121,7 @@ function hasChildPlanRefs(plan) {
|
|
|
115
121
|
return refs.some((ref) => typeof ref === "object" &&
|
|
116
122
|
ref !== null &&
|
|
117
123
|
!Array.isArray(ref) &&
|
|
118
|
-
ref.type
|
|
124
|
+
referenceTypeMatches(String(ref.type ?? ""), "plan"));
|
|
119
125
|
}
|
|
120
126
|
function looksLikePhase(path, plan) {
|
|
121
127
|
const title = String(plan.title ?? "");
|
|
@@ -153,9 +159,7 @@ function storyId(path, plan) {
|
|
|
153
159
|
return value.trim();
|
|
154
160
|
}
|
|
155
161
|
const name = basename(path);
|
|
156
|
-
return name.
|
|
157
|
-
? name.slice(0, -".vbrief.json".length)
|
|
158
|
-
: name.replace(/\.[^.]+$/, "");
|
|
162
|
+
return hasArtifactSuffix(name) ? stripArtifactSuffix(name) : name.replace(/\.[^.]+$/, "");
|
|
159
163
|
}
|
|
160
164
|
function hasTraces(plan, swarm) {
|
|
161
165
|
const narratives = plan.narratives;
|
|
@@ -182,7 +186,7 @@ function hasTraces(plan, swarm) {
|
|
|
182
186
|
if (typeof ref === "object" &&
|
|
183
187
|
ref !== null &&
|
|
184
188
|
!Array.isArray(ref) &&
|
|
185
|
-
ref.type
|
|
189
|
+
referenceTypeMatches(String(ref.type ?? ""), "spec-section")) {
|
|
186
190
|
return true;
|
|
187
191
|
}
|
|
188
192
|
}
|
|
@@ -206,14 +210,14 @@ function acceptanceCountJustification(plan, swarm) {
|
|
|
206
210
|
}
|
|
207
211
|
function allScopeIds(projectRoot) {
|
|
208
212
|
const ids = new Map();
|
|
209
|
-
const vbriefDir =
|
|
213
|
+
const vbriefDir = resolveLifecycleRoot(projectRoot);
|
|
210
214
|
for (const folder of LIFECYCLE_FOLDERS) {
|
|
211
215
|
const dir = join(vbriefDir, folder);
|
|
212
216
|
if (!existsSync(dir)) {
|
|
213
217
|
continue;
|
|
214
218
|
}
|
|
215
219
|
for (const name of readdirSync(dir).sort()) {
|
|
216
|
-
if (!name
|
|
220
|
+
if (!hasArtifactSuffix(name)) {
|
|
217
221
|
continue;
|
|
218
222
|
}
|
|
219
223
|
const path = join(dir, name);
|
|
@@ -224,9 +228,7 @@ function allScopeIds(projectRoot) {
|
|
|
224
228
|
const plan = planOf(data);
|
|
225
229
|
const sid = storyId(path, plan);
|
|
226
230
|
ids.set(sid, [path, String(plan.status ?? "")]);
|
|
227
|
-
const stem = name
|
|
228
|
-
? name.slice(0, -".vbrief.json".length)
|
|
229
|
-
: name.replace(/\.[^.]+$/, "");
|
|
231
|
+
const stem = stripArtifactSuffix(name);
|
|
230
232
|
if (!ids.has(stem)) {
|
|
231
233
|
ids.set(stem, [path, String(plan.status ?? "")]);
|
|
232
234
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* @see {@link https://github.com/deftai/directive/issues/1739} Superseding PR
|
|
12
12
|
* @see {@link https://github.com/deftai/directive/issues/1860} Hard deletion tracking
|
|
13
13
|
*/
|
|
14
|
+
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
14
15
|
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
15
16
|
import { LEAF_CODING_WORKER_ROLE, SUBAGENT_BACKEND_SET_CMD } from "./constants.js";
|
|
16
17
|
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
@@ -63,11 +64,7 @@ export function probeSubagentBackends(environ = process.env) {
|
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
function getPolicyBlock(data) {
|
|
66
|
-
const
|
|
67
|
-
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
68
|
-
return {};
|
|
69
|
-
}
|
|
70
|
-
const policy = plan.policy;
|
|
67
|
+
const policy = readPlanPolicy(data.plan);
|
|
71
68
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
72
69
|
return {};
|
|
73
70
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
3
4
|
import { defaultRunGh, fetchGreptileCommentBody, fetchPrHeadSha, } from "../pr-merge-readiness/gh.js";
|
|
4
5
|
import { evaluateGates, parseGreptileBody } from "../pr-merge-readiness/index.js";
|
|
5
6
|
import { EXIT_EXTERNAL_ERROR, EXIT_OK, EXIT_UNCLEAN } from "./constants.js";
|
|
@@ -34,7 +35,7 @@ function globPaths(pattern) {
|
|
|
34
35
|
return [];
|
|
35
36
|
}
|
|
36
37
|
return readdirSync(dir)
|
|
37
|
-
.filter((name) => (glob === "*.vbrief.json" ? name
|
|
38
|
+
.filter((name) => (glob === "*.vbrief.json" ? hasArtifactSuffix(name) : name === glob))
|
|
38
39
|
.map((name) => resolve(dir, name));
|
|
39
40
|
}
|
|
40
41
|
return [resolve(pattern)];
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { AuditEntry, CandidatesLog } from "./types.js";
|
|
2
|
+
/** Display/back-compat constant; resolution flows through resolveEvalPath (#2109). */
|
|
2
3
|
export declare const AUDIT_LOG_REL_PATH = "vbrief/.eval/candidates.jsonl";
|
|
4
|
+
/** Layout-aware candidates audit-log path (xbrief when migrated, else vbrief). */
|
|
5
|
+
export declare function resolveCandidatesLogPath(projectRoot: string): string;
|
|
3
6
|
/** Read every well-formed audit-log row in insertion order (#1698 shared reader). */
|
|
4
7
|
export declare function readAuditLog(logPath: string, repo?: string | null): AuditEntry[];
|
|
5
8
|
/** Return every entry for ``(repo, issue_number)`` in insertion order. */
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { resolveEvalPath } from "../../layout/resolve.js";
|
|
4
5
|
import { CandidatesLogError } from "./errors.js";
|
|
6
|
+
/** Display/back-compat constant; resolution flows through resolveEvalPath (#2109). */
|
|
5
7
|
export const AUDIT_LOG_REL_PATH = "vbrief/.eval/candidates.jsonl";
|
|
8
|
+
/** Layout-aware candidates audit-log path (xbrief when migrated, else vbrief). */
|
|
9
|
+
export function resolveCandidatesLogPath(projectRoot) {
|
|
10
|
+
return resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
11
|
+
}
|
|
6
12
|
const VALID_DECISIONS = new Set([
|
|
7
13
|
"accept",
|
|
8
14
|
"reject",
|
|
@@ -103,7 +109,7 @@ function resolveLogPath(projectRoot, override) {
|
|
|
103
109
|
if (override !== undefined) {
|
|
104
110
|
return override;
|
|
105
111
|
}
|
|
106
|
-
return
|
|
112
|
+
return resolveCandidatesLogPath(projectRoot);
|
|
107
113
|
}
|
|
108
114
|
function stableStringify(entry) {
|
|
109
115
|
const sortedKeys = Object.keys(entry).sort();
|
|
@@ -227,6 +233,6 @@ export function rollbackAuditEntry(decisionId, projectRoot, logPathOverride) {
|
|
|
227
233
|
return removed;
|
|
228
234
|
}
|
|
229
235
|
export function resolveAuditLogPath(projectRoot) {
|
|
230
|
-
return
|
|
236
|
+
return resolveCandidatesLogPath(projectRoot);
|
|
231
237
|
}
|
|
232
238
|
//# sourceMappingURL=candidates-log.js.map
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
2
|
+
import { dirname, relative } from "node:path";
|
|
3
|
+
import { resolveEvalPath } from "../../layout/resolve.js";
|
|
4
|
+
/** POSIX-style display path for `absPath` relative to `projectRoot` (#2109). */
|
|
5
|
+
function evalRelDisplay(projectRoot, absPath) {
|
|
6
|
+
return relative(projectRoot, absPath).split(/[\\/]/).join("/");
|
|
7
|
+
}
|
|
3
8
|
export const GITIGNORE_LINE = ".deft-cache/";
|
|
4
9
|
export const GITIGNORE_DEFT_RUNTIME_SENTINELS = [
|
|
5
10
|
".deft/ritual-state.json",
|
|
@@ -126,7 +131,6 @@ operators should know:
|
|
|
126
131
|
- \`.gitattributes\` -- the \`merge=union\` rule.
|
|
127
132
|
- \`scripts/candidates_log.py\` -- the writer for \`candidates.jsonl\`.
|
|
128
133
|
`;
|
|
129
|
-
const CANDIDATES_RELPATH = "vbrief/.eval/candidates.jsonl";
|
|
130
134
|
function stepOutcome(name, ok, message, details = {}, error = null) {
|
|
131
135
|
return { name, ok, message, error, details };
|
|
132
136
|
}
|
|
@@ -307,10 +311,10 @@ function ensureGitattributesMergeUnion(gitattributesPath, stepName) {
|
|
|
307
311
|
}
|
|
308
312
|
return stepOutcome(stepName, true, "created .gitattributes with vbrief/.eval/*.jsonl merge=union", { gitattributes_appended: true, gitattributes_created: true });
|
|
309
313
|
}
|
|
310
|
-
function ensureEvalReadme(readmePath, stepName) {
|
|
314
|
+
function ensureEvalReadme(readmePath, readmeRel, stepName) {
|
|
311
315
|
try {
|
|
312
316
|
readFileSync(readmePath, { encoding: "utf8" });
|
|
313
|
-
return stepOutcome(stepName, true,
|
|
317
|
+
return stepOutcome(stepName, true, `${readmeRel} already present (no-op)`, {
|
|
314
318
|
readme_created: false,
|
|
315
319
|
readme_already_present: true,
|
|
316
320
|
});
|
|
@@ -325,7 +329,7 @@ function ensureEvalReadme(readmePath, stepName) {
|
|
|
325
329
|
catch (exc) {
|
|
326
330
|
return stepOutcome(stepName, false, `could not create ${readmePath}`, { readme_created: false }, String(exc));
|
|
327
331
|
}
|
|
328
|
-
return stepOutcome(stepName, true,
|
|
332
|
+
return stepOutcome(stepName, true, `created ${readmeRel} (#1144 hybrid policy)`, {
|
|
329
333
|
readme_created: true,
|
|
330
334
|
});
|
|
331
335
|
}
|
|
@@ -333,7 +337,10 @@ function ensureEvalReadme(readmePath, stepName) {
|
|
|
333
337
|
export function stepEnsureGitignoreEvalEntries(projectRoot) {
|
|
334
338
|
const gitignorePath = `${projectRoot}/.gitignore`;
|
|
335
339
|
const gitattributesPath = `${projectRoot}/.gitattributes`;
|
|
336
|
-
|
|
340
|
+
// Layout-aware (#2109): resolve the README under the active lifecycle `.eval`
|
|
341
|
+
// dir (xbrief/ when migrated, else vbrief/) instead of a hardcoded vbrief/ path.
|
|
342
|
+
const readmePath = resolveEvalPath(projectRoot, "README.md");
|
|
343
|
+
const readmeRel = evalRelDisplay(projectRoot, readmePath);
|
|
337
344
|
const stepName = "ensure_gitignore_eval_entries";
|
|
338
345
|
const details = {};
|
|
339
346
|
const giResult = ensureGitignoreSelectiveEntries(gitignorePath, stepName);
|
|
@@ -348,7 +355,7 @@ export function stepEnsureGitignoreEvalEntries(projectRoot) {
|
|
|
348
355
|
return stepOutcome(stepName, false, gaResult.message, details, gaResult.error ?? null);
|
|
349
356
|
}
|
|
350
357
|
Object.assign(details, gaResult.details);
|
|
351
|
-
const rdResult = ensureEvalReadme(readmePath, stepName);
|
|
358
|
+
const rdResult = ensureEvalReadme(readmePath, readmeRel, stepName);
|
|
352
359
|
if (!rdResult.ok) {
|
|
353
360
|
Object.assign(details, rdResult.details);
|
|
354
361
|
return stepOutcome(stepName, false, rdResult.message, details, rdResult.error ?? null);
|
|
@@ -361,7 +368,7 @@ export function stepEnsureGitignoreEvalEntries(projectRoot) {
|
|
|
361
368
|
if (appendedLines === 0 && !appendedAttr && !createdReadme) {
|
|
362
369
|
message =
|
|
363
370
|
".gitignore selective entries, .gitattributes merge=union, " +
|
|
364
|
-
|
|
371
|
+
`and ${readmeRel} already present (#1144 hybrid ` +
|
|
365
372
|
"policy satisfied; no-op)";
|
|
366
373
|
}
|
|
367
374
|
else {
|
|
@@ -373,7 +380,7 @@ export function stepEnsureGitignoreEvalEntries(projectRoot) {
|
|
|
373
380
|
if (appendedAttr)
|
|
374
381
|
parts.push(".gitattributes merge=union rule");
|
|
375
382
|
if (createdReadme)
|
|
376
|
-
parts.push(
|
|
383
|
+
parts.push(readmeRel);
|
|
377
384
|
message = `wrote ${parts.join(" + ")} per #1144 hybrid policy`;
|
|
378
385
|
}
|
|
379
386
|
message += formatBlanketWarning(Boolean(details.blanket_present));
|
|
@@ -381,7 +388,10 @@ export function stepEnsureGitignoreEvalEntries(projectRoot) {
|
|
|
381
388
|
}
|
|
382
389
|
/** Ensure `vbrief/.eval/candidates.jsonl` exists (#1240 option A). */
|
|
383
390
|
export function stepSeedCandidatesLog(projectRoot) {
|
|
384
|
-
|
|
391
|
+
// Layout-aware (#2109): seed under the active lifecycle `.eval` dir (xbrief/
|
|
392
|
+
// when migrated, else vbrief/) instead of a hardcoded vbrief/ path.
|
|
393
|
+
const auditPath = resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
394
|
+
const auditRel = evalRelDisplay(projectRoot, auditPath);
|
|
385
395
|
const auditDir = dirname(auditPath);
|
|
386
396
|
try {
|
|
387
397
|
mkdirSync(auditDir, { recursive: true });
|
|
@@ -391,8 +401,7 @@ export function stepSeedCandidatesLog(projectRoot) {
|
|
|
391
401
|
}
|
|
392
402
|
try {
|
|
393
403
|
readFileSync(auditPath, { encoding: "utf8" });
|
|
394
|
-
|
|
395
|
-
return stepOutcome("seed_candidates_log", true, `${relative} already present (no-op)`, {
|
|
404
|
+
return stepOutcome("seed_candidates_log", true, `${auditRel} already present (no-op)`, {
|
|
396
405
|
created: false,
|
|
397
406
|
already_present: true,
|
|
398
407
|
});
|
|
@@ -406,7 +415,7 @@ export function stepSeedCandidatesLog(projectRoot) {
|
|
|
406
415
|
catch (exc) {
|
|
407
416
|
return stepOutcome("seed_candidates_log", false, `could not seed ${auditPath}`, {}, String(exc));
|
|
408
417
|
}
|
|
409
|
-
return stepOutcome("seed_candidates_log", true, `created empty ${
|
|
418
|
+
return stepOutcome("seed_candidates_log", true, `created empty ${auditRel}`, {
|
|
410
419
|
created: true,
|
|
411
420
|
already_present: false,
|
|
412
421
|
});
|
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
|
+
import { hasArtifactSuffix, resolveEvalPath, resolveLifecycleRoot } from "../../layout/resolve.js";
|
|
6
7
|
import { SUBPROCESS_MAX_BUFFER } from "../../subprocess/max-buffer.js";
|
|
7
8
|
import { stepEnsureGitignoreEntry, stepEnsureGitignoreEvalEntries, stepSeedCandidatesLog, } from "./gitignore.js";
|
|
8
9
|
import { PROGRESS_DEFAULT } from "./types.js";
|
|
@@ -296,7 +297,9 @@ function extractIssueNumber(vbriefData) {
|
|
|
296
297
|
if (typeof ref !== "object" || ref === null)
|
|
297
298
|
continue;
|
|
298
299
|
const typed = ref;
|
|
299
|
-
|
|
300
|
+
// Dual-namespace (#2109): accept the migrated `x-xbrief/` corpus token and
|
|
301
|
+
// the legacy `x-vbrief/` token (still read-accepted for back-compat).
|
|
302
|
+
if (typed.type !== "x-xbrief/github-issue" && typed.type !== "x-vbrief/github-issue")
|
|
300
303
|
continue;
|
|
301
304
|
const uri = typed.uri;
|
|
302
305
|
if (typeof uri !== "string")
|
|
@@ -312,7 +315,7 @@ function scanLifecycleFolder(folder) {
|
|
|
312
315
|
if (!existsSync(folder))
|
|
313
316
|
return results;
|
|
314
317
|
const entries = readdirSync(folder)
|
|
315
|
-
.filter((name) => name
|
|
318
|
+
.filter((name) => hasArtifactSuffix(name))
|
|
316
319
|
.sort();
|
|
317
320
|
for (const name of entries) {
|
|
318
321
|
const path = join(folder, name);
|
|
@@ -370,11 +373,11 @@ export function stepBackfillAuditLog(projectRoot, repo, options = {}) {
|
|
|
370
373
|
if (repo === null) {
|
|
371
374
|
return stepOutcome("backfill_audit_log", true, "skipped (no --repo provided; pass --repo OWNER/NAME to backfill)", { skipped: "no-repo" });
|
|
372
375
|
}
|
|
373
|
-
const vbriefRoot =
|
|
376
|
+
const vbriefRoot = resolveLifecycleRoot(projectRoot);
|
|
374
377
|
if (!existsSync(vbriefRoot)) {
|
|
375
378
|
return stepOutcome("backfill_audit_log", true, `skipped (no vbrief/ directory under ${projectRoot})`, { skipped: "no-vbrief" });
|
|
376
379
|
}
|
|
377
|
-
const auditPath =
|
|
380
|
+
const auditPath = resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
378
381
|
const alreadyLogged = existingAuditIssueNumbers(auditPath);
|
|
379
382
|
const nowIso = options.nowIso ?? nowIsoDefault;
|
|
380
383
|
const appendEntry = options.appendAuditEntry ?? appendAuditEntryDefault;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { resolveEvalPath } from "../../layout/resolve.js";
|
|
4
5
|
export const ACTION_FN_NAMES = {
|
|
5
6
|
accept: "accept",
|
|
6
7
|
reject: "reject",
|
|
@@ -375,7 +376,7 @@ export function createFilesystemCacheModule() {
|
|
|
375
376
|
CacheError,
|
|
376
377
|
};
|
|
377
378
|
}
|
|
378
|
-
export function createFilesystemCandidatesLogModule(logPath = "
|
|
379
|
+
export function createFilesystemCandidatesLogModule(logPath = resolveEvalPath(process.cwd(), "candidates.jsonl")) {
|
|
379
380
|
return {
|
|
380
381
|
readAll(options) {
|
|
381
382
|
if (!existsSync(logPath)) {
|