@deftai/directive-core 0.99.0 → 0.100.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/authz/actions.d.ts +6 -0
- package/dist/authz/actions.js +3 -0
- package/dist/authz/classify.js +157 -23
- package/dist/authz/decompose-apply.d.ts +67 -0
- package/dist/authz/decompose-apply.js +302 -0
- package/dist/authz/index.d.ts +3 -1
- package/dist/authz/index.js +3 -1
- package/dist/authz/store.d.ts +53 -0
- package/dist/authz/store.js +219 -1
- package/dist/authz/types.d.ts +19 -2
- package/dist/authz/types.js +5 -1
- package/dist/doctor/checks.d.ts +44 -0
- package/dist/doctor/checks.js +428 -2
- package/dist/doctor/main.d.ts +6 -7
- package/dist/doctor/main.js +72 -79
- package/dist/doctor/which.d.ts +20 -1
- package/dist/doctor/which.js +67 -1
- package/dist/finish-loop/pr-finish-loop.d.ts +13 -2
- package/dist/finish-loop/pr-finish-loop.js +116 -5
- package/dist/finish-loop/types.d.ts +1 -1
- package/dist/hooks/dispatcher.d.ts +9 -5
- package/dist/hooks/dispatcher.js +23 -15
- package/dist/hooks/readonly.d.ts +6 -3
- package/dist/hooks/readonly.js +44 -25
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init-deposit/agent-hooks.d.ts +20 -0
- package/dist/init-deposit/agent-hooks.js +51 -31
- package/dist/intake/issue-ingest.js +29 -0
- package/dist/lifecycle/completed-consistency.d.ts +60 -0
- package/dist/lifecycle/completed-consistency.js +443 -0
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +47 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +415 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +52 -0
- package/dist/literal-acceptance/capture.js +658 -0
- package/dist/literal-acceptance/evaluate.d.ts +47 -0
- package/dist/literal-acceptance/evaluate.js +193 -0
- package/dist/literal-acceptance/index.d.ts +12 -0
- package/dist/literal-acceptance/index.js +12 -0
- package/dist/literal-acceptance/run.d.ts +38 -0
- package/dist/literal-acceptance/run.js +216 -0
- package/dist/literal-acceptance/safety.d.ts +24 -0
- package/dist/literal-acceptance/safety.js +251 -0
- package/dist/literal-acceptance/types.d.ts +82 -0
- package/dist/literal-acceptance/types.js +26 -0
- package/dist/policy/ceremony-dial.d.ts +67 -11
- package/dist/policy/ceremony-dial.js +174 -23
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +1 -0
- package/dist/policy/merge-approval-head.d.ts +129 -0
- package/dist/policy/merge-approval-head.js +461 -0
- package/dist/policy/require-human-merge.d.ts +5 -0
- package/dist/policy/require-human-merge.js +5 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +20 -3
- package/dist/pr-merge-readiness/ci-gate.js +65 -5
- package/dist/pr-merge-readiness/compute.d.ts +13 -0
- package/dist/pr-merge-readiness/compute.js +99 -14
- package/dist/pr-merge-readiness/gh.d.ts +49 -0
- package/dist/pr-merge-readiness/gh.js +268 -0
- package/dist/pr-merge-readiness/index.d.ts +2 -2
- package/dist/pr-merge-readiness/index.js +2 -2
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +4 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +11 -0
- package/dist/pr-wait-mergeable/cascade.js +115 -1
- package/dist/pr-wait-mergeable/main.d.ts +4 -0
- package/dist/pr-wait-mergeable/main.js +4 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -1
- package/dist/pr-wait-mergeable/wrappers.js +7 -1
- package/dist/preflight/evaluate.d.ts +15 -1
- package/dist/preflight/evaluate.js +33 -2
- package/dist/render/framework-commands.js +4 -0
- package/dist/scope/acceptance-evidence.d.ts +76 -0
- package/dist/scope/acceptance-evidence.js +348 -0
- package/dist/scope/coverage-map.d.ts +98 -0
- package/dist/scope/coverage-map.js +558 -0
- package/dist/scope/decompose.js +125 -11
- package/dist/scope/index.d.ts +3 -0
- package/dist/scope/index.js +3 -0
- package/dist/scope/parent-lineage.d.ts +125 -0
- package/dist/scope/parent-lineage.js +626 -0
- package/dist/scope/transition.d.ts +8 -0
- package/dist/scope/transition.js +61 -2
- package/dist/session/active-cli.d.ts +79 -0
- package/dist/session/active-cli.js +382 -0
- package/dist/session/effort-budget.d.ts +130 -0
- package/dist/session/effort-budget.js +334 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/session-start.d.ts +6 -0
- package/dist/session/session-start.js +53 -2
- package/dist/session/verify-session-ritual.d.ts +14 -0
- package/dist/session/verify-session-ritual.js +33 -0
- package/dist/story-ready/evaluate.d.ts +11 -0
- package/dist/story-ready/evaluate.js +41 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -2
- package/dist/triage/actions/candidates-log.js +37 -6
- package/dist/xbrief-migrate/migrate-project.d.ts +4 -0
- package/dist/xbrief-migrate/migrate-project.js +92 -3
- package/package.json +7 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
+
import { readCorePackageVersion } from "../engine-version.js";
|
|
2
3
|
import { formatFrameworkCommand } from "../render/framework-commands.js";
|
|
4
|
+
import { checkActiveCliAgainstTarget, } from "./active-cli.js";
|
|
3
5
|
import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
|
|
4
6
|
import { pythonJsonDump } from "./json.js";
|
|
5
7
|
import { ENV_SESSION_POSTURE, readOnlyPostureMessage, resolveSessionPosture, ritualStateIsPostureAuthority, } from "./posture.js";
|
|
@@ -389,6 +391,37 @@ export function verifySessionRitual(projectRoot, options = {}) {
|
|
|
389
391
|
ritualStateRequired,
|
|
390
392
|
};
|
|
391
393
|
}
|
|
394
|
+
// #3233: after gated entrypoints, verify the shell-active deft/directive
|
|
395
|
+
// is not a stale higher-precedence shadow of a multi-prefix install.
|
|
396
|
+
// Independent target = explicit option, else in-process publishable engine
|
|
397
|
+
// (never the active CLI's own version — see resolveDefaultActiveCliTarget).
|
|
398
|
+
const targetEngineVersion = options.targetEngineVersion ?? null;
|
|
399
|
+
const checkActiveCli = options.checkActiveCli ?? checkActiveCliAgainstTarget;
|
|
400
|
+
let inProcessVersion = null;
|
|
401
|
+
try {
|
|
402
|
+
inProcessVersion = readCorePackageVersion();
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
inProcessVersion = null;
|
|
406
|
+
}
|
|
407
|
+
const activeCliSeams = {
|
|
408
|
+
inProcessVersion,
|
|
409
|
+
...options.activeCliSeams,
|
|
410
|
+
};
|
|
411
|
+
const activeCli = checkActiveCli(targetEngineVersion, activeCliSeams);
|
|
412
|
+
if (!activeCli.ok) {
|
|
413
|
+
return {
|
|
414
|
+
code: activeCli.code === 0 ? 1 : activeCli.code,
|
|
415
|
+
message: activeCli.lines.length > 0 ? activeCli.lines.join("\n") : activeCli.message,
|
|
416
|
+
tier,
|
|
417
|
+
statePath,
|
|
418
|
+
bypassed: false,
|
|
419
|
+
wouldFailCode: null,
|
|
420
|
+
posture,
|
|
421
|
+
ritualStateRequired,
|
|
422
|
+
recoveryTier: "cold",
|
|
423
|
+
};
|
|
424
|
+
}
|
|
392
425
|
}
|
|
393
426
|
const evaluated = evaluateLoadedState(projectRoot, state, {
|
|
394
427
|
tier,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type PathLike } from "node:fs";
|
|
2
|
+
import { type ParentLineageResult } from "../scope/parent-lineage.js";
|
|
2
3
|
import { type ParsedAllocation, parseAllocationSection, SOLO_KIND, SWARM_COHORT_KIND } from "./allocation.js";
|
|
3
4
|
export declare const ACTIVE_FOLDER = "active";
|
|
4
5
|
export declare const ELIGIBLE_STATUS = "running";
|
|
@@ -7,17 +8,27 @@ export interface EvaluateResult {
|
|
|
7
8
|
readonly exitCode: 0 | 1 | 2;
|
|
8
9
|
readonly message: string;
|
|
9
10
|
readonly dispatchKind: string | null;
|
|
11
|
+
/** #3241 parent-lineage probe (present when vBRIEF checks ran). */
|
|
12
|
+
readonly parentLineage?: ParentLineageResult;
|
|
10
13
|
}
|
|
11
14
|
export interface EvaluateOptions {
|
|
12
15
|
readonly gitStatus?: string | null;
|
|
13
16
|
readonly allocationContext?: string | null;
|
|
14
17
|
readonly allowDirty?: boolean;
|
|
15
18
|
readonly parsed?: ParsedAllocation;
|
|
19
|
+
/** Project root for resolving child planRef → parent (#3241). */
|
|
20
|
+
readonly projectRoot?: string;
|
|
21
|
+
/** Skip parent-lineage check (tests / opt-out). Default false. */
|
|
22
|
+
readonly skipParentLineage?: boolean;
|
|
16
23
|
}
|
|
17
24
|
/**
|
|
18
25
|
* Pure evaluator — returns exit code + human message. Faithful to
|
|
19
26
|
* `scripts/preflight_story_start.evaluate`.
|
|
27
|
+
*
|
|
28
|
+
* #3241: after structural active+running checks, re-check parent requirement
|
|
29
|
+
* lineage (coverage map + behavioral deltas) when the parent authors IDs.
|
|
20
30
|
*/
|
|
21
31
|
export declare function evaluate(vbriefPath: PathLike, options?: EvaluateOptions): EvaluateResult;
|
|
32
|
+
export { evaluateParentLineage, formatParentLineageLine, type ParentLineageResult, } from "../scope/parent-lineage.js";
|
|
22
33
|
export { parseAllocationSection, SOLO_KIND, SWARM_COHORT_KIND };
|
|
23
34
|
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { evaluateParentLineage, formatParentLineageLine, } from "../scope/parent-lineage.js";
|
|
3
4
|
import { parseAllocationSection, SOLO_KIND, SWARM_COHORT_KIND, VALID_DISPATCH_KINDS, } from "./allocation.js";
|
|
4
5
|
export const ACTIVE_FOLDER = "active";
|
|
5
6
|
export const ELIGIBLE_STATUS = "running";
|
|
@@ -66,7 +67,7 @@ function checkVbrief(vbriefPath) {
|
|
|
66
67
|
"is eligible for a story start",
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
|
-
return { ok: true };
|
|
70
|
+
return { ok: true, path, payload: payload };
|
|
70
71
|
}
|
|
71
72
|
function readyMessage(treeNote, suffix) {
|
|
72
73
|
return `OK: ready to start -- ${treeNote}, vBRIEF active+running, ${suffix}`;
|
|
@@ -116,6 +117,9 @@ function classifyAllocation(fields, treeNote) {
|
|
|
116
117
|
/**
|
|
117
118
|
* Pure evaluator — returns exit code + human message. Faithful to
|
|
118
119
|
* `scripts/preflight_story_start.evaluate`.
|
|
120
|
+
*
|
|
121
|
+
* #3241: after structural active+running checks, re-check parent requirement
|
|
122
|
+
* lineage (coverage map + behavioral deltas) when the parent authors IDs.
|
|
119
123
|
*/
|
|
120
124
|
export function evaluate(vbriefPath, options = {}) {
|
|
121
125
|
const gitStatus = options.gitStatus ?? null;
|
|
@@ -147,15 +151,49 @@ export function evaluate(vbriefPath, options = {}) {
|
|
|
147
151
|
message: `not ready: ${vbriefCheck.reason}.`,
|
|
148
152
|
};
|
|
149
153
|
}
|
|
154
|
+
// #3241 parent lineage (fail closed when parent authors IDs and child lacks coverage).
|
|
155
|
+
const lineage = evaluateParentLineage({
|
|
156
|
+
child: vbriefCheck.payload,
|
|
157
|
+
childPath: vbriefCheck.path,
|
|
158
|
+
projectRoot: options.projectRoot,
|
|
159
|
+
skip: options.skipParentLineage === true,
|
|
160
|
+
});
|
|
161
|
+
if (!lineage.ok) {
|
|
162
|
+
return {
|
|
163
|
+
exitCode: 1,
|
|
164
|
+
dispatchKind: null,
|
|
165
|
+
parentLineage: lineage,
|
|
166
|
+
message: `not ready: ${lineage.message}` +
|
|
167
|
+
(lineage.defect_class !== null ? ` [defect_class=${lineage.defect_class}]` : "") +
|
|
168
|
+
`\n${formatParentLineageLine(lineage)}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const lineageNote = lineage.applicable
|
|
172
|
+
? `parent lineage OK (${lineage.parent_requirement_ids.length} req IDs` +
|
|
173
|
+
(lineage.negative_invariant_ids.length > 0
|
|
174
|
+
? `, ${lineage.negative_invariant_ids.length} negative invariants`
|
|
175
|
+
: "") +
|
|
176
|
+
")"
|
|
177
|
+
: null;
|
|
150
178
|
const [found, fields] = options.parsed ?? parseAllocationSection(options.allocationContext ?? null);
|
|
151
179
|
if (!found) {
|
|
180
|
+
const base = readyMessage(treeNote, "no `## Allocation context` section (solo path, #1371 carve-out).");
|
|
152
181
|
return {
|
|
153
182
|
exitCode: 0,
|
|
154
183
|
dispatchKind: null,
|
|
155
|
-
|
|
184
|
+
parentLineage: lineage,
|
|
185
|
+
message: lineageNote !== null ? `${base.replace(/\.$/, "")}; ${lineageNote}.` : base,
|
|
156
186
|
};
|
|
157
187
|
}
|
|
158
|
-
|
|
188
|
+
const classified = classifyAllocation(fields, treeNote);
|
|
189
|
+
return {
|
|
190
|
+
...classified,
|
|
191
|
+
parentLineage: lineage,
|
|
192
|
+
message: classified.exitCode === 0 && lineageNote !== null
|
|
193
|
+
? `${classified.message.replace(/\.$/, "")}; ${lineageNote}.`
|
|
194
|
+
: classified.message,
|
|
195
|
+
};
|
|
159
196
|
}
|
|
197
|
+
export { evaluateParentLineage, formatParentLineageLine, } from "../scope/parent-lineage.js";
|
|
160
198
|
export { parseAllocationSection, SOLO_KIND, SWARM_COHORT_KIND };
|
|
161
199
|
//# sourceMappingURL=evaluate.js.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { resolveCandidatesLogPath } from "../cache-path.js";
|
|
2
|
-
import type { AuditEntry, CandidatesLog } from "./types.js";
|
|
3
|
-
/** Display/back-compat constant; resolution flows through resolveTriageCachePath (#1703). */
|
|
2
|
+
import type { AuditEntry, CandidatesLog } from "./types.js"; /** Display/back-compat constant; resolution flows through resolveTriageCachePath (#1703). */
|
|
4
3
|
export declare const AUDIT_LOG_REL_PATH = "xbrief/.triage-cache/candidates.jsonl";
|
|
5
4
|
export { resolveCandidatesLogPath };
|
|
6
5
|
/** Read every well-formed audit-log row in insertion order (#1698 shared reader). */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
import { containedWrite } from "../../fs/contained-write.js";
|
|
4
5
|
import { resolveCandidatesLogPath, TRIAGE_CANDIDATES_LOG_REL_PATH } from "../cache-path.js";
|
|
5
6
|
import { CandidatesLogError } from "./errors.js";
|
|
6
|
-
/** Display/back-compat constant; resolution flows through resolveTriageCachePath (#1703). */
|
|
7
7
|
export const AUDIT_LOG_REL_PATH = TRIAGE_CANDIDATES_LOG_REL_PATH;
|
|
8
8
|
export { resolveCandidatesLogPath };
|
|
9
9
|
const VALID_DECISIONS = new Set([
|
|
@@ -108,6 +108,32 @@ function resolveLogPath(projectRoot, override) {
|
|
|
108
108
|
}
|
|
109
109
|
return resolveCandidatesLogPath(projectRoot);
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Containment root for candidates-log product writes (#3245).
|
|
113
|
+
* Prefer project root when the log is nested under it; otherwise parent dir
|
|
114
|
+
* (matches intake candidates-log parity for override paths outside the tree).
|
|
115
|
+
*/
|
|
116
|
+
function containmentRootForLog(projectRoot, logPath) {
|
|
117
|
+
const rootAbs = resolve(projectRoot);
|
|
118
|
+
const logAbs = resolve(logPath);
|
|
119
|
+
const parent = dirname(logAbs);
|
|
120
|
+
mkdirSync(parent, { recursive: true });
|
|
121
|
+
const rel = relative(rootAbs, logAbs);
|
|
122
|
+
if (rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel)) {
|
|
123
|
+
return rootAbs;
|
|
124
|
+
}
|
|
125
|
+
return resolve(parent);
|
|
126
|
+
}
|
|
127
|
+
/** #3245 / #2980: product append refuses leaf symlink follow (in-tree or escape). */
|
|
128
|
+
function appendAuditLine(projectRoot, logPath, line) {
|
|
129
|
+
const root = containmentRootForLog(projectRoot, logPath);
|
|
130
|
+
containedWrite({
|
|
131
|
+
root,
|
|
132
|
+
target: logPath,
|
|
133
|
+
data: line.endsWith("\n") ? line : `${line}\n`,
|
|
134
|
+
mode: "append",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
111
137
|
function stableStringify(entry) {
|
|
112
138
|
const sortedKeys = Object.keys(entry).sort();
|
|
113
139
|
const sorted = {};
|
|
@@ -186,8 +212,7 @@ export function createCandidatesLog(defaultProjectRoot) {
|
|
|
186
212
|
append(entry, options) {
|
|
187
213
|
validateEntry(entry);
|
|
188
214
|
const logPath = resolveLogPath(defaultProjectRoot, options?.path);
|
|
189
|
-
|
|
190
|
-
appendFileSync(logPath, `${stableStringify(entry)}\n`, { encoding: "utf8" });
|
|
215
|
+
appendAuditLine(defaultProjectRoot, logPath, `${stableStringify(entry)}\n`);
|
|
191
216
|
return entry.decision_id;
|
|
192
217
|
},
|
|
193
218
|
latestDecision(issueNumber, repo, options) {
|
|
@@ -225,7 +250,13 @@ export function rollbackAuditEntry(decisionId, projectRoot, logPathOverride) {
|
|
|
225
250
|
}
|
|
226
251
|
}
|
|
227
252
|
if (removed) {
|
|
228
|
-
|
|
253
|
+
// #3245: replace rewrite also refuses leaf symlink follow.
|
|
254
|
+
containedWrite({
|
|
255
|
+
root: containmentRootForLog(projectRoot, path),
|
|
256
|
+
target: path,
|
|
257
|
+
data: kept.join(""),
|
|
258
|
+
mode: "replace",
|
|
259
|
+
});
|
|
229
260
|
}
|
|
230
261
|
return removed;
|
|
231
262
|
}
|
|
@@ -24,6 +24,10 @@ export type XbriefMigrationOutcome = {
|
|
|
24
24
|
readonly kind: "migrated";
|
|
25
25
|
readonly backupDir: string;
|
|
26
26
|
readonly files: number;
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: "rewritten";
|
|
29
|
+
readonly files: number;
|
|
30
|
+
readonly message: string;
|
|
27
31
|
} | {
|
|
28
32
|
readonly kind: "converged";
|
|
29
33
|
readonly action: VbriefConvergeAction;
|
|
@@ -5,12 +5,12 @@ import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/project
|
|
|
5
5
|
import { checkGitClean } from "../migrate-preflight/index.js";
|
|
6
6
|
import { applyAgentsRefresh } from "../platform/agents-md.js";
|
|
7
7
|
import { patchAgentsMdHeader, renderHeaderPatchSummary } from "./agents-header.js";
|
|
8
|
-
import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME, VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, } from "./constants.js";
|
|
8
|
+
import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX, LEGACY_VBRIEF_VERSION, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME, VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, } from "./constants.js";
|
|
9
9
|
import { detectLegacyVbriefLayout, detectXbriefConvergence } from "./detect.js";
|
|
10
10
|
import { hasVbriefDeprecationMarker, isDirectory, isEffectivelyEmptyDir } from "./fs-helpers.js";
|
|
11
11
|
import { assertMigrationSourceSafe } from "./migration-containment.js";
|
|
12
12
|
import { renderXbriefMigrationLine, xbriefMigrationGuidance } from "./signpost.js";
|
|
13
|
-
import { rewriteEmbeddedTokens, transformArtifactV06ToV08Transactional } from "./transforms.js";
|
|
13
|
+
import { readDeclaredArtifactVersion, rewriteEmbeddedTokens, transformArtifactV06ToV08Transactional, } from "./transforms.js";
|
|
14
14
|
function collectFiles(root, acc = []) {
|
|
15
15
|
if (!isDirectory(root)) {
|
|
16
16
|
return acc;
|
|
@@ -74,6 +74,65 @@ function writeMigratedFile(projectRoot, srcPath, destPath) {
|
|
|
74
74
|
mode: "replace",
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Plan in-place hybrid envelope rewrites for xbrief lifecycle `.xbrief.json`
|
|
79
|
+
* artifacts that still declare `xBRIEFInfo@0.6` after the layout rename
|
|
80
|
+
* (#3236 / #2974). Pure probe — no writes. Already-0.8 artifacts, schema
|
|
81
|
+
* deposits, and non-0.6 JSON are skipped so residual #2368 schema-only markers
|
|
82
|
+
* still route to `directive update`.
|
|
83
|
+
*/
|
|
84
|
+
function planHybridEnvelopeRewrites(projectRoot) {
|
|
85
|
+
const migratedDir = join(projectRoot, MIGRATED_ARTIFACT_DIR);
|
|
86
|
+
const artifactPaths = collectFiles(migratedDir).filter((path) => path.endsWith(MIGRATED_ARTIFACT_SUFFIX));
|
|
87
|
+
const pending = [];
|
|
88
|
+
for (const filePath of artifactPaths) {
|
|
89
|
+
let parsed;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
error: `failed to parse hybrid envelope candidate ${relative(projectRoot, filePath)}: ${detail}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Only residual v0.6 envelopes are rewrite candidates; skip 0.8 and unknown.
|
|
101
|
+
if (readDeclaredArtifactVersion(parsed) !== LEGACY_VBRIEF_VERSION) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const result = transformArtifactV06ToV08Transactional(parsed);
|
|
105
|
+
if (!result.ok) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
error: `failed to transform hybrid envelope ${relative(projectRoot, filePath)}: ${result.error}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!result.changed) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
pending.push({
|
|
115
|
+
path: filePath,
|
|
116
|
+
body: `${JSON.stringify(result.artifact, null, 2)}\n`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { ok: true, pending };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Apply a previously planned hybrid-envelope rewrite set via containedWrite (#3236).
|
|
123
|
+
*/
|
|
124
|
+
function applyHybridEnvelopeRewrites(projectRoot, pending) {
|
|
125
|
+
const root = resolve(projectRoot);
|
|
126
|
+
for (const entry of pending) {
|
|
127
|
+
containedWrite({
|
|
128
|
+
root,
|
|
129
|
+
target: entry.path,
|
|
130
|
+
data: entry.body,
|
|
131
|
+
mode: "replace",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return pending.length;
|
|
135
|
+
}
|
|
77
136
|
function backupMigrationInputs(projectRoot, legacyDir, migratedDir) {
|
|
78
137
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
79
138
|
const backupRoot = join(projectRoot, ".deft", `xbrief-migrate-backup-${stamp}`);
|
|
@@ -256,6 +315,33 @@ export function runXbriefMigration(args, _io) {
|
|
|
256
315
|
default:
|
|
257
316
|
break;
|
|
258
317
|
}
|
|
318
|
+
// #3236: on already-xbrief trees with no vbrief/, rewrite residual hybrid
|
|
319
|
+
// xBRIEFInfo@0.6 envelopes in place (transform accepts hybrid — #2974).
|
|
320
|
+
// Run before the layout-detection short-circuit so compact `"version":"0.6"`
|
|
321
|
+
// JSON that the string-scan may miss still gets rewritten.
|
|
322
|
+
if (convergence.xbriefHasContent && !convergence.vbriefPresent) {
|
|
323
|
+
const plan = planHybridEnvelopeRewrites(projectRoot);
|
|
324
|
+
if (!plan.ok) {
|
|
325
|
+
return { kind: "config", message: plan.error };
|
|
326
|
+
}
|
|
327
|
+
if (plan.pending.length > 0) {
|
|
328
|
+
if (!args.force) {
|
|
329
|
+
const git = checkGitClean(projectRoot);
|
|
330
|
+
if (git.status === "WARN") {
|
|
331
|
+
return {
|
|
332
|
+
kind: "refused",
|
|
333
|
+
message: `${git.message} ${xbriefMigrationGuidance()} Pass --force to override.`,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const files = applyHybridEnvelopeRewrites(projectRoot, plan.pending);
|
|
338
|
+
return {
|
|
339
|
+
kind: "rewritten",
|
|
340
|
+
files,
|
|
341
|
+
message: `Rewrote ${files} hybrid xBRIEFInfo@0.6 envelope(s) in place to xBRIEFInfo@0.8 under '${MIGRATED_ARTIFACT_DIR}/'.`,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
259
345
|
const detection = detectLegacyVbriefLayout(projectRoot);
|
|
260
346
|
if (!detection.legacyLayout) {
|
|
261
347
|
return {
|
|
@@ -266,7 +352,7 @@ export function runXbriefMigration(args, _io) {
|
|
|
266
352
|
if (!isDirectory(legacyDir)) {
|
|
267
353
|
// Already on canonical xbrief/ with no vbrief/ tree — residual markers (e.g. a
|
|
268
354
|
// stale deposited v0.6 schema under xbrief/schemas/) are refreshed by update,
|
|
269
|
-
// not migrate:xbrief (#2368).
|
|
355
|
+
// not migrate:xbrief (#2368). Hybrid 0.6 envelopes were handled above (#3236).
|
|
270
356
|
if (convergence.xbriefHasContent && !convergence.vbriefPresent) {
|
|
271
357
|
return {
|
|
272
358
|
kind: "noop",
|
|
@@ -319,6 +405,9 @@ export function emitXbriefMigration(outcome, io, options = {}) {
|
|
|
319
405
|
io.writeOut(`Migrated ${outcome.files} file(s) from ${LEGACY_ARTIFACT_DIR}/ to ${MIGRATED_ARTIFACT_DIR}/.\n` +
|
|
320
406
|
`Backup written to ${outcome.backupDir}.\n`);
|
|
321
407
|
return 0;
|
|
408
|
+
case "rewritten":
|
|
409
|
+
io.writeOut(`${outcome.message}\n`);
|
|
410
|
+
return 0;
|
|
322
411
|
case "converged":
|
|
323
412
|
io.writeOut(`${outcome.message}\n`);
|
|
324
413
|
return 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.100.0",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -283,6 +283,10 @@
|
|
|
283
283
|
"types": "./dist/lifecycle/index.d.ts",
|
|
284
284
|
"default": "./dist/lifecycle/index.js"
|
|
285
285
|
},
|
|
286
|
+
"./literal-acceptance": {
|
|
287
|
+
"types": "./dist/literal-acceptance/index.d.ts",
|
|
288
|
+
"default": "./dist/literal-acceptance/index.js"
|
|
289
|
+
},
|
|
286
290
|
"./packs": {
|
|
287
291
|
"types": "./dist/packs/index.d.ts",
|
|
288
292
|
"default": "./dist/packs/index.js"
|
|
@@ -366,8 +370,8 @@
|
|
|
366
370
|
"provenance": true
|
|
367
371
|
},
|
|
368
372
|
"dependencies": {
|
|
369
|
-
"@deftai/directive-content": "^0.
|
|
370
|
-
"@deftai/directive-types": "^0.
|
|
373
|
+
"@deftai/directive-content": "^0.100.0",
|
|
374
|
+
"@deftai/directive-types": "^0.100.0",
|
|
371
375
|
"archiver": "^8.0.0"
|
|
372
376
|
},
|
|
373
377
|
"scripts": {
|