@deftai/directive-core 0.106.0 → 0.108.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 +2 -0
- package/dist/check/cached-orchestrator.js +28 -3
- package/dist/check/gate-lists.js +2 -0
- package/dist/check/named-cause.js +18 -3
- package/dist/codebase/provider.js +7 -1
- package/dist/delivery-attempt/handoff.js +1 -1
- package/dist/design-critique/citation-grammar.d.ts +53 -0
- package/dist/design-critique/citation-grammar.js +303 -0
- package/dist/design-critique/completed-arc-record.d.ts +62 -0
- package/dist/design-critique/completed-arc-record.js +232 -0
- package/dist/design-critique/exclusive-chip.d.ts +33 -0
- package/dist/design-critique/exclusive-chip.js +68 -0
- package/dist/design-critique/parent-audit.d.ts +59 -0
- package/dist/design-critique/parent-audit.js +121 -0
- package/dist/doctor/checks.d.ts +6 -0
- package/dist/doctor/checks.js +35 -0
- package/dist/freshness/bind.d.ts +32 -3
- package/dist/freshness/bind.js +58 -12
- package/dist/hooks/classify/host-session-identity.d.ts +76 -0
- package/dist/hooks/classify/host-session-identity.js +470 -0
- package/dist/hooks/classify/index.d.ts +3 -2
- package/dist/hooks/classify/index.js +3 -2
- package/dist/hooks/classify/paths.d.ts +7 -0
- package/dist/hooks/classify/paths.js +35 -0
- package/dist/hooks/classify/stdin.d.ts +5 -0
- package/dist/hooks/classify/stdin.js +27 -0
- package/dist/hooks/dest-form.d.ts +46 -0
- package/dist/hooks/dest-form.js +575 -0
- package/dist/hooks/dispatcher.d.ts +69 -2
- package/dist/hooks/dispatcher.js +609 -45
- package/dist/hooks/fixtures/cases.d.ts +16 -2
- package/dist/hooks/fixtures/cases.js +175 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/scope.js +3 -1
- package/dist/init-deposit/agent-hooks.d.ts +2 -1
- package/dist/init-deposit/agent-hooks.js +8 -1
- package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
- package/dist/init-deposit/hook-runtime-travel.js +269 -0
- package/dist/intake/clause-derivation.js +9 -3
- package/dist/intake/github-auth-modes-cli.js +14 -2
- package/dist/intake/github-auth-modes.d.ts +54 -9
- package/dist/intake/github-auth-modes.js +326 -123
- package/dist/intake/issue-ingest.js +62 -2
- package/dist/intake/platform-capabilities.d.ts +9 -2
- package/dist/intake/platform-capabilities.js +38 -9
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +26 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +37 -14
- package/dist/lifecycle/completed-write-guard.d.ts +48 -0
- package/dist/lifecycle/completed-write-guard.js +373 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/literal-acceptance/capture.d.ts +16 -0
- package/dist/literal-acceptance/capture.js +75 -7
- package/dist/literal-acceptance/evaluate.js +25 -3
- package/dist/literal-acceptance/index.d.ts +2 -2
- package/dist/literal-acceptance/index.js +2 -2
- package/dist/literal-acceptance/run.js +9 -3
- package/dist/literal-acceptance/types.d.ts +6 -0
- package/dist/literal-acceptance/types.js +6 -0
- package/dist/orphan-active/evaluate.d.ts +25 -0
- package/dist/orphan-active/evaluate.js +193 -128
- package/dist/orphan-active/index.d.ts +1 -0
- package/dist/orphan-active/index.js +1 -0
- package/dist/orphan-active/issue-state.d.ts +121 -0
- package/dist/orphan-active/issue-state.js +278 -0
- package/dist/platform/cursor-managed-runtime.d.ts +95 -0
- package/dist/platform/cursor-managed-runtime.js +241 -0
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/platform/platform-capabilities.d.ts +23 -1
- package/dist/platform/platform-capabilities.js +55 -15
- package/dist/policy/ceremony-dial.js +5 -9
- package/dist/policy/host-hooks.js +4 -9
- package/dist/policy/org-force-on-migration.js +5 -10
- package/dist/policy/plan-extensions.d.ts +14 -2
- package/dist/policy/plan-extensions.js +24 -3
- package/dist/policy/product-signal.js +5 -10
- package/dist/policy/require-human-merge.js +5 -9
- package/dist/policy/resolve.js +77 -16
- package/dist/policy/runtime-authority.d.ts +18 -0
- package/dist/policy/runtime-authority.js +11 -0
- package/dist/policy/value-feedback.js +9 -18
- package/dist/policy/write-fence.js +3 -0
- package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
- package/dist/pr-closeout-attestable/evaluate.js +306 -0
- package/dist/pr-closeout-attestable/index.d.ts +2 -0
- package/dist/pr-closeout-attestable/index.js +2 -0
- package/dist/pr-protected-issues/gh.d.ts +6 -2
- package/dist/pr-protected-issues/gh.js +7 -3
- package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
- package/dist/pr-wait-mergeable/cascade.js +21 -1
- package/dist/pr-wait-mergeable/types.d.ts +2 -0
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +11 -0
- package/dist/preflight/evaluate.d.ts +15 -0
- package/dist/preflight/evaluate.js +29 -3
- package/dist/preflight-cache/evaluate.js +9 -3
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
- package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
- package/dist/product-first-done-gate/evaluate.js +53 -12
- package/dist/release/cli-drift-report.d.ts +68 -0
- package/dist/release/cli-drift-report.js +189 -0
- package/dist/release/index.d.ts +1 -0
- package/dist/release/index.js +1 -0
- package/dist/release/issue-state-fetch.d.ts +5 -3
- package/dist/release/issue-state-fetch.js +86 -21
- package/dist/release/native-steps.js +1 -0
- package/dist/release/pipeline.js +19 -0
- package/dist/release/types.d.ts +14 -0
- package/dist/render/constants.d.ts +4 -0
- package/dist/render/constants.js +11 -8
- package/dist/render/export-spec.js +31 -5
- package/dist/render/index.d.ts +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/prd-render.d.ts +4 -1
- package/dist/render/prd-render.js +82 -23
- package/dist/render/project-render.js +14 -7
- package/dist/render/scope-outlook.d.ts +2 -0
- package/dist/render/scope-outlook.js +3 -0
- package/dist/render/spec-render.js +2 -2
- package/dist/resolution/package-manager.d.ts +33 -4
- package/dist/resolution/package-manager.js +210 -9
- package/dist/review-monitor/constants.d.ts +7 -0
- package/dist/review-monitor/constants.js +7 -0
- package/dist/review-monitor/github-lease.d.ts +98 -1
- package/dist/review-monitor/github-lease.js +182 -1
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +103 -2
- package/dist/scm/call.js +12 -3
- package/dist/scm/design-critique-chip.d.ts +38 -0
- package/dist/scm/design-critique-chip.js +187 -0
- package/dist/scm/gh-rest.d.ts +9 -1
- package/dist/scm/gh-rest.js +103 -0
- package/dist/scm/index.d.ts +1 -0
- package/dist/scm/index.js +1 -0
- package/dist/scm/main.d.ts +3 -0
- package/dist/scm/main.js +15 -1
- package/dist/scm/readiness-cli.d.ts +2 -0
- package/dist/scm/readiness-cli.js +42 -1
- package/dist/scm/readiness.d.ts +4 -1
- package/dist/scm/readiness.js +35 -5
- package/dist/scope/decompose.js +2 -1
- package/dist/scope/delivery-evidence.d.ts +27 -1
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/lifecycle-write.d.ts +24 -0
- package/dist/scope/lifecycle-write.js +64 -0
- package/dist/scope/main.js +3 -1
- package/dist/scope/project-definition-sync.js +14 -7
- package/dist/scope/transition.js +90 -11
- package/dist/scope-provenance/evaluate.js +5 -1
- package/dist/session/git.d.ts +42 -0
- package/dist/session/git.js +219 -3
- package/dist/session/occupancy.d.ts +239 -12
- package/dist/session/occupancy.js +820 -27
- package/dist/session/ritual-sentinel.d.ts +5 -0
- package/dist/session/ritual-sentinel.js +5 -0
- package/dist/session/session-ready.d.ts +5 -1
- package/dist/session/session-ready.js +98 -8
- package/dist/session/session-start.d.ts +17 -1
- package/dist/session/session-start.js +49 -17
- package/dist/session/toolchain-preflight.d.ts +13 -3
- package/dist/session/toolchain-preflight.js +93 -18
- package/dist/session/verify-session-ritual.d.ts +33 -3
- package/dist/session/verify-session-ritual.js +89 -19
- package/dist/slice/constants.d.ts +1 -1
- package/dist/spec-authority/constants.d.ts +10 -2
- package/dist/spec-authority/constants.js +53 -8
- package/dist/spec-authority/resolver.d.ts +3 -0
- package/dist/spec-authority/resolver.js +55 -7
- package/dist/subprocess/max-buffer.d.ts +14 -0
- package/dist/subprocess/max-buffer.js +15 -0
- package/dist/swarm/complete-cohort.d.ts +2 -0
- package/dist/swarm/complete-cohort.js +21 -13
- package/dist/swarm/index.d.ts +1 -0
- package/dist/swarm/index.js +1 -0
- package/dist/swarm/launch-cli.js +53 -33
- package/dist/swarm/launch.d.ts +77 -0
- package/dist/swarm/launch.js +248 -38
- package/dist/swarm/pre-dispatch-cli.js +2 -1
- package/dist/swarm/pre-dispatch.js +10 -1
- package/dist/swarm/subagent-status-dir.d.ts +27 -0
- package/dist/swarm/subagent-status-dir.js +42 -0
- package/dist/swarm/worktrees.d.ts +17 -3
- package/dist/swarm/worktrees.js +48 -4
- package/dist/triage/actions/index.d.ts +4 -0
- package/dist/triage/actions/index.js +6 -0
- package/dist/triage/evaluate/evaluate.d.ts +11 -0
- package/dist/triage/evaluate/evaluate.js +169 -0
- package/dist/triage/evaluate/github.d.ts +16 -0
- package/dist/triage/evaluate/github.js +153 -0
- package/dist/triage/evaluate/index.d.ts +9 -0
- package/dist/triage/evaluate/index.js +8 -0
- package/dist/triage/evaluate/paths.d.ts +7 -0
- package/dist/triage/evaluate/paths.js +25 -0
- package/dist/triage/evaluate/sink.d.ts +5 -0
- package/dist/triage/evaluate/sink.js +52 -0
- package/dist/triage/evaluate/types.d.ts +93 -0
- package/dist/triage/evaluate/types.js +13 -0
- package/dist/triage/evaluate/validity.d.ts +8 -0
- package/dist/triage/evaluate/validity.js +87 -0
- package/dist/triage/evaluate/value.d.ts +12 -0
- package/dist/triage/evaluate/value.js +33 -0
- package/dist/triage/evaluate/wip-census.d.ts +5 -0
- package/dist/triage/evaluate/wip-census.js +39 -0
- package/dist/triage/evaluate/worktrees.d.ts +8 -0
- package/dist/triage/evaluate/worktrees.js +68 -0
- package/dist/triage/evaluate/xbrief-refs.d.ts +8 -0
- package/dist/triage/evaluate/xbrief-refs.js +60 -0
- package/dist/triage/help/registry-data.d.ts +21 -7
- package/dist/triage/help/registry-data.js +53 -5
- package/dist/triage/index.d.ts +1 -0
- package/dist/triage/index.js +1 -0
- package/dist/triage/scope/mutations-core.d.ts +2 -6
- package/dist/triage/scope/mutations-core.js +23 -55
- package/dist/triage/scope-drift/add-ignore.js +5 -39
- package/dist/triage/subscribe/index.d.ts +2 -6
- package/dist/triage/subscribe/index.js +15 -65
- package/dist/triage/welcome/writers.js +15 -74
- package/dist/value/feedback-file.d.ts +17 -2
- package/dist/value/feedback-file.js +80 -6
- package/dist/vbrief-build/index.d.ts +2 -1
- package/dist/vbrief-build/index.js +2 -1
- package/dist/vbrief-build/parity-scenarios.js +5 -5
- package/dist/vbrief-build/project-definition-io.d.ts +63 -3
- package/dist/vbrief-build/project-definition-io.js +357 -31
- package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
- package/dist/vbrief-build/project-definition-mutation.js +43 -0
- package/dist/vbrief-reconcile/labels.d.ts +1 -0
- package/dist/vbrief-reconcile/labels.js +30 -0
- package/dist/vbrief-validate/conformance.d.ts +1 -0
- package/dist/vbrief-validate/conformance.js +85 -8
- package/dist/vbrief-validate/main.js +2 -0
- package/dist/vbrief-validate/precutover.js +5 -10
- package/dist/vbrief-validation/story-quality.d.ts +7 -0
- package/dist/vbrief-validation/story-quality.js +8 -1
- package/dist/verify-ac/clauses.d.ts +62 -2
- package/dist/verify-ac/clauses.js +183 -111
- package/dist/verify-ac/index.d.ts +1 -1
- package/dist/verify-ac/index.js +1 -1
- package/dist/verify-env/node-runtime.d.ts +8 -4
- package/dist/verify-env/node-runtime.js +9 -6
- package/dist/verify-env/toolchain-check.d.ts +35 -3
- package/dist/verify-env/toolchain-check.js +155 -36
- package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
- package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
- package/dist/xbrief-migrate/agents-header.js +69 -7
- package/package.json +7 -3
|
@@ -3,12 +3,9 @@ export declare const SUBSCRIPTION_HISTORY_SCHEMA = "deft.triage.subscription-cha
|
|
|
3
3
|
export declare const PROJECT_DEFINITION_REL_PATH = "xbrief/PROJECT-DEFINITION.xbrief.json";
|
|
4
4
|
/** Python ``!r``-style quoting for parity with triage_subscribe.py messages. */
|
|
5
5
|
export declare function pyRepr(value: string): string;
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
}
|
|
6
|
+
export { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionPath, } from "../../vbrief-build/project-definition-io.js";
|
|
7
|
+
export { ProjectDefinitionIOError } from "../../vbrief-build/types.js";
|
|
9
8
|
type TriageRule = Record<string, unknown>;
|
|
10
|
-
export declare function loadProjectDefinitionForMutation(projectRoot: string): [Record<string, unknown>, string];
|
|
11
|
-
export declare function atomicWriteProjectDefinition(path: string, data: Record<string, unknown>): void;
|
|
12
9
|
export declare function recordSubscriptionChange(projectRoot: string, options: {
|
|
13
10
|
op: string;
|
|
14
11
|
label?: string | null;
|
|
@@ -34,5 +31,4 @@ export declare function unsubscribe(projectRoot: string, options?: {
|
|
|
34
31
|
actor?: string | null;
|
|
35
32
|
}): [boolean, string];
|
|
36
33
|
export declare const RECONCILE_HINT: string;
|
|
37
|
-
export {};
|
|
38
34
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { appendFileSync,
|
|
3
|
-
import {
|
|
4
|
-
import { resolveProjectDefinitionPath } from "../../layout/resolve.js";
|
|
2
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
5
4
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
6
|
-
import {
|
|
5
|
+
import { withProjectDefinitionMutation } from "../../vbrief-build/project-definition-mutation.js";
|
|
7
6
|
import { resolveTriageCachePath } from "../cache-path.js";
|
|
8
7
|
export const SUBSCRIPTION_HISTORY_REL_PATH = "xbrief/.triage-cache/subscription-history.jsonl";
|
|
9
8
|
export const SUBSCRIPTION_HISTORY_SCHEMA = "deft.triage.subscription-change.v1";
|
|
@@ -12,61 +11,12 @@ export const PROJECT_DEFINITION_REL_PATH = "xbrief/PROJECT-DEFINITION.xbrief.jso
|
|
|
12
11
|
export function pyRepr(value) {
|
|
13
12
|
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
function projectDefinitionPath(projectRoot) {
|
|
22
|
-
return resolveProjectDefinitionPath(projectRoot);
|
|
23
|
-
}
|
|
24
|
-
export function loadProjectDefinitionForMutation(projectRoot) {
|
|
25
|
-
const path = projectDefinitionPath(projectRoot);
|
|
26
|
-
if (!existsSync(path)) {
|
|
27
|
-
throw new ProjectDefinitionIOError(`PROJECT-DEFINITION not found at ${path}; run task triage:welcome / ` +
|
|
28
|
-
"task triage:bootstrap to scaffold one first.");
|
|
29
|
-
}
|
|
30
|
-
let raw;
|
|
31
|
-
try {
|
|
32
|
-
raw = readFileSync(path, { encoding: "utf8" });
|
|
33
|
-
}
|
|
34
|
-
catch (exc) {
|
|
35
|
-
throw new ProjectDefinitionIOError(`Could not read PROJECT-DEFINITION at ${path}: ${String(exc)}`);
|
|
36
|
-
}
|
|
37
|
-
let data;
|
|
38
|
-
try {
|
|
39
|
-
data = JSON.parse(raw);
|
|
40
|
-
}
|
|
41
|
-
catch (exc) {
|
|
42
|
-
throw new ProjectDefinitionIOError(`PROJECT-DEFINITION at ${path} is not valid JSON: ${String(exc)}`);
|
|
43
|
-
}
|
|
44
|
-
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
45
|
-
throw new ProjectDefinitionIOError(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
46
|
-
}
|
|
47
|
-
return [data, path];
|
|
48
|
-
}
|
|
49
|
-
export function atomicWriteProjectDefinition(path, data) {
|
|
50
|
-
const parent = join(path, "..");
|
|
51
|
-
mkdirSync(parent, { recursive: true });
|
|
52
|
-
const payload = JSON.stringify(data, null, 2);
|
|
53
|
-
const tmpName = join(parent, `${basename(path)}.${process.pid}.${Date.now()}.tmp`);
|
|
54
|
-
try {
|
|
55
|
-
writeFileSync(tmpName, payload.endsWith("\n") ? payload : `${payload}\n`, {
|
|
56
|
-
encoding: "utf8",
|
|
57
|
-
});
|
|
58
|
-
renameSync(tmpName, path);
|
|
59
|
-
}
|
|
60
|
-
catch (exc) {
|
|
61
|
-
try {
|
|
62
|
-
unlinkSync(tmpName);
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// ignore
|
|
66
|
-
}
|
|
67
|
-
throw exc;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
14
|
+
// One canonical PROJECT-DEFINITION identity (#3796) -- see the same note in
|
|
15
|
+
// `triage/scope/mutations-core.ts`. These were module-local duplicates that
|
|
16
|
+
// resolved the layout path directly instead of honouring the override the lock
|
|
17
|
+
// uses, and wrote through their own temp sink.
|
|
18
|
+
export { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionPath, } from "../../vbrief-build/project-definition-io.js";
|
|
19
|
+
export { ProjectDefinitionIOError } from "../../vbrief-build/types.js";
|
|
70
20
|
function snapshotRules(rules) {
|
|
71
21
|
return JSON.parse(JSON.stringify(rules));
|
|
72
22
|
}
|
|
@@ -251,10 +201,10 @@ function mutate(projectRoot, options) {
|
|
|
251
201
|
// Serialise the read-modify-write + subscription-history append under the
|
|
252
202
|
// shared PROJECT-DEFINITION mutation lock so concurrent mutators cannot lose
|
|
253
203
|
// an update or emit out-of-order audit rows (#1260).
|
|
254
|
-
return
|
|
255
|
-
const
|
|
204
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
205
|
+
const data = mutation.load();
|
|
256
206
|
if (typeof data.plan !== "object" || data.plan === null || Array.isArray(data.plan)) {
|
|
257
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
207
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-object 'plan' key`);
|
|
258
208
|
}
|
|
259
209
|
const plan = data.plan;
|
|
260
210
|
migrateLegacyPolicyKey(plan);
|
|
@@ -266,7 +216,7 @@ function mutate(projectRoot, options) {
|
|
|
266
216
|
plan[PLAN_POLICY_KEY] = {};
|
|
267
217
|
}
|
|
268
218
|
else {
|
|
269
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
219
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-object 'plan.policy' key`);
|
|
270
220
|
}
|
|
271
221
|
}
|
|
272
222
|
const policy = plan[PLAN_POLICY_KEY];
|
|
@@ -275,7 +225,7 @@ function mutate(projectRoot, options) {
|
|
|
275
225
|
policy.triageScope = [];
|
|
276
226
|
}
|
|
277
227
|
else {
|
|
278
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
228
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-list 'plan.policy.triageScope'`);
|
|
279
229
|
}
|
|
280
230
|
}
|
|
281
231
|
const rules = policy.triageScope;
|
|
@@ -291,7 +241,7 @@ function mutate(projectRoot, options) {
|
|
|
291
241
|
if (!changed) {
|
|
292
242
|
return [false, message];
|
|
293
243
|
}
|
|
294
|
-
|
|
244
|
+
mutation.persist(data);
|
|
295
245
|
const after = snapshotRules(rules);
|
|
296
246
|
recordSubscriptionChange(projectRoot, {
|
|
297
247
|
op: options.op,
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync,
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
3
|
import { containedWrite } from "../../fs/contained-write.js";
|
|
4
|
-
import {
|
|
5
|
-
import { hasArtifactSuffix, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
4
|
+
import { hasArtifactSuffix, resolveLifecycleFolder } from "../../layout/resolve.js";
|
|
6
5
|
import { migrateLegacyPolicyKey, PLAN_ONBOARDING_KEY, PLAN_POLICY_KEY, } from "../../policy/plan-extensions.js";
|
|
7
6
|
import { stampChangedToken } from "../../policy/resolve.js";
|
|
8
|
-
import {
|
|
7
|
+
import { withProjectDefinitionMutation } from "../../vbrief-build/project-definition-mutation.js";
|
|
9
8
|
import { AUDIT_LOG_REL_PATH, DEFAULT_RELIEF_AGE_DAYS, DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, WELCOME_AUDIT_TAG, } from "./constants.js";
|
|
10
|
-
function projectDefinitionPath(projectRoot) {
|
|
11
|
-
return resolveProjectDefinitionPath(projectRoot);
|
|
12
|
-
}
|
|
13
9
|
function utcIso() {
|
|
14
10
|
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
15
11
|
}
|
|
@@ -38,51 +34,10 @@ export function appendAuditEntry(projectRoot, entry, changed = true) {
|
|
|
38
34
|
});
|
|
39
35
|
return logPath;
|
|
40
36
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Atomically write PROJECT-DEFINITION JSON under projectRoot containment (#3077).
|
|
43
|
-
* Containment root is projectRoot (not dirname(path)) so a force-added `xbrief/`
|
|
44
|
-
* directory symlink fails closed before temp+rename — same class as #3042.
|
|
45
|
-
*/
|
|
46
|
-
function atomicWrite(projectRoot, path, data) {
|
|
47
|
-
const root = resolve(projectRoot);
|
|
48
|
-
const targetAbs = resolve(path);
|
|
49
|
-
// Refuse leaf/parent symlinks and out-of-root targets before temp+rename.
|
|
50
|
-
assertWriteTargetSafe(root, targetAbs);
|
|
51
|
-
const dir = dirname(targetAbs);
|
|
52
|
-
const payload = `${JSON.stringify(data, null, 2)}\n`;
|
|
53
|
-
const tmpName = `.${Date.now()}.tmp`;
|
|
54
|
-
const tmp = join(dir, tmpName);
|
|
55
|
-
try {
|
|
56
|
-
// #2980 wave D / #3077: product write routes through containedWrite under projectRoot.
|
|
57
|
-
containedWrite({
|
|
58
|
-
root,
|
|
59
|
-
target: tmp,
|
|
60
|
-
data: payload,
|
|
61
|
-
mode: "create",
|
|
62
|
-
});
|
|
63
|
-
renameSync(tmp, targetAbs);
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
try {
|
|
67
|
-
rmSync(tmp, { force: true });
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
/* best-effort cleanup */
|
|
71
|
-
}
|
|
72
|
-
throw err;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
37
|
export function writeTriageScope(projectRoot, rules, options = { presetLabel: "custom" }) {
|
|
76
38
|
// Serialise read-modify-write + audit append under the shared lock (#1260).
|
|
77
|
-
return
|
|
78
|
-
const
|
|
79
|
-
if (!existsSync(path))
|
|
80
|
-
throw new Error(`PROJECT-DEFINITION not found at ${path}`);
|
|
81
|
-
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
82
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
83
|
-
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
84
|
-
}
|
|
85
|
-
const data = parsed;
|
|
39
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
40
|
+
const data = mutation.load();
|
|
86
41
|
const plan = data.plan;
|
|
87
42
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
88
43
|
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
@@ -97,7 +52,7 @@ export function writeTriageScope(projectRoot, rules, options = { presetLabel: "c
|
|
|
97
52
|
const policyRec = policy;
|
|
98
53
|
const previous = policyRec.triageScope;
|
|
99
54
|
policyRec.triageScope = rules;
|
|
100
|
-
|
|
55
|
+
mutation.persist(data);
|
|
101
56
|
const changed = JSON.stringify(previous) !== JSON.stringify(rules);
|
|
102
57
|
const actor = options.actor ?? WELCOME_AUDIT_TAG;
|
|
103
58
|
const auditEntry = [
|
|
@@ -117,15 +72,8 @@ export function writeTriageScope(projectRoot, rules, options = { presetLabel: "c
|
|
|
117
72
|
* onboarding decision and MUST be representable without writing the value field.
|
|
118
73
|
*/
|
|
119
74
|
export function writeWipCapDecision(projectRoot, options = {}) {
|
|
120
|
-
return
|
|
121
|
-
const
|
|
122
|
-
if (!existsSync(path))
|
|
123
|
-
throw new Error(`PROJECT-DEFINITION not found at ${path}`);
|
|
124
|
-
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
125
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
126
|
-
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
127
|
-
}
|
|
128
|
-
const data = parsed;
|
|
75
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
76
|
+
const data = mutation.load();
|
|
129
77
|
const plan = data.plan;
|
|
130
78
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
131
79
|
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
@@ -149,7 +97,7 @@ export function writeWipCapDecision(projectRoot, options = {}) {
|
|
|
149
97
|
next.value = options.value;
|
|
150
98
|
}
|
|
151
99
|
planRec[PLAN_ONBOARDING_KEY] = next;
|
|
152
|
-
|
|
100
|
+
mutation.persist(data);
|
|
153
101
|
const changed = !alreadyDecided || previousRec.acceptedDefault !== acceptedDefault;
|
|
154
102
|
const auditEntry = [
|
|
155
103
|
`actor=${actor}`,
|
|
@@ -167,15 +115,8 @@ export function writeWipCap(projectRoot, wipCap, options = {}) {
|
|
|
167
115
|
throw new Error(`wipCap must be a positive int, got ${JSON.stringify(wipCap)}`);
|
|
168
116
|
}
|
|
169
117
|
// Serialise read-modify-write + audit append under the shared lock (#1260).
|
|
170
|
-
return
|
|
171
|
-
const
|
|
172
|
-
if (!existsSync(path))
|
|
173
|
-
throw new Error(`PROJECT-DEFINITION not found at ${path}`);
|
|
174
|
-
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
175
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
176
|
-
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
177
|
-
}
|
|
178
|
-
const data = parsed;
|
|
118
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
119
|
+
const data = mutation.load();
|
|
179
120
|
const plan = data.plan;
|
|
180
121
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
181
122
|
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
@@ -206,7 +147,7 @@ export function writeWipCap(projectRoot, wipCap, options = {}) {
|
|
|
206
147
|
}
|
|
207
148
|
planRec[PLAN_ONBOARDING_KEY] = onboardingRecord;
|
|
208
149
|
if (previous === undefined && wipCap === DEFAULT_WIP_CAP) {
|
|
209
|
-
|
|
150
|
+
mutation.persist(data);
|
|
210
151
|
const auditEntry = `actor=${actor} field=${PLAN_ONBOARDING_KEY}.wipCapDecided value=true ` +
|
|
211
152
|
`acceptedDefault=true changed=true note=default-no-materialize`;
|
|
212
153
|
appendAuditEntry(projectRoot, auditEntry);
|
|
@@ -214,14 +155,14 @@ export function writeWipCap(projectRoot, wipCap, options = {}) {
|
|
|
214
155
|
}
|
|
215
156
|
if (previous !== undefined && wipCap === DEFAULT_WIP_CAP) {
|
|
216
157
|
delete policyRec.wipCap;
|
|
217
|
-
|
|
158
|
+
mutation.persist(data);
|
|
218
159
|
const auditEntry = `actor=${actor} field=plan.policy.wipCap action=cleared-to-default value=${wipCap} ` +
|
|
219
160
|
`previous=${JSON.stringify(previous)} changed=true`;
|
|
220
161
|
appendAuditEntry(projectRoot, auditEntry);
|
|
221
162
|
return [true, auditEntry];
|
|
222
163
|
}
|
|
223
164
|
policyRec.wipCap = wipCap;
|
|
224
|
-
|
|
165
|
+
mutation.persist(data);
|
|
225
166
|
const changed = previous !== wipCap;
|
|
226
167
|
const auditEntry = `actor=${actor} field=plan.policy.wipCap value=${wipCap} previous=${JSON.stringify(previous)} ` +
|
|
227
168
|
`changed=${changed ? "true" : "false"}`;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type GhRestSeams } from "../scm/gh-rest.js";
|
|
2
2
|
export declare const DEFAULT_UPSTREAM_REPO = "deftai/directive";
|
|
3
3
|
export declare const FRAMEWORK_GAP_TITLE_PREFIX = "[framework-gap]";
|
|
4
|
+
/** Sole sanctioned title classification (#3713). Does not write `adoption-blocker`. */
|
|
5
|
+
export declare const ADOPTION_BLOCKER_TITLE_TOKEN = "BLOCKER";
|
|
6
|
+
/** Strip source prefix and the optional BLOCKER token so marked/unmarked titles dedup. */
|
|
7
|
+
export declare function stripTitleClassificationTokens(title: string): string;
|
|
4
8
|
export type FeedbackFileOutcome = "draft" | "filed" | "skipped-maintainer" | "blocked-policy" | "blocked-duplicate" | "blocked-no-confirm" | "error-bad-args" | "error-config" | "error-network";
|
|
5
9
|
export interface FeedbackGapInput {
|
|
6
10
|
readonly summary: string;
|
|
@@ -8,6 +12,11 @@ export interface FeedbackGapInput {
|
|
|
8
12
|
readonly expected?: string;
|
|
9
13
|
readonly actual?: string;
|
|
10
14
|
readonly sessionNotes?: string;
|
|
15
|
+
/** Consumer hard-stop judgment. Advisory title token only — never applies a label. */
|
|
16
|
+
readonly adoptionBlocker?: boolean;
|
|
17
|
+
readonly flowAndVersion?: string;
|
|
18
|
+
readonly alternatives?: string;
|
|
19
|
+
readonly recoveryCost?: string;
|
|
11
20
|
}
|
|
12
21
|
export interface FeedbackFileOptions extends FeedbackGapInput {
|
|
13
22
|
readonly projectRoot?: string | null;
|
|
@@ -29,10 +38,12 @@ export interface FeedbackFileResult {
|
|
|
29
38
|
}
|
|
30
39
|
/** True when the resolved project root is the directive maintainer source checkout. */
|
|
31
40
|
export declare function isMaintainerFrameworkRepo(projectRoot: string): boolean;
|
|
32
|
-
/** Normalize a title for duplicate comparison (#1709). */
|
|
41
|
+
/** Normalize a title for duplicate comparison (#1709 / #3713). */
|
|
33
42
|
export declare function normalizeForDedup(title: string): string;
|
|
34
43
|
/** Build the upstream issue title for a framework-gap report. */
|
|
35
|
-
export declare function buildFrameworkGapTitle(summary: string
|
|
44
|
+
export declare function buildFrameworkGapTitle(summary: string, options?: {
|
|
45
|
+
adoptionBlocker?: boolean;
|
|
46
|
+
}): string;
|
|
36
47
|
/** Build the structured framework-gap issue body. */
|
|
37
48
|
export declare function buildFrameworkGapBody(input: FeedbackGapInput): string;
|
|
38
49
|
export interface DuplicateMatch {
|
|
@@ -49,6 +60,10 @@ export interface FeedbackFileCliArgs {
|
|
|
49
60
|
expected?: string;
|
|
50
61
|
actual?: string;
|
|
51
62
|
sessionNotes?: string;
|
|
63
|
+
adoptionBlocker?: boolean;
|
|
64
|
+
flowAndVersion?: string;
|
|
65
|
+
alternatives?: string;
|
|
66
|
+
recoveryCost?: string;
|
|
52
67
|
confirm?: boolean;
|
|
53
68
|
dryRun?: boolean;
|
|
54
69
|
json?: boolean;
|
|
@@ -7,6 +7,21 @@ import { GhRestError, restCreateIssue, restIssueListPaginated, } from "../scm/gh
|
|
|
7
7
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
8
8
|
export const DEFAULT_UPSTREAM_REPO = "deftai/directive";
|
|
9
9
|
export const FRAMEWORK_GAP_TITLE_PREFIX = "[framework-gap]";
|
|
10
|
+
/** Sole sanctioned title classification (#3713). Does not write `adoption-blocker`. */
|
|
11
|
+
export const ADOPTION_BLOCKER_TITLE_TOKEN = "BLOCKER";
|
|
12
|
+
const FRAMEWORK_GAP_PREFIX_RE = /^\[framework-gap\]\s*/i;
|
|
13
|
+
const BLOCKER_TITLE_TOKEN_RE = /^BLOCKER\b[:\s-]*/i;
|
|
14
|
+
/** Strip source prefix and the optional BLOCKER token so marked/unmarked titles dedup. */
|
|
15
|
+
export function stripTitleClassificationTokens(title) {
|
|
16
|
+
let rest = title.trim();
|
|
17
|
+
let previous = "";
|
|
18
|
+
while (rest !== previous) {
|
|
19
|
+
previous = rest;
|
|
20
|
+
rest = rest.replace(FRAMEWORK_GAP_PREFIX_RE, "").trim();
|
|
21
|
+
rest = rest.replace(BLOCKER_TITLE_TOKEN_RE, "").trim();
|
|
22
|
+
}
|
|
23
|
+
return rest;
|
|
24
|
+
}
|
|
10
25
|
function sanitizeOneLine(value) {
|
|
11
26
|
return value.replace(/\r?\n/g, " ").trim();
|
|
12
27
|
}
|
|
@@ -18,23 +33,54 @@ function sectionOrPlaceholder(value, placeholder) {
|
|
|
18
33
|
export function isMaintainerFrameworkRepo(projectRoot) {
|
|
19
34
|
return isFrameworkRepoRoot(resolve(projectRoot));
|
|
20
35
|
}
|
|
21
|
-
/** Normalize a title for duplicate comparison (#1709). */
|
|
36
|
+
/** Normalize a title for duplicate comparison (#1709 / #3713). */
|
|
22
37
|
export function normalizeForDedup(title) {
|
|
23
|
-
return title
|
|
24
|
-
.trim()
|
|
25
|
-
.replace(/^\[framework-gap\]\s*/i, "")
|
|
38
|
+
return stripTitleClassificationTokens(title)
|
|
26
39
|
.toLowerCase()
|
|
27
40
|
.replace(/[^\p{L}\p{N}]+/gu, " ")
|
|
28
41
|
.trim();
|
|
29
42
|
}
|
|
30
43
|
/** Build the upstream issue title for a framework-gap report. */
|
|
31
|
-
export function buildFrameworkGapTitle(summary) {
|
|
44
|
+
export function buildFrameworkGapTitle(summary, options) {
|
|
32
45
|
const clean = sanitizeOneLine(summary);
|
|
46
|
+
if (options?.adoptionBlocker) {
|
|
47
|
+
const rest = stripTitleClassificationTokens(clean);
|
|
48
|
+
return `${FRAMEWORK_GAP_TITLE_PREFIX} ${ADOPTION_BLOCKER_TITLE_TOKEN} ${rest}`;
|
|
49
|
+
}
|
|
33
50
|
if (clean.toLowerCase().startsWith(FRAMEWORK_GAP_TITLE_PREFIX.toLowerCase())) {
|
|
34
51
|
return clean;
|
|
35
52
|
}
|
|
36
53
|
return `${FRAMEWORK_GAP_TITLE_PREFIX} ${clean}`;
|
|
37
54
|
}
|
|
55
|
+
function buildAdoptionImpactSection(input) {
|
|
56
|
+
if (!input.adoptionBlocker) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return [
|
|
60
|
+
"## Adoption impact",
|
|
61
|
+
"",
|
|
62
|
+
"This report is a consumer hard-stop: the consumer cannot complete an intended Directive flow and has no reasonable workaround.",
|
|
63
|
+
"",
|
|
64
|
+
`Title token: \`${ADOPTION_BLOCKER_TITLE_TOKEN}\`. This token does not apply the \`adoption-blocker\` ranking label. A privileged actor applies that label after the body-evidence test.`,
|
|
65
|
+
"",
|
|
66
|
+
"### Affected consumer flow and version",
|
|
67
|
+
"",
|
|
68
|
+
sectionOrPlaceholder(input.flowAndVersion, "_(not provided)_"),
|
|
69
|
+
"",
|
|
70
|
+
"### Documented alternatives attempted",
|
|
71
|
+
"",
|
|
72
|
+
sectionOrPlaceholder(input.alternatives, "_(not provided — why documented alternatives are not a reasonable workaround)_"),
|
|
73
|
+
"",
|
|
74
|
+
"### Observed recovery cost",
|
|
75
|
+
"",
|
|
76
|
+
sectionOrPlaceholder(input.recoveryCost, "_(not provided)_"),
|
|
77
|
+
"",
|
|
78
|
+
"### Triage owner and date",
|
|
79
|
+
"",
|
|
80
|
+
"_(to be filled by a privileged actor when applying `adoption-blocker`)_",
|
|
81
|
+
"",
|
|
82
|
+
];
|
|
83
|
+
}
|
|
38
84
|
/** Build the structured framework-gap issue body. */
|
|
39
85
|
export function buildFrameworkGapBody(input) {
|
|
40
86
|
const summary = sanitizeOneLine(input.summary);
|
|
@@ -59,6 +105,7 @@ export function buildFrameworkGapBody(input) {
|
|
|
59
105
|
"",
|
|
60
106
|
sectionOrPlaceholder(input.sessionNotes, "_(not provided)_"),
|
|
61
107
|
"",
|
|
108
|
+
...buildAdoptionImpactSection(input),
|
|
62
109
|
"---",
|
|
63
110
|
"_Filed via `task feedback:file` from a directive consumer project (Refs #1709 value-feedback gap escalation)._",
|
|
64
111
|
"",
|
|
@@ -126,7 +173,7 @@ export function runFeedbackFile(options) {
|
|
|
126
173
|
}
|
|
127
174
|
const projectRoot = resolve(projectRootRaw);
|
|
128
175
|
const repo = resolveRepo(options);
|
|
129
|
-
const title = buildFrameworkGapTitle(summary);
|
|
176
|
+
const title = buildFrameworkGapTitle(summary, { adoptionBlocker: options.adoptionBlocker });
|
|
130
177
|
const body = buildFrameworkGapBody(options);
|
|
131
178
|
if (isMaintainerFrameworkRepo(projectRoot)) {
|
|
132
179
|
return {
|
|
@@ -254,6 +301,8 @@ export function parseFeedbackFileArgs(argv) {
|
|
|
254
301
|
out.dryRun = true;
|
|
255
302
|
else if (arg === "--json")
|
|
256
303
|
out.json = true;
|
|
304
|
+
else if (arg === "--blocker")
|
|
305
|
+
out.adoptionBlocker = true;
|
|
257
306
|
else if (arg === "--summary") {
|
|
258
307
|
out.summary = argv[++i];
|
|
259
308
|
}
|
|
@@ -287,6 +336,27 @@ export function parseFeedbackFileArgs(argv) {
|
|
|
287
336
|
else if (arg?.startsWith("--session-notes=")) {
|
|
288
337
|
out.sessionNotes = arg.slice("--session-notes=".length);
|
|
289
338
|
}
|
|
339
|
+
else if (arg === "--flow" || arg === "--flow-and-version") {
|
|
340
|
+
out.flowAndVersion = argv[++i];
|
|
341
|
+
}
|
|
342
|
+
else if (arg?.startsWith("--flow=")) {
|
|
343
|
+
out.flowAndVersion = arg.slice("--flow=".length);
|
|
344
|
+
}
|
|
345
|
+
else if (arg?.startsWith("--flow-and-version=")) {
|
|
346
|
+
out.flowAndVersion = arg.slice("--flow-and-version=".length);
|
|
347
|
+
}
|
|
348
|
+
else if (arg === "--alternatives") {
|
|
349
|
+
out.alternatives = argv[++i];
|
|
350
|
+
}
|
|
351
|
+
else if (arg?.startsWith("--alternatives=")) {
|
|
352
|
+
out.alternatives = arg.slice("--alternatives=".length);
|
|
353
|
+
}
|
|
354
|
+
else if (arg === "--recovery-cost") {
|
|
355
|
+
out.recoveryCost = argv[++i];
|
|
356
|
+
}
|
|
357
|
+
else if (arg?.startsWith("--recovery-cost=")) {
|
|
358
|
+
out.recoveryCost = arg.slice("--recovery-cost=".length);
|
|
359
|
+
}
|
|
290
360
|
else if (arg === "--repo") {
|
|
291
361
|
out.repo = argv[++i];
|
|
292
362
|
}
|
|
@@ -327,6 +397,10 @@ export function feedbackFileMain(argv) {
|
|
|
327
397
|
expected: args.expected,
|
|
328
398
|
actual: args.actual,
|
|
329
399
|
sessionNotes: args.sessionNotes,
|
|
400
|
+
adoptionBlocker: args.adoptionBlocker,
|
|
401
|
+
flowAndVersion: args.flowAndVersion,
|
|
402
|
+
alternatives: args.alternatives,
|
|
403
|
+
recoveryCost: args.recoveryCost,
|
|
330
404
|
confirm: args.confirm,
|
|
331
405
|
dryRun: args.dryRun,
|
|
332
406
|
json: args.json,
|
|
@@ -3,7 +3,8 @@ export { DEFAULT_STATUS_FOR_FOLDER, DEPRECATION_SENTINEL, EMITTED_VBRIEF_VERSION
|
|
|
3
3
|
export { pythonJsonPretty } from "./json.js";
|
|
4
4
|
export { cmdVbriefBuild, run, usage } from "./main.js";
|
|
5
5
|
export { PARITY_SCENARIO_NAMES, renderScenarioOutput, runParityScenario, SAMPLE_PROJECT_MD, SAMPLE_ROADMAP_MD, SAMPLE_SPEC_MD, SAMPLE_SPEC_VBRIEF, } from "./parity-scenarios.js";
|
|
6
|
-
export { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionMutationLock, projectDefinitionPath, } from "./project-definition-io.js";
|
|
6
|
+
export { atomicWriteProjectDefinition, CONFIGURED_PROJECT_DEFINITION_LABEL, loadProjectDefinitionForMutation, type MutationLockDeps, ProjectDefinitionLockError, parseProjectDefinitionAt, projectDefinitionArtifactLabel, projectDefinitionMutationLock, projectDefinitionPath, } from "./project-definition-io.js";
|
|
7
|
+
export { type ProjectDefinitionMutation, withProjectDefinitionMutation, } from "./project-definition-mutation.js";
|
|
7
8
|
export { loadProjectInvariants, parseProjectInvariantsField } from "./project-invariants-io.js";
|
|
8
9
|
export { buildScopeVbriefFromReconciled, defaultStatusForFolder, folderForStatus, migrationTimestamp, planStatusMatchesFolder, } from "./routing.js";
|
|
9
10
|
export { deriveOverviewNarrative, extractTechStack, firstProseParagraph, parseRoadmapItems, resolveRepoUrl, } from "./sources.js";
|
|
@@ -3,7 +3,8 @@ export { DEFAULT_STATUS_FOR_FOLDER, DEPRECATION_SENTINEL, EMITTED_VBRIEF_VERSION
|
|
|
3
3
|
export { pythonJsonPretty } from "./json.js";
|
|
4
4
|
export { cmdVbriefBuild, run, usage } from "./main.js";
|
|
5
5
|
export { PARITY_SCENARIO_NAMES, renderScenarioOutput, runParityScenario, SAMPLE_PROJECT_MD, SAMPLE_ROADMAP_MD, SAMPLE_SPEC_MD, SAMPLE_SPEC_VBRIEF, } from "./parity-scenarios.js";
|
|
6
|
-
export { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionMutationLock, projectDefinitionPath, } from "./project-definition-io.js";
|
|
6
|
+
export { atomicWriteProjectDefinition, CONFIGURED_PROJECT_DEFINITION_LABEL, loadProjectDefinitionForMutation, ProjectDefinitionLockError, parseProjectDefinitionAt, projectDefinitionArtifactLabel, projectDefinitionMutationLock, projectDefinitionPath, } from "./project-definition-io.js";
|
|
7
|
+
export { withProjectDefinitionMutation, } from "./project-definition-mutation.js";
|
|
7
8
|
export { loadProjectInvariants, parseProjectInvariantsField } from "./project-invariants-io.js";
|
|
8
9
|
export { buildScopeVbriefFromReconciled, defaultStatusForFolder, folderForStatus, migrationTimestamp, planStatusMatchesFolder, } from "./routing.js";
|
|
9
10
|
export { deriveOverviewNarrative, extractTechStack, firstProseParagraph, parseRoadmapItems, resolveRepoUrl, } from "./sources.js";
|
|
@@ -2,7 +2,7 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { createScopeVbrief, referenceWithDefaultTrust, slugify } from "./build.js";
|
|
4
4
|
import { pythonJsonPretty } from "./json.js";
|
|
5
|
-
import {
|
|
5
|
+
import { withProjectDefinitionMutation } from "./project-definition-mutation.js";
|
|
6
6
|
import { buildScopeVbriefFromReconciled, folderForStatus, planStatusMatchesFolder, } from "./routing.js";
|
|
7
7
|
import { deriveOverviewNarrative, extractTechStack, firstProseParagraph, parseRoadmapItems, resolveRepoUrl, } from "./sources.js";
|
|
8
8
|
import { createSpeckitScopeVbrief, dependenciesForItem, edgeNodes, migrateSpeckitPlan, speckitIpIndex, speckitIpSlug, } from "./speckit.js";
|
|
@@ -209,11 +209,11 @@ export function runParityScenario(name, ctx) {
|
|
|
209
209
|
const pdPath = join(vbriefDir, "PROJECT-DEFINITION.xbrief.json");
|
|
210
210
|
writeFileSync(pdPath, pythonJsonPretty(seed), "utf8");
|
|
211
211
|
let roundtrip = {};
|
|
212
|
-
|
|
213
|
-
const
|
|
212
|
+
withProjectDefinitionMutation(ctx.fixtureRoot, (mutation) => {
|
|
213
|
+
const data = mutation.load();
|
|
214
214
|
data.plan.policy = { wipCap: 12 };
|
|
215
|
-
|
|
216
|
-
roundtrip = JSON.parse(readFileSync(
|
|
215
|
+
mutation.persist(data);
|
|
216
|
+
roundtrip = JSON.parse(readFileSync(mutation.artifactPath, "utf8"));
|
|
217
217
|
});
|
|
218
218
|
return { scenario: name, ok: true, payload: roundtrip };
|
|
219
219
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { JsonObject } from "./types.js";
|
|
2
|
+
/** Setup override for a noncanonical PROJECT-DEFINITION path. */
|
|
3
|
+
export declare const ENV_PROJECT_PATH = "DEFT_PROJECT_PATH";
|
|
4
|
+
/**
|
|
5
|
+
* Constant display label for a PROJECT-DEFINITION reached through the
|
|
6
|
+
* `DEFT_PROJECT_PATH` override (#3796). A configured path is operator- or
|
|
7
|
+
* environment-supplied data, so lock and loader diagnostics name this label
|
|
8
|
+
* instead of interpolating the raw path. Matches the label already used by
|
|
9
|
+
* `verify:vbrief-conformance` so one artifact reads the same way on every
|
|
10
|
+
* diagnostic surface.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CONFIGURED_PROJECT_DEFINITION_LABEL = "<configured PROJECT-DEFINITION>";
|
|
2
13
|
/**
|
|
3
14
|
* Absolute path to the PROJECT-DEFINITION artifact. Layout-aware (#2302):
|
|
4
15
|
* resolves `xbrief/PROJECT-DEFINITION.xbrief.json` on a migrated tree, else the
|
|
@@ -6,14 +17,63 @@ import type { JsonObject } from "./types.js";
|
|
|
6
17
|
* name the path that actually applies to the project's layout.
|
|
7
18
|
*/
|
|
8
19
|
export declare function projectDefinitionPath(projectRoot: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Control-safe label for an artifact path in a human diagnostic (#3796).
|
|
22
|
+
* A configured artifact collapses to {@link CONFIGURED_PROJECT_DEFINITION_LABEL};
|
|
23
|
+
* a layout-resolved canonical artifact keeps its path because the layout
|
|
24
|
+
* resolver derived it rather than reading it from configuration.
|
|
25
|
+
*/
|
|
26
|
+
export declare function projectDefinitionArtifactLabel(artifactPath: string): string;
|
|
27
|
+
/** Result of probing whether a recorded lock owner PID is still running. */
|
|
28
|
+
export type ProcessLiveness = "dead" | "alive" | "unknown";
|
|
29
|
+
/**
|
|
30
|
+
* Why an acquisition attempt could not take the lock. Only `contended` is
|
|
31
|
+
* ordinary traffic; the rest fail closed to manual recovery (#3796).
|
|
32
|
+
*/
|
|
33
|
+
export type LockBlockedReason = "contended" | "owner-alive" | "owner-liveness-unknown" | "malformed-lock-directory" | "legacy-file-sidecar";
|
|
9
34
|
export interface MutationLockDeps {
|
|
10
35
|
readonly sleepMs?: (ms: number) => void;
|
|
11
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Monotonic millisecond clock. One budget is derived from a single reading of
|
|
38
|
+
* this clock and shared by every retry and recovery branch (#3796), so a
|
|
39
|
+
* wall-clock step or a recovery detour cannot extend the acquisition window.
|
|
40
|
+
*/
|
|
41
|
+
readonly monotonicNowMs?: () => number;
|
|
42
|
+
/** Total acquisition budget in milliseconds. */
|
|
43
|
+
readonly acquisitionBudgetMs?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Three-state liveness oracle. Only `dead` authorises an automatic reap;
|
|
46
|
+
* `alive` (including possible PID reuse) and `unknown` fail closed.
|
|
47
|
+
*/
|
|
48
|
+
readonly probeProcess?: (pid: number) => ProcessLiveness;
|
|
49
|
+
readonly writeOwner?: (fd: number, payload: string) => number;
|
|
50
|
+
readonly renameLock?: (source: string, destination: string) => void;
|
|
51
|
+
/** Test seam: fires after the stale owner entry is unlinked, before `rmdir`. */
|
|
52
|
+
readonly beforeLockDirRemove?: (lockPath: string) => void;
|
|
12
53
|
}
|
|
13
|
-
/**
|
|
14
|
-
export declare
|
|
54
|
+
/** Acquisition failed within the shared budget. `reason` names the blocker. */
|
|
55
|
+
export declare class ProjectDefinitionLockError extends Error {
|
|
56
|
+
readonly reason: LockBlockedReason;
|
|
57
|
+
constructor(message: string, reason: LockBlockedReason);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Serialise PROJECT-DEFINITION read-modify-write critical sections.
|
|
61
|
+
*
|
|
62
|
+
* Acquisition publishes a fully-materialised, non-empty owner directory by
|
|
63
|
+
* renaming it onto the public lock pathname, so no contender can observe a
|
|
64
|
+
* partial lock. Prefer {@link withProjectDefinitionMutation} in
|
|
65
|
+
* `project-definition-mutation.ts`: it binds load/parse/persist to the captured
|
|
66
|
+
* artifact path so a caller cannot lock one identity and write another (#3796).
|
|
67
|
+
*/
|
|
68
|
+
export declare function projectDefinitionMutationLock<T>(projectRoot: string, fn: (artifactPath: string) => T, deps?: MutationLockDeps): T;
|
|
15
69
|
/** Read PROJECT-DEFINITION.vbrief.json and return ``(data, path)``. */
|
|
16
70
|
export declare function loadProjectDefinitionForMutation(projectRoot: string): [JsonObject, string];
|
|
71
|
+
/**
|
|
72
|
+
* Load and parse the PROJECT-DEFINITION at an already-captured path (#3796).
|
|
73
|
+
* Diagnostics name the artifact through {@link projectDefinitionArtifactLabel}
|
|
74
|
+
* so a configured path is never interpolated raw.
|
|
75
|
+
*/
|
|
76
|
+
export declare function parseProjectDefinitionAt(path: string): JsonObject;
|
|
17
77
|
/** Atomically write ``data`` to ``path`` as pretty-printed JSON. */
|
|
18
78
|
export declare function atomicWriteProjectDefinition(path: string, data: JsonObject): void;
|
|
19
79
|
//# sourceMappingURL=project-definition-io.d.ts.map
|