@deftai/directive-core 0.69.0 → 0.71.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.
Files changed (103) hide show
  1. package/dist/branch/evaluate.js +7 -0
  2. package/dist/deposit/contain.d.ts +41 -0
  3. package/dist/deposit/contain.js +113 -0
  4. package/dist/deposit/copy-tree.js +8 -2
  5. package/dist/doctor/checks.d.ts +11 -0
  6. package/dist/doctor/checks.js +75 -2
  7. package/dist/doctor/doctor-state.js +2 -2
  8. package/dist/doctor/main.js +54 -1
  9. package/dist/doctor/manifest.d.ts +20 -0
  10. package/dist/doctor/manifest.js +22 -0
  11. package/dist/doctor/types.d.ts +8 -0
  12. package/dist/eval/crud-telemetry.d.ts +59 -0
  13. package/dist/eval/crud-telemetry.js +307 -0
  14. package/dist/eval/health.d.ts +52 -0
  15. package/dist/eval/health.js +240 -0
  16. package/dist/eval/readback.d.ts +38 -0
  17. package/dist/eval/readback.js +209 -0
  18. package/dist/eval/report.d.ts +53 -0
  19. package/dist/eval/report.js +161 -0
  20. package/dist/eval/run.d.ts +79 -0
  21. package/dist/eval/run.js +309 -0
  22. package/dist/events/attribution-constants.d.ts +13 -0
  23. package/dist/events/attribution-constants.js +18 -0
  24. package/dist/events/attribution-ledger.d.ts +43 -0
  25. package/dist/events/attribution-ledger.js +61 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.js +5 -0
  28. package/dist/init-deposit/gitignore.js +5 -5
  29. package/dist/init-deposit/init-deposit.js +6 -0
  30. package/dist/init-deposit/refresh.js +5 -0
  31. package/dist/intake/issue-ingest.d.ts +11 -0
  32. package/dist/intake/issue-ingest.js +86 -11
  33. package/dist/layout/resolve.d.ts +2 -2
  34. package/dist/layout/resolve.js +2 -2
  35. package/dist/lifecycle/events.js +2 -0
  36. package/dist/policy/index.d.ts +2 -1
  37. package/dist/policy/index.js +15 -2
  38. package/dist/policy/plan-extensions.d.ts +31 -0
  39. package/dist/policy/plan-extensions.js +45 -0
  40. package/dist/policy/value-feedback.d.ts +56 -0
  41. package/dist/policy/value-feedback.js +284 -0
  42. package/dist/policy/wip.d.ts +2 -2
  43. package/dist/policy/wip.js +2 -2
  44. package/dist/preflight-cache/evaluate.js +4 -3
  45. package/dist/scope/audit-log.js +3 -3
  46. package/dist/scope/transition.js +16 -5
  47. package/dist/session/session-start.js +20 -0
  48. package/dist/slice/record.js +3 -3
  49. package/dist/swarm/routing.js +10 -3
  50. package/dist/triage/actions/candidates-log.d.ts +4 -4
  51. package/dist/triage/actions/candidates-log.js +4 -7
  52. package/dist/triage/bootstrap/gitignore.d.ts +7 -3
  53. package/dist/triage/bootstrap/gitignore.js +67 -37
  54. package/dist/triage/bootstrap/index.d.ts +1 -1
  55. package/dist/triage/bootstrap/index.js +4 -3
  56. package/dist/triage/bulk/index.d.ts +11 -2
  57. package/dist/triage/bulk/index.js +43 -6
  58. package/dist/triage/cache-path.d.ts +39 -0
  59. package/dist/triage/cache-path.js +98 -0
  60. package/dist/triage/help/registry-data.d.ts +53 -20
  61. package/dist/triage/help/registry-data.js +104 -20
  62. package/dist/triage/index.d.ts +2 -0
  63. package/dist/triage/index.js +2 -0
  64. package/dist/triage/queue/audit.js +2 -2
  65. package/dist/triage/queue/cache.js +2 -2
  66. package/dist/triage/reconcile/reconcile.js +4 -3
  67. package/dist/triage/scope/cli.d.ts +2 -1
  68. package/dist/triage/scope/cli.js +30 -6
  69. package/dist/triage/scope/mutations-core.js +3 -2
  70. package/dist/triage/scope/mutations.d.ts +10 -0
  71. package/dist/triage/scope/mutations.js +22 -0
  72. package/dist/triage/subscribe/index.d.ts +1 -1
  73. package/dist/triage/subscribe/index.js +4 -3
  74. package/dist/triage/summary/index.d.ts +2 -2
  75. package/dist/triage/summary/index.js +4 -3
  76. package/dist/triage/summary/reconcilable.d.ts +1 -1
  77. package/dist/triage/summary/reconcilable.js +3 -2
  78. package/dist/triage/welcome/constants.d.ts +3 -4
  79. package/dist/triage/welcome/constants.js +3 -4
  80. package/dist/triage/welcome/index.d.ts +1 -0
  81. package/dist/triage/welcome/index.js +1 -0
  82. package/dist/triage/welcome/onboard.d.ts +35 -0
  83. package/dist/triage/welcome/onboard.js +94 -0
  84. package/dist/triage/welcome/prior-state.js +3 -2
  85. package/dist/triage/welcome/summary.js +5 -4
  86. package/dist/umbrella-current-shape/index.d.ts +25 -1
  87. package/dist/umbrella-current-shape/index.js +84 -4
  88. package/dist/value/adoption-emit.d.ts +14 -0
  89. package/dist/value/adoption-emit.js +67 -0
  90. package/dist/value/adoption-registry.d.ts +62 -0
  91. package/dist/value/adoption-registry.js +214 -0
  92. package/dist/value/feedback-file.d.ts +67 -0
  93. package/dist/value/feedback-file.js +353 -0
  94. package/dist/value/friction-emit.d.ts +10 -0
  95. package/dist/value/friction-emit.js +24 -0
  96. package/dist/value/readback.d.ts +86 -0
  97. package/dist/value/readback.js +503 -0
  98. package/dist/vbrief-build/project-definition-io.d.ts +6 -0
  99. package/dist/vbrief-build/project-definition-io.js +7 -2
  100. package/dist/vbrief-validate/decomposition.js +1 -1
  101. package/dist/vbrief-validate/precutover.js +7 -3
  102. package/dist/wip-cap/evaluate.js +3 -0
  103. package/package.json +35 -3
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, relative } from "node:path";
3
- import { resolveEvalPath } from "../../layout/resolve.js";
3
+ import { resolveLifecycleLayout } from "../../layout/resolve.js";
4
+ import { resolveCandidatesLogPath, resolveTriageCachePath, TRIAGE_CACHE_DIR_NAME, triageCacheRelPath, } from "../cache-path.js";
4
5
  /** POSIX-style display path for `absPath` relative to `projectRoot` (#2109). */
