@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
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type GithubIssueSnapshot, type ValueAdvice } from "./types.js";
|
|
2
|
+
export declare class ReservedClearanceError extends Error {
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function assertNoReservedClearance(text: string, label: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* Value MAY recommend critique via `critique-recommend:`.
|
|
8
|
+
* It MUST NOT stamp the reserved ADR-005 clearance grammar.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildValueAdvice(issue: GithubIssueSnapshot | null): ValueAdvice;
|
|
11
|
+
export declare function formatValueField(advice: ValueAdvice): string;
|
|
12
|
+
//# sourceMappingURL=value.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { CRITIQUE_RECOMMEND_FIELD, RESERVED_CLEARANCE_RE, } from "./types.js";
|
|
2
|
+
export class ReservedClearanceError extends Error {
|
|
3
|
+
name = "ReservedClearanceError";
|
|
4
|
+
}
|
|
5
|
+
export function assertNoReservedClearance(text, label) {
|
|
6
|
+
if (RESERVED_CLEARANCE_RE.test(text)) {
|
|
7
|
+
throw new ReservedClearanceError(`${label} must not emit the reserved clearance line ` +
|
|
8
|
+
"`design-critique: warranted | not warranted, because …`");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Value MAY recommend critique via `critique-recommend:`.
|
|
13
|
+
* It MUST NOT stamp the reserved ADR-005 clearance grammar.
|
|
14
|
+
*/
|
|
15
|
+
export function buildValueAdvice(issue) {
|
|
16
|
+
const labels = issue?.labels ?? [];
|
|
17
|
+
const stamped = labels.some((label) => label === "design-critique:mechanism-shaped");
|
|
18
|
+
const reason = stamped
|
|
19
|
+
? "GitHub label design-critique:mechanism-shaped is present; author still stamps clearance"
|
|
20
|
+
: "no mechanism-shaped stamp; critique not recommended from evaluation";
|
|
21
|
+
const advice = {
|
|
22
|
+
[CRITIQUE_RECOMMEND_FIELD]: stamped,
|
|
23
|
+
reason,
|
|
24
|
+
};
|
|
25
|
+
assertNoReservedClearance(JSON.stringify(advice), "value advice");
|
|
26
|
+
return advice;
|
|
27
|
+
}
|
|
28
|
+
export function formatValueField(advice) {
|
|
29
|
+
const line = `${CRITIQUE_RECOMMEND_FIELD}: ${advice[CRITIQUE_RECOMMEND_FIELD] ? "true" : "false"}`;
|
|
30
|
+
assertNoReservedClearance(`${line}\n${advice.reason}`, "value field");
|
|
31
|
+
return `${line}\n${advice.reason}`;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=value.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WipCensus, WipHit } from "./types.js";
|
|
2
|
+
/** Parent-owned. Evaluators must never receive this object. */
|
|
3
|
+
export declare function collectWipCensus(projectRoot: string, issues: readonly number[]): WipCensus;
|
|
4
|
+
export declare function wipHitsForIssue(census: WipCensus, issue: number): WipHit[];
|
|
5
|
+
//# sourceMappingURL=wip-census.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readPlanSequence } from "../../plan-sequence/store.js";
|
|
2
|
+
import { listXbriefHits } from "./xbrief-refs.js";
|
|
3
|
+
function hitsForIssues(hits, issues, kind) {
|
|
4
|
+
return hits
|
|
5
|
+
.filter((hit) => issues.has(hit.issue))
|
|
6
|
+
.map((hit) => ({ kind, path: hit.path, issue: hit.issue }));
|
|
7
|
+
}
|
|
8
|
+
/** Parent-owned. Evaluators must never receive this object. */
|
|
9
|
+
export function collectWipCensus(projectRoot, issues) {
|
|
10
|
+
const wanted = new Set(issues);
|
|
11
|
+
const activeHits = listXbriefHits(projectRoot, "active");
|
|
12
|
+
const pendingHits = listXbriefHits(projectRoot, "pending");
|
|
13
|
+
const planHits = [];
|
|
14
|
+
const sequence = readPlanSequence(projectRoot);
|
|
15
|
+
if (sequence !== null) {
|
|
16
|
+
for (const entry of sequence.entries) {
|
|
17
|
+
const fromField = entry.issue;
|
|
18
|
+
const fromId = Number.parseInt(entry.id.trim().replace(/^#/u, ""), 10);
|
|
19
|
+
const asNumber = typeof fromField === "number" ? fromField : fromId;
|
|
20
|
+
if (!Number.isFinite(asNumber) || !wanted.has(asNumber)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
planHits.push({
|
|
24
|
+
kind: "plan-sequence",
|
|
25
|
+
path: ".deft/plan-sequence.json",
|
|
26
|
+
issue: asNumber,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
active: hitsForIssues(activeHits, wanted, "active-xbrief"),
|
|
32
|
+
pending: hitsForIssues(pendingHits, wanted, "pending-xbrief"),
|
|
33
|
+
planSequence: planHits,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function wipHitsForIssue(census, issue) {
|
|
37
|
+
return [...census.active, ...census.pending, ...census.planSequence].filter((hit) => hit.issue === issue);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=wip-census.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GitRunner } from "./types.js";
|
|
2
|
+
export declare class EvaluatorWorktreeError extends Error {
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function addEvaluatorWorktree(projectRoot: string, issue: number, invocationId: string, originSha: string, git?: GitRunner): string;
|
|
6
|
+
/** Verb-owned remove. Not a shared core git-worktree helper. */
|
|
7
|
+
export declare function removeEvaluatorWorktree(projectRoot: string, worktreePath: string, git?: GitRunner): void;
|
|
8
|
+
//# sourceMappingURL=worktrees.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { defaultGitRunner as swarmGitRunner, } from "../../swarm/worktrees.js";
|
|
4
|
+
import { evaluatorWorktreePath } from "./paths.js";
|
|
5
|
+
export class EvaluatorWorktreeError extends Error {
|
|
6
|
+
name = "EvaluatorWorktreeError";
|
|
7
|
+
}
|
|
8
|
+
function toSwarm(git) {
|
|
9
|
+
return (args, cwd) => git(args, cwd);
|
|
10
|
+
}
|
|
11
|
+
export function addEvaluatorWorktree(projectRoot, issue, invocationId, originSha, git = swarmGitRunner) {
|
|
12
|
+
const sha = originSha.trim();
|
|
13
|
+
if (sha.length === 0) {
|
|
14
|
+
throw new EvaluatorWorktreeError("originSha is required for a detached evaluator worktree");
|
|
15
|
+
}
|
|
16
|
+
const worktreePath = evaluatorWorktreePath(projectRoot, issue, invocationId);
|
|
17
|
+
mkdirSync(dirname(resolve(worktreePath)), { recursive: true });
|
|
18
|
+
const proc = toSwarm(git)(["worktree", "add", "--detach", worktreePath, sha], projectRoot);
|
|
19
|
+
if (proc.returncode !== 0) {
|
|
20
|
+
throw new EvaluatorWorktreeError(`git worktree add --detach failed for issue ${issue}: ${proc.stderr.trim() || "<no stderr>"}`);
|
|
21
|
+
}
|
|
22
|
+
return worktreePath;
|
|
23
|
+
}
|
|
24
|
+
function forceDeleteWorktreeDir(worktreePath) {
|
|
25
|
+
if (!existsSync(worktreePath)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
rmSync(worktreePath, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
function worktreeStillRegistered(git, projectRoot, worktreePath) {
|
|
31
|
+
const listed = git(["worktree", "list", "--porcelain"], projectRoot);
|
|
32
|
+
if (listed.returncode !== 0) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const needle = resolve(worktreePath).replace(/\\/g, "/").toLowerCase();
|
|
36
|
+
for (const line of listed.stdout.split(/\r?\n/u)) {
|
|
37
|
+
if (!line.startsWith("worktree ")) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const listedPath = line.slice("worktree ".length).replace(/\\/g, "/").toLowerCase();
|
|
41
|
+
if (listedPath === needle) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
/** Verb-owned remove. Not a shared core git-worktree helper. */
|
|
48
|
+
export function removeEvaluatorWorktree(projectRoot, worktreePath, git = swarmGitRunner) {
|
|
49
|
+
const runner = toSwarm(git);
|
|
50
|
+
const proc = runner(["worktree", "remove", "--force", worktreePath], projectRoot);
|
|
51
|
+
if (proc.returncode === 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const firstError = proc.stderr.trim() || "<no stderr>";
|
|
55
|
+
forceDeleteWorktreeDir(worktreePath);
|
|
56
|
+
runner(["worktree", "prune"], projectRoot);
|
|
57
|
+
const retry = runner(["worktree", "remove", "--force", worktreePath], projectRoot);
|
|
58
|
+
if (retry.returncode === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
forceDeleteWorktreeDir(worktreePath);
|
|
62
|
+
runner(["worktree", "prune"], projectRoot);
|
|
63
|
+
if (!worktreeStillRegistered(runner, projectRoot, worktreePath) && !existsSync(worktreePath)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
throw new EvaluatorWorktreeError(`git worktree remove failed for ${worktreePath}: ${firstError}; fallback: ${retry.stderr.trim() || "<no stderr>"}`);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=worktrees.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface XbriefHit {
|
|
2
|
+
readonly issue: number;
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly folder: "active" | "pending" | "proposed" | "completed" | "cancelled";
|
|
5
|
+
}
|
|
6
|
+
export declare function issueNumbersFromXbriefJson(raw: unknown): number[];
|
|
7
|
+
export declare function listXbriefHits(root: string, folder: XbriefHit["folder"]): XbriefHit[];
|
|
8
|
+
//# sourceMappingURL=xbrief-refs.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const ISSUE_URI_RE = /\/issues\/(\d+)\b/u;
|
|
4
|
+
const ISSUE_HASH_RE = /(?:Issue\s+)?#(\d+)\b/u;
|
|
5
|
+
function numbersFromUnknown(value, into) {
|
|
6
|
+
if (typeof value === "string") {
|
|
7
|
+
for (const re of [ISSUE_URI_RE, ISSUE_HASH_RE]) {
|
|
8
|
+
const match = re.exec(value);
|
|
9
|
+
if (match?.[1] !== undefined) {
|
|
10
|
+
into.add(Number.parseInt(match[1], 10));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
for (const item of value) {
|
|
17
|
+
numbersFromUnknown(item, into);
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (value !== null && typeof value === "object") {
|
|
22
|
+
for (const nested of Object.values(value)) {
|
|
23
|
+
numbersFromUnknown(nested, into);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function issueNumbersFromXbriefJson(raw) {
|
|
28
|
+
const found = new Set();
|
|
29
|
+
if (raw === null || typeof raw !== "object") {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const plan = raw.plan;
|
|
33
|
+
numbersFromUnknown(plan?.references, found);
|
|
34
|
+
return [...found];
|
|
35
|
+
}
|
|
36
|
+
export function listXbriefHits(root, folder) {
|
|
37
|
+
const dir = join(root, "xbrief", folder);
|
|
38
|
+
if (!existsSync(dir)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const hits = [];
|
|
42
|
+
for (const name of readdirSync(dir)) {
|
|
43
|
+
if (!name.endsWith(".xbrief.json") && !name.endsWith(".vbrief.json")) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const path = join(dir, name);
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
for (const issue of issueNumbersFromXbriefJson(parsed)) {
|
|
55
|
+
hits.push({ issue, path, folder });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return hits;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=xbrief-refs.js.map
|
|
@@ -23,6 +23,17 @@ export declare const registryData: {
|
|
|
23
23
|
readonly see_also: readonly ["task triage:bootstrap", "task cache:fetch-all", "#1119 / D5"];
|
|
24
24
|
readonly placeholder: false;
|
|
25
25
|
};
|
|
26
|
+
readonly "task triage:evaluate": {
|
|
27
|
+
readonly name: "task triage:evaluate";
|
|
28
|
+
readonly summary: "Isolated validity + parent WIP census + value advice";
|
|
29
|
+
readonly refs: "(#3648)";
|
|
30
|
+
readonly description: "Stage A issue-eval: detached origin/master worktrees own validity and ADR/contract reads; the parent on the live working set owns WIP census (xbrief/active, xbrief/pending, plan-sequence); GitHub REST owns open PRs/issues/duplicates. Writes a gitignored sink under .deft-scratch/issue-eval/<sha12>/<invocation-id>/. No GitHub writes. No candidates-log row. Value may set critique-recommend: and must not emit the reserved design-critique clearance line. Fan-out default 4, override --concurrency N.";
|
|
31
|
+
readonly usage: "task triage:evaluate -- <N...> [--concurrency N] [--repo OWNER/NAME] [--json]";
|
|
32
|
+
readonly flags: readonly [readonly ["<N...>", "(required)", "Issue numbers to evaluate."], readonly ["--concurrency N", "4", "Parallel evaluator cap (bind, not a measured existing cap)."], readonly ["--repo OWNER/NAME", "(git remote / env)", "Upstream repo for REST reads."], readonly ["--json", "(off)", "Emit the structured EvaluateResult."], readonly ["--project-root PATH", "(cwd)", "Project root override (Taskfile threads USER_WORKING_DIR)."]];
|
|
33
|
+
readonly examples: readonly ["task triage:evaluate -- 3648", "task triage:evaluate -- 10 11 --concurrency 2 --json"];
|
|
34
|
+
readonly see_also: readonly ["task triage:queue", "task triage:accept", "skills/deft-directive-issue-eval/SKILL.md", "#3648"];
|
|
35
|
+
readonly placeholder: false;
|
|
36
|
+
};
|
|
26
37
|
readonly "task triage:accept": {
|
|
27
38
|
readonly name: "task triage:accept";
|
|
28
39
|
readonly summary: "Mark issue accepted; chains into scope:promote w/ flag";
|
|
@@ -444,12 +455,12 @@ export declare const registryData: {
|
|
|
444
455
|
readonly "task scope:complete": {
|
|
445
456
|
readonly name: "task scope:complete";
|
|
446
457
|
readonly summary: "active/ -> completed/ (set status completed)";
|
|
447
|
-
readonly refs: "(#845)";
|
|
448
|
-
readonly description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15).";
|
|
449
|
-
readonly usage: "task scope:complete -- <file>";
|
|
450
|
-
readonly flags: readonly [readonly ["<file>", "(required)", "Path to vBRIEF."], readonly ["--project-root PATH", "(detected)", "Consumer project root override."]];
|
|
451
|
-
readonly examples: readonly ["task scope:complete -- vbrief/active/2026-05-19-foo.vbrief.json"];
|
|
452
|
-
readonly see_also: readonly ["task scope:fail", "task scope:cancel", "#1119 / #845"];
|
|
458
|
+
readonly refs: "(#845, #3041, #3721)";
|
|
459
|
+
readonly description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15). Code-bearing completion requires delivery evidence: --merge-commit and --pr. Those flags feed the delivery gate; they do not skip literal-AC.";
|
|
460
|
+
readonly usage: "task scope:complete -- <file> [--merge-commit SHA] [--pr N]";
|
|
461
|
+
readonly flags: readonly [readonly ["<file>", "(required)", "Path to vBRIEF."], readonly ["--project-root PATH", "(detected)", "Consumer project root override."], readonly ["--merge-commit SHA", "(required for code-bearing)", "Merge commit SHA for delivery evidence (#3041)."], readonly ["--pr N", "(required for code-bearing)", "Merged PR number for delivery evidence (#3041)."], readonly ["--pr-base BRANCH", "(optional)", "PR base branch for delivery evidence."], readonly ["--delivery-branch BRANCH", "(optional)", "Delivery branch tip for completed-tracked."]];
|
|
462
|
+
readonly examples: readonly ["task scope:complete -- vbrief/active/2026-05-19-foo.vbrief.json", "task scope:complete -- xbrief/active/story.xbrief.json --merge-commit abc123 --pr 42"];
|
|
463
|
+
readonly see_also: readonly ["task scope:fail", "task scope:cancel", "#1119 / #845", "#3041"];
|
|
453
464
|
readonly placeholder: false;
|
|
454
465
|
};
|
|
455
466
|
readonly "task scope:fail": {
|
|
@@ -530,7 +541,7 @@ export declare const registryData: {
|
|
|
530
541
|
readonly placeholder: false;
|
|
531
542
|
};
|
|
532
543
|
};
|
|
533
|
-
readonly categoriesTriage: readonly [readonly ["Session-start", readonly ["task triage:summary", "task verify:cache-fresh"]], readonly ["State verbs (mutate audit log)", readonly ["task triage:accept", "task triage:defer", "task triage:reject", "task triage:needs-ac", "task triage:mark-duplicate", "task triage:reset", "task triage:status", "task triage:history"]], readonly ["Read verbs", readonly ["task triage:queue", "task triage:audit", "task triage:show", "task triage:scope", "task triage:scope-drift", "task triage:classify"]], readonly ["Lifecycle", readonly ["task triage:bootstrap", "task triage:welcome", "task triage:reconcile"]], readonly ["Bulk variants", readonly ["task triage:bulk-accept", "task triage:bulk-reject", "task triage:bulk-defer", "task triage:bulk-needs-ac", "task triage:refresh-active", "task triage:smoketest"]], readonly ["Subscription mutation", readonly ["task triage:subscribe", "task triage:unsubscribe"]], readonly ["Archive / rotation", readonly ["task triage:audit:prune", "task triage:archive-list", "task triage:restore-from-archive", "task triage:audit-log:rotate", "task triage:metrics"]], readonly ["Framework eval (#1703)", readonly ["task eval:health", "task eval:triggers", "task eval:run", "task eval:report"]]];
|
|
544
|
+
readonly categoriesTriage: readonly [readonly ["Session-start", readonly ["task triage:summary", "task verify:cache-fresh"]], readonly ["State verbs (mutate audit log)", readonly ["task triage:accept", "task triage:defer", "task triage:reject", "task triage:needs-ac", "task triage:mark-duplicate", "task triage:reset", "task triage:status", "task triage:history"]], readonly ["Read verbs", readonly ["task triage:queue", "task triage:evaluate", "task triage:audit", "task triage:show", "task triage:scope", "task triage:scope-drift", "task triage:classify"]], readonly ["Lifecycle", readonly ["task triage:bootstrap", "task triage:welcome", "task triage:reconcile"]], readonly ["Bulk variants", readonly ["task triage:bulk-accept", "task triage:bulk-reject", "task triage:bulk-defer", "task triage:bulk-needs-ac", "task triage:refresh-active", "task triage:smoketest"]], readonly ["Subscription mutation", readonly ["task triage:subscribe", "task triage:unsubscribe"]], readonly ["Archive / rotation", readonly ["task triage:audit:prune", "task triage:archive-list", "task triage:restore-from-archive", "task triage:audit-log:rotate", "task triage:metrics"]], readonly ["Framework eval (#1703)", readonly ["task eval:health", "task eval:triggers", "task eval:run", "task eval:report"]]];
|
|
534
545
|
readonly categoriesScope: readonly [readonly ["Promote / demote", readonly ["task scope:promote", "task scope:demote"]], readonly ["Activate / complete", readonly ["task scope:activate", "task scope:complete", "task scope:fail", "task scope:cancel", "task scope:block", "task scope:unblock"]], readonly ["Reversibility", readonly ["task scope:undo", "task scope:restore"]], readonly ["Decomposition", readonly ["task scope:decompose"]]];
|
|
535
546
|
readonly scriptSubcommandMap: {
|
|
536
547
|
readonly triage_actions: {
|
|
@@ -575,6 +586,9 @@ export declare const registryData: {
|
|
|
575
586
|
readonly triage_reconcile: {
|
|
576
587
|
readonly __default__: "task triage:reconcile";
|
|
577
588
|
};
|
|
589
|
+
readonly triage_evaluate: {
|
|
590
|
+
readonly __default__: "task triage:evaluate";
|
|
591
|
+
};
|
|
578
592
|
readonly triage_queue: {
|
|
579
593
|
readonly queue: "task triage:queue";
|
|
580
594
|
readonly show: "task triage:show";
|
|
@@ -34,6 +34,35 @@ export const registryData = {
|
|
|
34
34
|
see_also: ["task triage:bootstrap", "task cache:fetch-all", "#1119 / D5"],
|
|
35
35
|
placeholder: false,
|
|
36
36
|
},
|
|
37
|
+
"task triage:evaluate": {
|
|
38
|
+
name: "task triage:evaluate",
|
|
39
|
+
summary: "Isolated validity + parent WIP census + value advice",
|
|
40
|
+
refs: "(#3648)",
|
|
41
|
+
description: "Stage A issue-eval: detached origin/master worktrees own validity and ADR/contract reads; the parent on the live working set owns WIP census (xbrief/active, xbrief/pending, plan-sequence); GitHub REST owns open PRs/issues/duplicates. Writes a gitignored sink under .deft-scratch/issue-eval/<sha12>/<invocation-id>/. No GitHub writes. No candidates-log row. Value may set critique-recommend: and must not emit the reserved design-critique clearance line. Fan-out default 4, override --concurrency N.",
|
|
42
|
+
usage: "task triage:evaluate -- <N...> [--concurrency N] [--repo OWNER/NAME] [--json]",
|
|
43
|
+
flags: [
|
|
44
|
+
["<N...>", "(required)", "Issue numbers to evaluate."],
|
|
45
|
+
["--concurrency N", "4", "Parallel evaluator cap (bind, not a measured existing cap)."],
|
|
46
|
+
["--repo OWNER/NAME", "(git remote / env)", "Upstream repo for REST reads."],
|
|
47
|
+
["--json", "(off)", "Emit the structured EvaluateResult."],
|
|
48
|
+
[
|
|
49
|
+
"--project-root PATH",
|
|
50
|
+
"(cwd)",
|
|
51
|
+
"Project root override (Taskfile threads USER_WORKING_DIR).",
|
|
52
|
+
],
|
|
53
|
+
],
|
|
54
|
+
examples: [
|
|
55
|
+
"task triage:evaluate -- 3648",
|
|
56
|
+
"task triage:evaluate -- 10 11 --concurrency 2 --json",
|
|
57
|
+
],
|
|
58
|
+
see_also: [
|
|
59
|
+
"task triage:queue",
|
|
60
|
+
"task triage:accept",
|
|
61
|
+
"skills/deft-directive-issue-eval/SKILL.md",
|
|
62
|
+
"#3648",
|
|
63
|
+
],
|
|
64
|
+
placeholder: false,
|
|
65
|
+
},
|
|
37
66
|
"task triage:accept": {
|
|
38
67
|
name: "task triage:accept",
|
|
39
68
|
summary: "Mark issue accepted; chains into scope:promote w/ flag",
|
|
@@ -782,15 +811,30 @@ export const registryData = {
|
|
|
782
811
|
"task scope:complete": {
|
|
783
812
|
name: "task scope:complete",
|
|
784
813
|
summary: "active/ -> completed/ (set status completed)",
|
|
785
|
-
refs: "(#845)",
|
|
786
|
-
description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15).",
|
|
787
|
-
usage: "task scope:complete -- <file>",
|
|
814
|
+
refs: "(#845, #3041, #3721)",
|
|
815
|
+
description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15). Code-bearing completion requires delivery evidence: --merge-commit and --pr. Those flags feed the delivery gate; they do not skip literal-AC.",
|
|
816
|
+
usage: "task scope:complete -- <file> [--merge-commit SHA] [--pr N]",
|
|
788
817
|
flags: [
|
|
789
818
|
["<file>", "(required)", "Path to vBRIEF."],
|
|
790
819
|
["--project-root PATH", "(detected)", "Consumer project root override."],
|
|
820
|
+
[
|
|
821
|
+
"--merge-commit SHA",
|
|
822
|
+
"(required for code-bearing)",
|
|
823
|
+
"Merge commit SHA for delivery evidence (#3041).",
|
|
824
|
+
],
|
|
825
|
+
[
|
|
826
|
+
"--pr N",
|
|
827
|
+
"(required for code-bearing)",
|
|
828
|
+
"Merged PR number for delivery evidence (#3041).",
|
|
829
|
+
],
|
|
830
|
+
["--pr-base BRANCH", "(optional)", "PR base branch for delivery evidence."],
|
|
831
|
+
["--delivery-branch BRANCH", "(optional)", "Delivery branch tip for completed-tracked."],
|
|
791
832
|
],
|
|
792
|
-
examples: [
|
|
793
|
-
|
|
833
|
+
examples: [
|
|
834
|
+
"task scope:complete -- vbrief/active/2026-05-19-foo.vbrief.json",
|
|
835
|
+
"task scope:complete -- xbrief/active/story.xbrief.json --merge-commit abc123 --pr 42",
|
|
836
|
+
],
|
|
837
|
+
see_also: ["task scope:fail", "task scope:cancel", "#1119 / #845", "#3041"],
|
|
794
838
|
placeholder: false,
|
|
795
839
|
},
|
|
796
840
|
"task scope:fail": {
|
|
@@ -930,6 +974,7 @@ export const registryData = {
|
|
|
930
974
|
"Read verbs",
|
|
931
975
|
[
|
|
932
976
|
"task triage:queue",
|
|
977
|
+
"task triage:evaluate",
|
|
933
978
|
"task triage:audit",
|
|
934
979
|
"task triage:show",
|
|
935
980
|
"task triage:scope",
|
|
@@ -1024,6 +1069,9 @@ export const registryData = {
|
|
|
1024
1069
|
triage_reconcile: {
|
|
1025
1070
|
__default__: "task triage:reconcile",
|
|
1026
1071
|
},
|
|
1072
|
+
triage_evaluate: {
|
|
1073
|
+
__default__: "task triage:evaluate",
|
|
1074
|
+
},
|
|
1027
1075
|
triage_queue: {
|
|
1028
1076
|
queue: "task triage:queue",
|
|
1029
1077
|
show: "task triage:show",
|
package/dist/triage/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * as bulk from "./bulk/index.js";
|
|
|
13
13
|
export * as cachePath from "./cache-path.js";
|
|
14
14
|
export * from "./cache-path.js";
|
|
15
15
|
export * as classify from "./classify/index.js";
|
|
16
|
+
export * as evaluate from "./evaluate/index.js";
|
|
16
17
|
export * as help from "./help/index.js";
|
|
17
18
|
export * as queue from "./queue/index.js";
|
|
18
19
|
export * as reconcile from "./reconcile/index.js";
|
package/dist/triage/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * as bulk from "./bulk/index.js";
|
|
|
13
13
|
export * as cachePath from "./cache-path.js";
|
|
14
14
|
export * from "./cache-path.js";
|
|
15
15
|
export * as classify from "./classify/index.js";
|
|
16
|
+
export * as evaluate from "./evaluate/index.js";
|
|
16
17
|
export * as help from "./help/index.js";
|
|
17
18
|
export * as queue from "./queue/index.js";
|
|
18
19
|
export * as reconcile from "./reconcile/index.js";
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
}
|
|
4
|
-
export declare function projectDefinitionPath(projectRoot: string): string;
|
|
5
|
-
export declare function loadProjectDefinitionForMutation(projectRoot: string): [Record<string, unknown>, string];
|
|
6
|
-
export declare function atomicWriteProjectDefinition(path: string, data: Record<string, unknown>): void;
|
|
1
|
+
export { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionPath, } from "../../vbrief-build/project-definition-io.js";
|
|
2
|
+
export { ProjectDefinitionIOError } from "../../vbrief-build/types.js";
|
|
7
3
|
export declare function recordSubscriptionChange(projectRoot: string, options: {
|
|
8
4
|
op: string;
|
|
9
5
|
label?: string | null;
|
|
@@ -1,52 +1,20 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { appendFileSync,
|
|
3
|
-
import {
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
import { resolveProjectDefinitionPath } from "../../layout/resolve.js";
|
|
2
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
6
4
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
7
|
-
import {
|
|
5
|
+
import { withProjectDefinitionMutation } from "../../vbrief-build/project-definition-mutation.js";
|
|
8
6
|
import { resolveTriageCachePath } from "../cache-path.js";
|
|
9
7
|
import { SUBSCRIPTION_HISTORY_SCHEMA } from "./constants.js";
|
|
10
8
|
import { pyStrRepr } from "./python-repr.js";
|
|
11
9
|
import { utcIso } from "./time.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
if (!existsSync(path)) {
|
|
21
|
-
throw new ProjectDefinitionIOError(`PROJECT-DEFINITION not found at ${path}; run task triage:welcome / ` +
|
|
22
|
-
"task triage:bootstrap to scaffold one first.");
|
|
23
|
-
}
|
|
24
|
-
let raw;
|
|
25
|
-
try {
|
|
26
|
-
raw = readFileSync(path, "utf8");
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
throw new ProjectDefinitionIOError(`Could not read PROJECT-DEFINITION at ${path}: ${String(err)}`);
|
|
30
|
-
}
|
|
31
|
-
let data;
|
|
32
|
-
try {
|
|
33
|
-
data = JSON.parse(raw);
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
throw new ProjectDefinitionIOError(`PROJECT-DEFINITION at ${path} is not valid JSON: ${String(err)}`);
|
|
37
|
-
}
|
|
38
|
-
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
39
|
-
throw new ProjectDefinitionIOError(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
40
|
-
}
|
|
41
|
-
return [data, path];
|
|
42
|
-
}
|
|
43
|
-
export function atomicWriteProjectDefinition(path, data) {
|
|
44
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
45
|
-
const payload = `${JSON.stringify(data, null, 2)}\n`;
|
|
46
|
-
const tmp = join(tmpdir(), `${randomUUID()}.tmp`);
|
|
47
|
-
writeFileSync(tmp, payload, "utf8");
|
|
48
|
-
renameSync(tmp, path);
|
|
49
|
-
}
|
|
10
|
+
// One canonical PROJECT-DEFINITION identity (#3796). The resolver, loader,
|
|
11
|
+
// error type, and write sink are the shared implementations the mutation
|
|
12
|
+
// capability binds. Module-local duplicates used to resolve the layout path
|
|
13
|
+
// directly -- ignoring the `DEFT_PROJECT_PATH` override the lock honours -- and
|
|
14
|
+
// wrote through an uncontained temp sink, so a caller could lock one artifact
|
|
15
|
+
// and load or persist another.
|
|
16
|
+
export { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionPath, } from "../../vbrief-build/project-definition-io.js";
|
|
17
|
+
export { ProjectDefinitionIOError } from "../../vbrief-build/types.js";
|
|
50
18
|
function resolveActor(actor) {
|
|
51
19
|
if (typeof actor === "string" && actor.trim())
|
|
52
20
|
return actor;
|
|
@@ -133,11 +101,11 @@ export function subscribe(projectRoot, options) {
|
|
|
133
101
|
// Serialise the read-modify-write + subscription-history append under the
|
|
134
102
|
// shared PROJECT-DEFINITION mutation lock so concurrent mutators cannot lose
|
|
135
103
|
// an update or emit out-of-order audit rows (#1260).
|
|
136
|
-
return
|
|
137
|
-
const
|
|
104
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
105
|
+
const data = mutation.load();
|
|
138
106
|
const plan = data.plan;
|
|
139
107
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
140
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
108
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-object 'plan' key`);
|
|
141
109
|
}
|
|
142
110
|
const planRec = plan;
|
|
143
111
|
migrateLegacyPolicyKey(planRec);
|
|
@@ -145,14 +113,14 @@ export function subscribe(projectRoot, options) {
|
|
|
145
113
|
planRec[PLAN_POLICY_KEY] = {};
|
|
146
114
|
const policy = planRec[PLAN_POLICY_KEY];
|
|
147
115
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
148
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
116
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-object 'plan.policy' key`);
|
|
149
117
|
}
|
|
150
118
|
const policyRec = policy;
|
|
151
119
|
if (policyRec.triageScope === undefined)
|
|
152
120
|
policyRec.triageScope = [];
|
|
153
121
|
const rules = policyRec.triageScope;
|
|
154
122
|
if (!Array.isArray(rules)) {
|
|
155
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
123
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-list 'plan.policy.triageScope'`);
|
|
156
124
|
}
|
|
157
125
|
const before = snapshotRules(rules);
|
|
158
126
|
let changed;
|
|
@@ -168,7 +136,7 @@ export function subscribe(projectRoot, options) {
|
|
|
168
136
|
}
|
|
169
137
|
if (!changed)
|
|
170
138
|
return [false, message];
|
|
171
|
-
|
|
139
|
+
mutation.persist(data);
|
|
172
140
|
recordSubscriptionChange(projectRoot, {
|
|
173
141
|
op: "subscribe",
|
|
174
142
|
label: options.label ?? null,
|
|
@@ -185,11 +153,11 @@ export function addIgnore(projectRoot, label) {
|
|
|
185
153
|
throw new Error(`label must be a non-empty string; got ${JSON.stringify(label)}`);
|
|
186
154
|
// Serialise the read-modify-write + subscription-history append under the
|
|
187
155
|
// shared PROJECT-DEFINITION mutation lock (#1260).
|
|
188
|
-
return
|
|
189
|
-
const
|
|
156
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
157
|
+
const data = mutation.load();
|
|
190
158
|
const plan = data.plan;
|
|
191
159
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
192
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
160
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-object 'plan' key`);
|
|
193
161
|
}
|
|
194
162
|
const planRec = plan;
|
|
195
163
|
migrateLegacyPolicyKey(planRec);
|
|
@@ -197,14 +165,14 @@ export function addIgnore(projectRoot, label) {
|
|
|
197
165
|
planRec[PLAN_POLICY_KEY] = {};
|
|
198
166
|
const policy = planRec[PLAN_POLICY_KEY];
|
|
199
167
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
200
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
168
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-object 'plan.policy' key`);
|
|
201
169
|
}
|
|
202
170
|
const policyRec = policy;
|
|
203
171
|
if (policyRec.triageScopeIgnores === undefined)
|
|
204
172
|
policyRec.triageScopeIgnores = [];
|
|
205
173
|
const raw = policyRec.triageScopeIgnores;
|
|
206
174
|
if (!Array.isArray(raw)) {
|
|
207
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
175
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-list 'plan.policy.triageScopeIgnores'`);
|
|
208
176
|
}
|
|
209
177
|
const before = snapshotRules(raw);
|
|
210
178
|
for (const entry of raw) {
|
|
@@ -216,7 +184,7 @@ export function addIgnore(projectRoot, label) {
|
|
|
216
184
|
}
|
|
217
185
|
}
|
|
218
186
|
raw.push({ label });
|
|
219
|
-
|
|
187
|
+
mutation.persist(data);
|
|
220
188
|
const after = snapshotRules(raw);
|
|
221
189
|
recordSubscriptionChange(projectRoot, {
|
|
222
190
|
op: "ignore-label",
|
|
@@ -1,39 +1,5 @@
|
|
|
1
|
-
import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import { resolveProjectDefinitionPath } from "../../layout/resolve.js";
|
|
4
1
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
5
|
-
import {
|
|
6
|
-
function loadForMutation(projectRoot) {
|
|
7
|
-
const path = resolveProjectDefinitionPath(resolve(projectRoot));
|
|
8
|
-
if (!existsSync(path)) {
|
|
9
|
-
throw new Error(`PROJECT-DEFINITION not found at ${path}; run task triage:welcome / task triage:bootstrap to scaffold one first.`);
|
|
10
|
-
}
|
|
11
|
-
let raw;
|
|
12
|
-
try {
|
|
13
|
-
raw = readFileSync(path, "utf8");
|
|
14
|
-
}
|
|
15
|
-
catch (err) {
|
|
16
|
-
throw new Error(`Could not read PROJECT-DEFINITION at ${path}: ${String(err)}`);
|
|
17
|
-
}
|
|
18
|
-
let data;
|
|
19
|
-
try {
|
|
20
|
-
data = JSON.parse(raw);
|
|
21
|
-
}
|
|
22
|
-
catch (err) {
|
|
23
|
-
throw new Error(`PROJECT-DEFINITION at ${path} is not valid JSON: ${String(err)}`);
|
|
24
|
-
}
|
|
25
|
-
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
26
|
-
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
27
|
-
}
|
|
28
|
-
return [data, path];
|
|
29
|
-
}
|
|
30
|
-
function atomicWrite(path, data) {
|
|
31
|
-
const dir = dirname(path);
|
|
32
|
-
const payload = `${JSON.stringify(data, null, 2)}\n`;
|
|
33
|
-
const tmp = join(dir, `.${Date.now()}.tmp`);
|
|
34
|
-
writeFileSync(tmp, payload, "utf8");
|
|
35
|
-
renameSync(tmp, path);
|
|
36
|
-
}
|
|
2
|
+
import { withProjectDefinitionMutation } from "../../vbrief-build/project-definition-mutation.js";
|
|
37
3
|
/** Append a label/milestone ignore entry — mirrors Python `add_ignore`. */
|
|
38
4
|
export function addIgnore(projectRoot, options) {
|
|
39
5
|
const hasLabel = options.label !== undefined;
|
|
@@ -48,11 +14,11 @@ export function addIgnore(projectRoot, options) {
|
|
|
48
14
|
}
|
|
49
15
|
// Serialise the read-modify-write under the shared PROJECT-DEFINITION
|
|
50
16
|
// mutation lock so concurrent mutators cannot lose an update (#1260).
|
|
51
|
-
return
|
|
52
|
-
const
|
|
17
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
18
|
+
const data = mutation.load();
|
|
53
19
|
const plan = data.plan;
|
|
54
20
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
55
|
-
throw new Error(`PROJECT-DEFINITION at ${
|
|
21
|
+
throw new Error(`PROJECT-DEFINITION at ${mutation.artifactLabel} has a non-object 'plan' key`);
|
|
56
22
|
}
|
|
57
23
|
const planRec = plan;
|
|
58
24
|
migrateLegacyPolicyKey(planRec);
|
|
@@ -74,7 +40,7 @@ export function addIgnore(projectRoot, options) {
|
|
|
74
40
|
}
|
|
75
41
|
}
|
|
76
42
|
raw.push({ [key]: value });
|
|
77
|
-
|
|
43
|
+
mutation.persist(data);
|
|
78
44
|
return { changed: true, message: `added ignore (${key}=${value})` };
|
|
79
45
|
});
|
|
80
46
|
}
|