5
6
  function evalRelDisplay(projectRoot, absPath) {
6
7
  return relative(projectRoot, absPath).split(/[\\/]/).join("/");
@@ -10,21 +11,41 @@ export const GITIGNORE_DEFT_RUNTIME_SENTINELS = [
10
11
  ".deft/ritual-state.json",
11
12
  ".deft/last-session.json",
12
13
  ];
14
+ /** Legacy static vbrief paths kept for tests referencing the pre-#1703 constant shape. */
13
15
  export const GITIGNORE_EVAL_ENTRIES = [
14
- "vbrief/.eval/candidates.jsonl",
15
- "vbrief/.eval/summary-history.jsonl",
16
- "vbrief/.eval/scope-lifecycle.jsonl",
17
- "vbrief/.eval/decompositions/",
18
- "vbrief/.eval/doctor-state.json",
16
+ "vbrief/.triage-cache/candidates.jsonl",
17
+ "vbrief/.triage-cache/summary-history.jsonl",
18
+ "vbrief/.triage-cache/scope-lifecycle.jsonl",
19
+ "vbrief/.triage-cache/decompositions/",
20
+ "vbrief/.triage-cache/doctor-state.json",
21
+ ];
22
+ /** Layout-aware gitignore lines for triage working-set files (#1703). */
23
+ export function gitignoreTriageCacheEntries(projectRoot) {
24
+ const decomp = triageCacheRelPath(projectRoot, "decompositions");
25
+ return [
26
+ triageCacheRelPath(projectRoot, "candidates.jsonl"),
27
+ triageCacheRelPath(projectRoot, "summary-history.jsonl"),
28
+ triageCacheRelPath(projectRoot, "scope-lifecycle.jsonl"),
29
+ decomp.endsWith("/") ? decomp : `${decomp}/`,
30
+ triageCacheRelPath(projectRoot, "doctor-state.json"),
31
+ ];
32
+ }
33
+ export function gitattributesTriageCacheGlob(projectRoot) {
34
+ const layout = resolveLifecycleLayout(projectRoot);
35
+ return `${layout.artifactDir}/${TRIAGE_CACHE_DIR_NAME}/*.jsonl`;
36
+ }
37
+ export const GITATTRIBUTES_EVAL_RULE = "vbrief/.triage-cache/*.jsonl merge=union";
38
+ export const FORBIDDEN_BLANKET_EVAL_LINES = [
39
+ "vbrief/.triage-cache/",
40
+ "vbrief/.triage-cache",
41
+ "vbrief/.triage-cache/",
42
+ "vbrief/.eval",
19
43
  ];
20
- export const GITATTRIBUTES_EVAL_RULE = "vbrief/.eval/*.jsonl merge=union";
21
- export const FORBIDDEN_BLANKET_EVAL_LINES = ["vbrief/.eval/", "vbrief/.eval"];
22
- const GITATTRIBUTES_EVAL_GLOB = "vbrief/.eval/*.jsonl";
23
44
  const DEFT_CACHE_RATIONALE = "\n# Triage v1 local content cache (#845, #883). Mirrors upstream\n" +
24
45
  "# issues into .deft-cache/github-issue/<owner>/<repo>/<N>/. See\n" +
25
46
  "# docs/privacy-nfr.md for the gitignore-default + opt-in-commit-cache\n" +
26
47
  "# contract. Comment this line out to opt in to committing the cache.\n";
27
- const EVAL_ENTRIES_RATIONALE = "\n# vbrief/.eval/ tracking governance (#1144, N4 of #1119).\n" +
48
+ const EVAL_ENTRIES_RATIONALE = "\n# vbrief/.triage-cache/ tracking governance (#1144, N4 of #1119).\n" +
28
49
  "# Hybrid policy from the Current Shape comment on #1144:\n" +
29
50
  "# - candidates.jsonl -> gitignored (operator-private triage\n" +
30
51
  "# decisions; re-derive via\n" +
@@ -50,15 +71,15 @@ const EVAL_ENTRIES_RATIONALE = "\n# vbrief/.eval/ tracking governance (#1144, N4
50
71
  "# - slices.jsonl -> TRACKED (team-shared cohort records\n" +
51
72
  "# produced by slicing skills; see\n" +
52
73
  "# #1132 / D13).\n" +
53
- "# See vbrief/.eval/README.md for the full policy + merge=union\n" +
74
+ "# See vbrief/.triage-cache/README.md for the full policy + merge=union\n" +
54
75
  "# rebase note.\n";
55
- const GITATTRIBUTES_EVAL_RATIONALE = "\n# Append-only JSON-lines logs under vbrief/.eval/ use the union merge driver\n" +
76
+ const GITATTRIBUTES_EVAL_RATIONALE = "\n# Append-only JSON-lines logs under vbrief/.triage-cache/ use the union merge driver\n" +
56
77
  "# (#1144, N4 of #1119). Both branches' appended lines are concatenated on\n" +
57
78
  "# auto-merge so single-operator rebases of two append branches resolve\n" +
58
79
  "# without manual conflict surgery. Note: merge=union does NOT dedupe; see\n" +
59
- "# vbrief/.eval/README.md for the operator-facing semantics.\n";
60
- const EVAL_ENTRIES_RATIONALE_SENTINEL = "# vbrief/.eval/ tracking governance (#1144, N4 of #1119).";
61
- export const EVAL_README_BODY = `# \`vbrief/.eval/\` -- triage + slicing evaluation artefacts
80
+ "# vbrief/.triage-cache/README.md for the operator-facing semantics.\n";
81
+ const EVAL_ENTRIES_RATIONALE_SENTINEL = "# vbrief/.triage-cache/ tracking governance (#1144, N4 of #1119).";
82
+ export const EVAL_README_BODY = `# \`vbrief/.triage-cache/\` -- triage working-set artefacts
62
83
 
63
84
  This directory holds the append-only JSON-lines logs that the triage and
64
85
  slicing skills emit. The framework governs which files in here are tracked
@@ -75,9 +96,9 @@ by git versus gitignored using a **hybrid policy** (#1144, child of #1119).
75
96
  | \`decompositions/\` | No -- **gitignored** | Temporary story-decomposition proposal drafts. These JSON drafts are local scratch artifacts, not vBRIEFs; generated child story vBRIEFs are created by \`task scope:decompose\` in lifecycle folders, defaulting to \`vbrief/pending/\`. |
76
97
  | \`doctor-state.json\` | No -- **gitignored** | Per-machine \`task doctor\` throttle state (last exit code + timestamps) persisted to gate the 24h/4h re-probe window (#1308 / #1464). Local to each clone; never committed. |
77
98
 
78
- The gitignore lines live in the repo-root \`.gitignore\` (\`vbrief/.eval/candidates.jsonl\`,
79
- \`vbrief/.eval/summary-history.jsonl\`, \`vbrief/.eval/scope-lifecycle.jsonl\`,
80
- \`vbrief/.eval/decompositions/\`, and \`vbrief/.eval/doctor-state.json\`). All paths
99
+ The gitignore lines live in the repo-root \`.gitignore\` (\`vbrief/.triage-cache/candidates.jsonl\`,
100
+ \`vbrief/.triage-cache/summary-history.jsonl\`, \`vbrief/.triage-cache/scope-lifecycle.jsonl\`,
101
+ \`vbrief/.triage-cache/decompositions/\`, and \`vbrief/.triage-cache/doctor-state.json\`). All paths
81
102
  not listed above remain committed by default.
82
103
 
83
104
  ## Fresh-clone regeneration
@@ -90,7 +111,7 @@ task triage:bootstrap
90
111
  \`\`\`
91
112
 
92
113
  The bootstrap path detects the missing file, runs the auto-classifier, and
93
- writes a fresh \`vbrief/.eval/candidates.jsonl\`. It does NOT touch the tracked
114
+ writes a fresh \`vbrief/.triage-cache/candidates.jsonl\`. It does NOT touch the tracked
94
115
  \`slices.jsonl\`; cohort records remain a team-shared resource.
95
116
 
96
117
  ## \`merge=union\` policy for \`*.jsonl\`
@@ -98,7 +119,7 @@ writes a fresh \`vbrief/.eval/candidates.jsonl\`. It does NOT touch the tracked
98
119
  The repo-root \`.gitattributes\` declares:
99
120
 
100
121
  \`\`\`
101
- vbrief/.eval/*.jsonl merge=union
122
+ vbrief/.triage-cache/*.jsonl merge=union
102
123
  \`\`\`
103
124
 
104
125
  The \`union\` merge driver concatenates both sides' appended lines on
@@ -217,10 +238,10 @@ export function stepEnsureGitignoreEntry(projectRoot) {
217
238
  function formatBlanketWarning(blanketPresent) {
218
239
  if (!blanketPresent)
219
240
  return "";
220
- return (" WARNING: stale blanket vbrief/.eval/ line detected in .gitignore -- " +
241
+ return (" WARNING: stale blanket vbrief/.triage-cache/ line detected in .gitignore -- " +
221
242
  "remove it manually (it hides tracked slices.jsonl from git per #1251)");
222
243
  }
223
- function gitattributesHasEvalMergeUnion(body) {
244
+ function gitattributesHasEvalMergeUnion(body, glob) {
224
245
  for (const raw of body.split("\n")) {
225
246
  const stripped = raw.trim();
226
247
  if (stripped.length === 0 || stripped.startsWith("#"))
@@ -228,14 +249,14 @@ function gitattributesHasEvalMergeUnion(body) {
228
249
  const parts = stripped.split(/\s+/);
229
250
  if (parts.length === 0)
230
251
  continue;
231
- if (parts[0] !== GITATTRIBUTES_EVAL_GLOB)
252
+ if (parts[0] !== glob)
232
253
  continue;
233
254
  if (parts.slice(1).includes("merge=union"))
234
255
  return true;
235
256
  }
236
257
  return false;
237
258
  }
238
- function ensureGitignoreSelectiveEntries(gitignorePath, stepName) {
259
+ function ensureGitignoreSelectiveEntries(gitignorePath, stepName, entries) {
239
260
  let existing;
240
261
  try {
241
262
  existing = readFileSync(gitignorePath, { encoding: "utf8" });
@@ -249,7 +270,7 @@ function ensureGitignoreSelectiveEntries(gitignorePath, stepName) {
249
270
  .filter((stripped) => stripped.length > 0));
250
271
  const blanketPresent = FORBIDDEN_BLANKET_EVAL_LINES.some((forbidden) => existingLines.has(forbidden));
251
272
  const rationaleAlreadyPresent = existing.includes(EVAL_ENTRIES_RATIONALE_SENTINEL);
252
- const missing = GITIGNORE_EVAL_ENTRIES.filter((entry) => !existingLines.has(entry));
273
+ const missing = entries.filter((entry) => !existingLines.has(entry));
253
274
  const blanketWarning = formatBlanketWarning(blanketPresent);
254
275
  if (missing.length === 0) {
255
276
  return stepOutcome(stepName, true, `all #1144 selective entries already in .gitignore (no-op)${blanketWarning}`, {
@@ -277,7 +298,7 @@ function ensureGitignoreSelectiveEntries(gitignorePath, stepName) {
277
298
  rationale_already_present: rationaleAlreadyPresent,
278
299
  });
279
300
  }
280
- function ensureGitattributesMergeUnion(gitattributesPath, stepName) {
301
+ function ensureGitattributesMergeUnion(gitattributesPath, stepName, glob, ruleLine) {
281
302
  if (existsSync(gitattributesPath)) {
282
303
  let existing;
283
304
  try {
@@ -286,30 +307,36 @@ function ensureGitattributesMergeUnion(gitattributesPath, stepName) {
286
307
  catch (exc) {
287
308
  return stepOutcome(stepName, false, "could not read .gitattributes", { gitattributes_appended: false }, String(exc));
288
309
  }
289
- if (gitattributesHasEvalMergeUnion(existing)) {
290
- return stepOutcome(stepName, true, "vbrief/.eval/*.jsonl merge=union already in .gitattributes (no-op)", {
310
+ if (gitattributesHasEvalMergeUnion(existing, glob)) {
311
+ return stepOutcome(stepName, true, `${glob} merge=union already in .gitattributes (no-op)`, {
291
312
  gitattributes_appended: false,
292
313
  gitattributes_already_present: true,
293
314
  });
294
315
  }
295
316
  const suffix = existing.endsWith("\n") || existing === "" ? "" : "\n";
296
- const newContent = `${existing + suffix + GITATTRIBUTES_EVAL_RATIONALE + GITATTRIBUTES_EVAL_RULE}\n`;
317
+ const newContent = `${existing + suffix + GITATTRIBUTES_EVAL_RATIONALE + ruleLine}\n`;
297
318
  try {
298
319
  writeFileSync(gitattributesPath, newContent, { encoding: "utf8" });
299
320
  }
300
321
  catch (exc) {
301
322
  return stepOutcome(stepName, false, "could not write .gitattributes", { gitattributes_appended: false }, String(exc));
302
323
  }
303
- return stepOutcome(stepName, true, "appended vbrief/.eval/*.jsonl merge=union to .gitattributes", { gitattributes_appended: true, gitattributes_created: false });
324
+ return stepOutcome(stepName, true, `appended ${glob} merge=union to .gitattributes`, {
325
+ gitattributes_appended: true,
326
+ gitattributes_created: false,
327
+ });
304
328
  }
305
- const newContent = `${GITATTRIBUTES_EVAL_RATIONALE + GITATTRIBUTES_EVAL_RULE}\n`;
329
+ const newContent = `${GITATTRIBUTES_EVAL_RATIONALE + ruleLine}\n`;
306
330
  try {
307
331
  writeFileSync(gitattributesPath, newContent, { encoding: "utf8" });
308
332
  }
309
333
  catch (exc) {
310
334
  return stepOutcome(stepName, false, "could not create .gitattributes", { gitattributes_appended: false }, String(exc));
311
335
  }
312
- return stepOutcome(stepName, true, "created .gitattributes with vbrief/.eval/*.jsonl merge=union", { gitattributes_appended: true, gitattributes_created: true });
336
+ return stepOutcome(stepName, true, `created .gitattributes with ${glob} merge=union`, {
337
+ gitattributes_appended: true,
338
+ gitattributes_created: true,
339
+ });
313
340
  }
314
341
  function ensureEvalReadme(readmePath, readmeRel, stepName) {
315
342
  try {
@@ -339,17 +366,20 @@ export function stepEnsureGitignoreEvalEntries(projectRoot) {
339
366
  const gitattributesPath = `${projectRoot}/.gitattributes`;
340
367
  // Layout-aware (#2109): resolve the README under the active lifecycle `.eval`
341
368
  // dir (xbrief/ when migrated, else vbrief/) instead of a hardcoded vbrief/ path.
342
- const readmePath = resolveEvalPath(projectRoot, "README.md");
369
+ const readmePath = resolveTriageCachePath(projectRoot, "README.md");
343
370
  const readmeRel = evalRelDisplay(projectRoot, readmePath);
371
+ const entries = gitignoreTriageCacheEntries(projectRoot);
372
+ const glob = gitattributesTriageCacheGlob(projectRoot);
373
+ const ruleLine = `${glob} merge=union`;
344
374
  const stepName = "ensure_gitignore_eval_entries";
345
375
  const details = {};
346
- const giResult = ensureGitignoreSelectiveEntries(gitignorePath, stepName);
376
+ const giResult = ensureGitignoreSelectiveEntries(gitignorePath, stepName, entries);
347
377
  if (!giResult.ok) {
348
378
  Object.assign(details, giResult.details);
349
379
  return stepOutcome(stepName, false, giResult.message, details, giResult.error ?? null);
350
380
  }
351
381
  Object.assign(details, giResult.details);
352
- const gaResult = ensureGitattributesMergeUnion(gitattributesPath, stepName);
382
+ const gaResult = ensureGitattributesMergeUnion(gitattributesPath, stepName, glob, ruleLine);
353
383
  if (!gaResult.ok) {
354
384
  Object.assign(details, gaResult.details);
355
385
  return stepOutcome(stepName, false, gaResult.message, details, gaResult.error ?? null);
@@ -386,11 +416,11 @@ export function stepEnsureGitignoreEvalEntries(projectRoot) {
386
416
  message += formatBlanketWarning(Boolean(details.blanket_present));
387
417
  return stepOutcome(stepName, true, message, details);
388
418
  }
389
- /** Ensure `vbrief/.eval/candidates.jsonl` exists (#1240 option A). */
419
+ /** Ensure `vbrief/.triage-cache/candidates.jsonl` exists (#1240 option A). */
390
420
  export function stepSeedCandidatesLog(projectRoot) {
391
421
  // Layout-aware (#2109): seed under the active lifecycle `.eval` dir (xbrief/
392
422
  // when migrated, else vbrief/) instead of a hardcoded vbrief/ path.
393
- const auditPath = resolveEvalPath(projectRoot, "candidates.jsonl");
423
+ const auditPath = resolveCandidatesLogPath(projectRoot);
394
424
  const auditRel = evalRelDisplay(projectRoot, auditPath);
395
425
  const auditDir = dirname(auditPath);
396
426
  try {
@@ -2,7 +2,7 @@ import type { BootstrapResult, CacheModule, RunBootstrapOptions, StepOutcome } f
2
2
  export * from "./gitignore.js";
3
3
  export * from "./types.js";
4
4
  export declare const CACHE_DIR_NAME = ".deft-cache";
5
- export declare const AUDIT_LOG_RELPATH = "vbrief/.eval/candidates.jsonl";
5
+ export declare const AUDIT_LOG_RELPATH = "vbrief/.triage-cache/candidates.jsonl";
6
6
  export declare const BACKFILL_FOLDERS: readonly ["proposed", "pending", "active"];
7
7
  export declare const BOOTSTRAP_ACTOR = "agent:bootstrap";
8
8
  export declare const DEFAULT_FETCH_TIMEOUT_S = 3600;
@@ -3,14 +3,15 @@ import { randomUUID } from "node:crypto";
3
3
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { promisify } from "node:util";
6
- import { hasArtifactSuffix, resolveEvalPath, resolveLifecycleRoot } from "../../layout/resolve.js";
6
+ import { hasArtifactSuffix, resolveLifecycleRoot } from "../../layout/resolve.js";
7
7
  import { SUBPROCESS_MAX_BUFFER } from "../../subprocess/max-buffer.js";
8
+ import { resolveCandidatesLogPath } from "../cache-path.js";
8
9
  import { stepEnsureGitignoreEntry, stepEnsureGitignoreEvalEntries, stepSeedCandidatesLog, } from "./gitignore.js";
9
10
  import { PROGRESS_DEFAULT } from "./types.js";
10
11
  export * from "./gitignore.js";
11
12
  export * from "./types.js";
12
13
  export const CACHE_DIR_NAME = ".deft-cache";
13
- export const AUDIT_LOG_RELPATH = "vbrief/.eval/candidates.jsonl";
14
+ export const AUDIT_LOG_RELPATH = "vbrief/.triage-cache/candidates.jsonl";
14
15
  export const BACKFILL_FOLDERS = ["proposed", "pending", "active"];
15
16
  export const BOOTSTRAP_ACTOR = "agent:bootstrap";
16
17
  export const DEFAULT_FETCH_TIMEOUT_S = 3600;
@@ -377,7 +378,7 @@ export function stepBackfillAuditLog(projectRoot, repo, options = {}) {
377
378
  if (!existsSync(vbriefRoot)) {
378
379
  return stepOutcome("backfill_audit_log", true, `skipped (no vbrief/ directory under ${projectRoot})`, { skipped: "no-vbrief" });
379
380
  }
380
- const auditPath = resolveEvalPath(projectRoot, "candidates.jsonl");
381
+ const auditPath = resolveCandidatesLogPath(projectRoot);
381
382
  const alreadyLogged = existingAuditIssueNumbers(auditPath);
382
383
  const nowIso = options.nowIso ?? nowIsoDefault;
383
384
  const appendEntry = options.appendAuditEntry ?? appendAuditEntryDefault;
@@ -88,12 +88,21 @@ export declare function excludeLogged(candidates: Iterable<IssuePayload>, option
88
88
  export declare function bulkAction(actionKey: string, repo: string, options?: BulkActionOptions): number;
89
89
  export declare function createFilesystemCacheModule(): CacheModule;
90
90
  export declare function createFilesystemCandidatesLogModule(logPath?: string): CandidatesLogModule;
91
+ /**
92
+ * Native TypeScript bulk actions module (#2279).
93
+ *
94
+ * The bulk path defaults to this factory instead of {@link createPythonActionsModule}
95
+ * so `triage:bulk-*` never spawns a project-local `scripts/triage_actions.py`
96
+ * (an arbitrary-code-execution trust boundary a malicious checkout could plant).
97
+ * Each verb delegates to the already-native per-issue triage actions
98
+ * (`@deftai/directive-core/triage/actions`, #1725) bound to `createDefaultDeps(projectRoot)`.
99
+ */
100
+ export declare function createNativeActionsModule(projectRoot: string): TriageActionsModule;
91
101
  export declare function createPythonActionsModule(scriptsDir: string): TriageActionsModule;
92
102
  export interface DefaultBulkDepsOptions {
93
103
  readonly cacheRoot?: string;
94
104
  readonly candidatesLogPath?: string;
95
- readonly scriptsDir?: string;
96
- readonly deftRoot?: string;
105
+ readonly projectRoot?: string;
97
106
  }
98
107
  export declare function bulkActionWithDefaults(actionKey: string, repo: string, options?: BulkActionOptions & DefaultBulkDepsOptions): number;
99
108
  //# sourceMappingURL=index.d.ts.map
@@ -1,7 +1,8 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
- import { resolveEvalPath } from "../../layout/resolve.js";
4
+ import { createDefaultDeps, accept as nativeAccept, deferAction as nativeDefer, needsAc as nativeNeedsAc, reject as nativeReject, } from "../actions/index.js";
5
+ import { resolveCandidatesLogPath } from "../cache-path.js";
5
6
  export const ACTION_FN_NAMES = {
6
7
  accept: "accept",
7
8
  reject: "reject",
@@ -376,7 +377,7 @@ export function createFilesystemCacheModule() {
376
377
  CacheError,
377
378
  };
378
379
  }
379
- export function createFilesystemCandidatesLogModule(logPath = resolveEvalPath(process.cwd(), "candidates.jsonl")) {
380
+ export function createFilesystemCandidatesLogModule(logPath = resolveCandidatesLogPath(process.cwd())) {
380
381
  return {
381
382
  readAll(options) {
382
383
  if (!existsSync(logPath)) {
@@ -402,6 +403,40 @@ export function createFilesystemCandidatesLogModule(logPath = resolveEvalPath(pr
402
403
  },
403
404
  };
404
405
  }
406
+ /**
407
+ * Native TypeScript bulk actions module (#2279).
408
+ *
409
+ * The bulk path defaults to this factory instead of {@link createPythonActionsModule}
410
+ * so `triage:bulk-*` never spawns a project-local `scripts/triage_actions.py`
411
+ * (an arbitrary-code-execution trust boundary a malicious checkout could plant).
412
+ * Each verb delegates to the already-native per-issue triage actions
413
+ * (`@deftai/directive-core/triage/actions`, #1725) bound to `createDefaultDeps(projectRoot)`.
414
+ */
415
+ export function createNativeActionsModule(projectRoot) {
416
+ const deps = createDefaultDeps(projectRoot);
417
+ return {
418
+ accept(n, repo) {
419
+ nativeAccept(n, repo, deps, { projectRoot });
420
+ },
421
+ reject(n, repo, ...args) {
422
+ let reason = "";
423
+ const first = args[0];
424
+ if (typeof first === "object" && first !== null && "reason" in first) {
425
+ reason = String(first.reason);
426
+ }
427
+ else if (typeof first === "string") {
428
+ reason = first;
429
+ }
430
+ nativeReject(n, repo, reason, deps, { projectRoot });
431
+ },
432
+ defer(n, repo) {
433
+ nativeDefer(n, repo, "bulk defer", deps, { projectRoot });
434
+ },
435
+ needs_ac(n, repo) {
436
+ nativeNeedsAc(n, repo, deps, { projectRoot });
437
+ },
438
+ };
439
+ }
405
440
  export function createPythonActionsModule(scriptsDir) {
406
441
  const runAction = (cmd, issueNumber, repo, extra = []) => {
407
442
  const result = spawnSync("uv", [
@@ -442,14 +477,16 @@ export function createPythonActionsModule(scriptsDir) {
442
477
  };
443
478
  }
444
479
  export function bulkActionWithDefaults(actionKey, repo, options = {}) {
445
- const deftRoot = options.deftRoot ?? process.cwd();
446
- const scriptsDir = options.scriptsDir ?? join(deftRoot, "scripts");
480
+ // #2279: the bulk path no longer resolves a `scripts/` dir from DEFT_ROOT /
481
+ // the project cwd. It defaults to the native TypeScript actions module so a
482
+ // malicious project-local `scripts/triage_actions.py` can never be executed.
483
+ const projectRoot = options.projectRoot ?? process.cwd();
447
484
  return bulkAction(actionKey, repo, {
448
485
  ...options,
449
- cacheRoot: options.cacheRoot ?? join(deftRoot, ".deft-cache"),
486
+ cacheRoot: options.cacheRoot ?? join(projectRoot, ".deft-cache"),
450
487
  cacheModule: options.cacheModule ?? createFilesystemCacheModule(),
451
488
  candidatesLogModule: options.candidatesLogModule ?? createFilesystemCandidatesLogModule(options.candidatesLogPath),
452
- actionsModule: options.actionsModule ?? createPythonActionsModule(scriptsDir),
489
+ actionsModule: options.actionsModule ?? createNativeActionsModule(projectRoot),
453
490
  });
454
491
  }
455
492
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Layout-aware triage working-set cache paths (#1703 namespace cleanup).
3
+ *
4
+ * Triage append-only logs and scratch dirs live under `.triage-cache/` so the
5
+ * `.eval/` namespace can be reclaimed for version-eval results (#1703 Tier 2).
6
+ */
7
+ /** Directory name for the triage working-set cache (not version-eval results). */
8
+ export declare const TRIAGE_CACHE_DIR_NAME = ".triage-cache";
9
+ /** Legacy directory that previously held triage cache files before #1703. */
10
+ export declare const LEGACY_TRIAGE_EVAL_DIR_NAME = ".eval";
11
+ /** Known triage-cache file basenames migrated off the legacy `.eval/` tree. */
12
+ export declare const TRIAGE_CACHE_FILE_NAMES: readonly ["candidates.jsonl", "summary-history.jsonl", "scope-lifecycle.jsonl", "subscription-history.jsonl", "slices.jsonl", "doctor-state.json", "README.md"];
13
+ /** Known triage-cache directory names migrated off the legacy `.eval/` tree. */
14
+ export declare const TRIAGE_CACHE_DIR_NAMES: readonly ["decompositions"];
15
+ export interface TriageCacheMigrationResult {
16
+ readonly migratedFiles: readonly string[];
17
+ readonly skippedFiles: readonly string[];
18
+ readonly migratedDirs: readonly string[];
19
+ }
20
+ /** Absolute path to the layout-aware `.triage-cache/` directory. */
21
+ export declare function resolveTriageCacheDir(projectRoot: string): string;
22
+ /** POSIX-style path relative to project root (e.g. `xbrief/.triage-cache/foo`). */
23
+ export declare function triageCacheRelPath(projectRoot: string, ...segments: string[]): string;
24
+ /**
25
+ * Idempotently move triage working-set artefacts from legacy `.eval/` into
26
+ * `.triage-cache/` when the new location is absent.
27
+ */
28
+ export declare function migrateLegacyTriageCacheFromEval(projectRoot: string): TriageCacheMigrationResult;
29
+ /** Resolve a path under `.triage-cache/`, migrating legacy `.eval/` files first. */
30
+ export declare function resolveTriageCachePath(projectRoot: string, ...segments: string[]): string;
31
+ /** Display helper: project-root-relative POSIX path for logs and gitignore copy. */
32
+ export declare function triageCacheDisplayPath(projectRoot: string, absPath: string): string;
33
+ /** Back-compat display constant; resolution flows through `resolveTriageCachePath`. */
34
+ export declare const TRIAGE_CANDIDATES_LOG_REL_PATH = "vbrief/.triage-cache/candidates.jsonl";
35
+ /** Layout-aware candidates audit-log path. */
36
+ export declare function resolveCandidatesLogPath(projectRoot: string): string;
37
+ /** Ensure the triage cache directory exists (post-migration). */
38
+ export declare function ensureTriageCacheDir(projectRoot: string): string;
39
+ //# sourceMappingURL=cache-path.d.ts.map
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Layout-aware triage working-set cache paths (#1703 namespace cleanup).
3
+ *
4
+ * Triage append-only logs and scratch dirs live under `.triage-cache/` so the
5
+ * `.eval/` namespace can be reclaimed for version-eval results (#1703 Tier 2).
6
+ */
7
+ import { existsSync, mkdirSync, renameSync } from "node:fs";
8
+ import { join, relative } from "node:path";
9
+ import { resolveEvalDir, resolveLifecycleLayout, resolveLifecycleRoot } from "../layout/resolve.js";
10
+ /** Directory name for the triage working-set cache (not version-eval results). */
11
+ export const TRIAGE_CACHE_DIR_NAME = ".triage-cache";
12
+ /** Legacy directory that previously held triage cache files before #1703. */
13
+ export const LEGACY_TRIAGE_EVAL_DIR_NAME = ".eval";
14
+ /** Known triage-cache file basenames migrated off the legacy `.eval/` tree. */
15
+ export const TRIAGE_CACHE_FILE_NAMES = [
16
+ "candidates.jsonl",
17
+ "summary-history.jsonl",
18
+ "scope-lifecycle.jsonl",
19
+ "subscription-history.jsonl",
20
+ "slices.jsonl",
21
+ "doctor-state.json",
22
+ "README.md",
23
+ ];
24
+ /** Known triage-cache directory names migrated off the legacy `.eval/` tree. */
25
+ export const TRIAGE_CACHE_DIR_NAMES = ["decompositions"];
26
+ /** Absolute path to the layout-aware `.triage-cache/` directory. */
27
+ export function resolveTriageCacheDir(projectRoot) {
28
+ return join(resolveLifecycleRoot(projectRoot), TRIAGE_CACHE_DIR_NAME);
29
+ }
30
+ /** POSIX-style path relative to project root (e.g. `xbrief/.triage-cache/foo`). */
31
+ export function triageCacheRelPath(projectRoot, ...segments) {
32
+ const layout = resolveLifecycleLayout(projectRoot);
33
+ return [layout.artifactDir, TRIAGE_CACHE_DIR_NAME, ...segments].join("/");
34
+ }
35
+ /**
36
+ * Idempotently move triage working-set artefacts from legacy `.eval/` into
37
+ * `.triage-cache/` when the new location is absent.
38
+ */
39
+ export function migrateLegacyTriageCacheFromEval(projectRoot) {
40
+ const legacyDir = resolveEvalDir(projectRoot);
41
+ const targetDir = resolveTriageCacheDir(projectRoot);
42
+ const migratedFiles = [];
43
+ const skippedFiles = [];
44
+ const migratedDirs = [];
45
+ if (!existsSync(legacyDir)) {
46
+ return { migratedFiles, skippedFiles, migratedDirs };
47
+ }
48
+ mkdirSync(targetDir, { recursive: true });
49
+ for (const name of TRIAGE_CACHE_FILE_NAMES) {
50
+ const legacyPath = join(legacyDir, name);
51
+ const targetPath = join(targetDir, name);
52
+ if (!existsSync(legacyPath)) {
53
+ continue;
54
+ }
55
+ if (existsSync(targetPath)) {
56
+ skippedFiles.push(name);
57
+ continue;
58
+ }
59
+ renameSync(legacyPath, targetPath);
60
+ migratedFiles.push(name);
61
+ }
62
+ for (const name of TRIAGE_CACHE_DIR_NAMES) {
63
+ const legacyPath = join(legacyDir, name);
64
+ const targetPath = join(targetDir, name);
65
+ if (!existsSync(legacyPath)) {
66
+ continue;
67
+ }
68
+ if (existsSync(targetPath)) {
69
+ skippedFiles.push(`${name}/`);
70
+ continue;
71
+ }
72
+ renameSync(legacyPath, targetPath);
73
+ migratedDirs.push(name);
74
+ }
75
+ return { migratedFiles, skippedFiles, migratedDirs };
76
+ }
77
+ /** Resolve a path under `.triage-cache/`, migrating legacy `.eval/` files first. */
78
+ export function resolveTriageCachePath(projectRoot, ...segments) {
79
+ migrateLegacyTriageCacheFromEval(projectRoot);
80
+ return join(resolveTriageCacheDir(projectRoot), ...segments);
81
+ }
82
+ /** Display helper: project-root-relative POSIX path for logs and gitignore copy. */
83
+ export function triageCacheDisplayPath(projectRoot, absPath) {
84
+ return relative(projectRoot, absPath).split(/[\\/]/).join("/");
85
+ }
86
+ /** Back-compat display constant; resolution flows through `resolveTriageCachePath`. */
87
+ export const TRIAGE_CANDIDATES_LOG_REL_PATH = "vbrief/.triage-cache/candidates.jsonl";
88
+ /** Layout-aware candidates audit-log path. */
89
+ export function resolveCandidatesLogPath(projectRoot) {
90
+ return resolveTriageCachePath(projectRoot, "candidates.jsonl");
91
+ }
92
+ /** Ensure the triage cache directory exists (post-migration). */
93
+ export function ensureTriageCacheDir(projectRoot) {
94
+ const dir = resolveTriageCacheDir(projectRoot);
95
+ mkdirSync(dir, { recursive: true });
96
+ return dir;
97
+ }
98
+ //# sourceMappingURL=cache-path.js.map