@deftai/directive-core 0.96.0 → 0.97.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 (73) hide show
  1. package/dist/cache/archive.js +10 -4
  2. package/dist/check/gate-lists.js +8 -0
  3. package/dist/consumer-check-contract/evaluate.d.ts +124 -0
  4. package/dist/consumer-check-contract/evaluate.js +699 -0
  5. package/dist/consumer-check-contract/index.d.ts +5 -0
  6. package/dist/consumer-check-contract/index.js +5 -0
  7. package/dist/delivery-attempt/disk-begin.d.ts +51 -0
  8. package/dist/delivery-attempt/disk-begin.js +68 -0
  9. package/dist/delivery-attempt/evaluate.d.ts +26 -0
  10. package/dist/delivery-attempt/evaluate.js +443 -0
  11. package/dist/delivery-attempt/fingerprint.d.ts +32 -0
  12. package/dist/delivery-attempt/fingerprint.js +100 -0
  13. package/dist/delivery-attempt/handoff.d.ts +25 -0
  14. package/dist/delivery-attempt/handoff.js +102 -0
  15. package/dist/delivery-attempt/index.d.ts +17 -0
  16. package/dist/delivery-attempt/index.js +17 -0
  17. package/dist/delivery-attempt/ledger.d.ts +169 -0
  18. package/dist/delivery-attempt/ledger.js +758 -0
  19. package/dist/delivery-attempt/material-delta.d.ts +38 -0
  20. package/dist/delivery-attempt/material-delta.js +126 -0
  21. package/dist/delivery-attempt/types.d.ts +210 -0
  22. package/dist/delivery-attempt/types.js +77 -0
  23. package/dist/doctor/index.d.ts +1 -0
  24. package/dist/doctor/index.js +1 -0
  25. package/dist/doctor/main.js +12 -0
  26. package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
  27. package/dist/doctor/openclaw-soft-rebind.js +164 -0
  28. package/dist/hooks/dispatcher.d.ts +2 -1
  29. package/dist/hooks/dispatcher.js +63 -9
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.js +4 -0
  32. package/dist/init-deposit/gitignore.js +7 -0
  33. package/dist/init-deposit/init-deposit.js +5 -0
  34. package/dist/init-deposit/refresh.js +3 -0
  35. package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
  36. package/dist/pr-merge-readiness/ci-gate.js +191 -24
  37. package/dist/pr-merge-readiness/compute.js +10 -1
  38. package/dist/pr-merge-readiness/index.d.ts +2 -1
  39. package/dist/pr-merge-readiness/index.js +2 -1
  40. package/dist/pr-merge-readiness/output.js +14 -0
  41. package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
  42. package/dist/pr-merge-readiness/platform-status.js +49 -0
  43. package/dist/pr-watch/constants.d.ts +10 -0
  44. package/dist/pr-watch/constants.js +12 -1
  45. package/dist/pr-watch/main.js +16 -1
  46. package/dist/pr-watch/probe.js +13 -1
  47. package/dist/pr-watch/types.d.ts +3 -2
  48. package/dist/pr-watch/watch.js +14 -7
  49. package/dist/scope-provenance/digest.d.ts +67 -0
  50. package/dist/scope-provenance/digest.js +188 -0
  51. package/dist/scope-provenance/evaluate.d.ts +82 -0
  52. package/dist/scope-provenance/evaluate.js +528 -0
  53. package/dist/scope-provenance/index.d.ts +6 -0
  54. package/dist/scope-provenance/index.js +6 -0
  55. package/dist/session/compact-ritual.d.ts +96 -0
  56. package/dist/session/compact-ritual.js +237 -0
  57. package/dist/session/compact-ritual.spec.d.ts +2 -0
  58. package/dist/session/compact-ritual.spec.js +21 -0
  59. package/dist/session/index.d.ts +2 -0
  60. package/dist/session/index.js +2 -0
  61. package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
  62. package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
  63. package/dist/test-boundary/evaluate.d.ts +54 -0
  64. package/dist/test-boundary/evaluate.js +368 -0
  65. package/dist/test-boundary/index.d.ts +6 -0
  66. package/dist/test-boundary/index.js +6 -0
  67. package/dist/test-boundary/policy.d.ts +52 -0
  68. package/dist/test-boundary/policy.js +182 -0
  69. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  70. package/dist/triage/bootstrap/gitignore.js +15 -1
  71. package/dist/vbrief-activate/activate.js +22 -6
  72. package/dist/xbrief/styles.js +33 -17
  73. package/package.json +7 -3
@@ -20,6 +20,9 @@ export const GITIGNORE_EVAL_ENTRIES = [
20
20
  "xbrief/.triage-cache/scope-lifecycle.jsonl",
21
21
  "xbrief/.triage-cache/decompositions/",
22
22
  "xbrief/.triage-cache/doctor-state.json",
23
+ // Per-clone session state (#3146); selective only — hybrid policy preserved.
24
+ "xbrief/.triage-cache/staleness-tickler-state.json",
25
+ "xbrief/.triage-cache/release-availability-state.json",
23
26
  ];
24
27
  /** Layout-aware gitignore lines for triage working-set files (#1703). */
25
28
  export function gitignoreTriageCacheEntries(projectRoot) {
@@ -30,6 +33,9 @@ export function gitignoreTriageCacheEntries(projectRoot) {
30
33
  triageCacheRelPath(projectRoot, "scope-lifecycle.jsonl"),
31
34
  decomp.endsWith("/") ? decomp : `${decomp}/`,
32
35
  triageCacheRelPath(projectRoot, "doctor-state.json"),
36
+ // Per-clone session state (#3146); selective only — hybrid policy preserved.
37
+ triageCacheRelPath(projectRoot, "staleness-tickler-state.json"),
38
+ triageCacheRelPath(projectRoot, "release-availability-state.json"),
33
39
  ];
34
40
  }
35
41
  export function gitattributesTriageCacheGlob(projectRoot) {
@@ -76,6 +82,10 @@ const EVAL_ENTRIES_RATIONALE = "\n# vbrief/.triage-cache/ tracking governance (#
76
82
  "# throttle state gating the 24h/4h\n" +
77
83
  "# re-probe window; #1308 / #1464). Local\n" +
78
84
  "# to each clone; never committed.\n" +
85
+ "# - staleness-tickler-state.json -> gitignored (per-clone upgrade\n" +
86
+ "# tickler throttle; #2488 / #3146).\n" +
87
+ "# - release-availability-state.json -> gitignored (per-clone npm\n" +
88
+ "# release-availability throttle; #1692 / #3146).\n" +
79
89
  "# - slices.jsonl -> TRACKED (team-shared cohort records\n" +
80
90
  "# produced by slicing skills; see\n" +
81
91
  "# #1132 / D13).\n" +
@@ -104,12 +114,16 @@ committed.
104
114
  | \`scope-lifecycle.jsonl\` | No | Local audit trail for scope demotions (\`deft scope:demote\`). Each operator's stream stays on their machine. |
105
115
  | \`decompositions/\` | No | Draft story-decomposition scratch. Produced child story xBRIEFs live in lifecycle folders via \`deft scope:decompose\`. |
106
116
  | \`doctor-state.json\` | No | Per-clone throttle state for \`deft doctor\` re-probe timing. |
117
+ | \`staleness-tickler-state.json\` | No | Per-clone upgrade-tickler throttle state. |
118
+ | \`release-availability-state.json\` | No | Per-clone release-availability probe throttle state. |
107
119
 
108
120
  Paths listed as "No" above are added to \`.gitignore\` during bootstrap; anything
109
121
  not listed remains committable by default. The selective ignore entries live in
110
122
  the repo-root \`.gitignore\` (\`vbrief/.triage-cache/candidates.jsonl\`,
111
123
  \`vbrief/.triage-cache/summary-history.jsonl\`, \`vbrief/.triage-cache/scope-lifecycle.jsonl\`,
112
- \`vbrief/.triage-cache/decompositions/\`, and \`vbrief/.triage-cache/doctor-state.json\`).
124
+ \`vbrief/.triage-cache/decompositions/\`, \`vbrief/.triage-cache/doctor-state.json\`,
125
+ \`vbrief/.triage-cache/staleness-tickler-state.json\`, and
126
+ \`vbrief/.triage-cache/release-availability-state.json\`).
113
127
 
114
128
  ## Fresh clone
115
129
 
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, } from "node:fs";
2
2
  import { basename, dirname, resolve } from "node:path";
3
3
  import { containedWrite } from "../fs/contained-write.js";
4
+ import { assertProjectionContained, ProjectionContainmentError, } from "../fs/projection-containment.js";
4
5
  import { utcNowIso } from "../scope/vbrief-json.js";
5
6
  import { pythonJsonPretty } from "../vbrief-build/json.js";
6
7
  import { ACTIVE_FOLDER, ELIGIBLE_STATUSES_FOR_FLIP, formatEligibleStatusList, SOURCE_FOLDERS, TARGET_STATUS, } from "./constants.js";
@@ -120,6 +121,22 @@ export function activate(vbriefPath, options = {}) {
120
121
  `'${TARGET_STATUS}'.`,
121
122
  };
122
123
  }
124
+ // Resolve destination early so containment can refuse before any mutation.
125
+ // Parity with scope:activate / #2447: projectRoot is parent of the xbrief/ root.
126
+ const vbriefDir = dirname(dirname(vbriefPath));
127
+ const projectRoot = dirname(vbriefDir);
128
+ const activeDir = `${vbriefDir}/${ACTIVE_FOLDER}`;
129
+ try {
130
+ // #3147 / #2447: refuse when active/ (or a parent) is a symlink escaping the checkout.
131
+ // Run before mutating the source payload or unlinking pending so a refusal leaves state intact.
132
+ assertProjectionContained(projectRoot, activeDir);
133
+ }
134
+ catch (err) {
135
+ if (err instanceof ProjectionContainmentError) {
136
+ return { exitCode: 1, message: err.message };
137
+ }
138
+ throw err;
139
+ }
123
140
  planObj.status = TARGET_STATUS;
124
141
  let info = payload.vBRIEFInfo;
125
142
  if (info === undefined) {
@@ -133,8 +150,6 @@ export function activate(vbriefPath, options = {}) {
133
150
  };
134
151
  }
135
152
  info.updated = utcNowIso(now);
136
- const vbriefDir = dirname(dirname(vbriefPath));
137
- const activeDir = `${vbriefDir}/${ACTIVE_FOLDER}`;
138
153
  try {
139
154
  mkdirSync(activeDir, { recursive: true });
140
155
  }
@@ -151,13 +166,14 @@ export function activate(vbriefPath, options = {}) {
151
166
  "collision manually before re-running `task vbrief:activate`.",
152
167
  };
153
168
  }
154
- const tmpBase = `${fileName}.tmp`;
155
169
  const tmp = `${dest}.tmp`;
156
170
  try {
157
- // #2980 wave D: product write sink routes through containedWrite.
171
+ // #2980 wave D + #3147: product write sink routes through containedWrite with
172
+ // checkout root as containment root (not resolve(activeDir), which treats an
173
+ // escaping active/ realpath as "contained" relative to itself).
158
174
  containedWrite({
159
- root: resolve(activeDir),
160
- target: tmpBase,
175
+ root: resolve(projectRoot),
176
+ target: resolve(tmp),
161
177
  data: pythonJsonPretty(payload),
162
178
  mode: "create",
163
179
  });
@@ -4,6 +4,19 @@
4
4
  * One schema spine (xBRIEF v0.8); markdown is a dense render of the same plan,
5
5
  * not a second dialect.
6
6
  */
7
+ import { parseMarkdownHeading } from "../text/redos-safe.js";
8
+ /**
9
+ * Linear frontmatter ``key: value`` parse (no nested quantifiers).
10
+ * Replaces ``/^key:\s*(.+)\s*$/`` flagged as CodeQL js/polynomial-redos (#3174).
11
+ */
12
+ function frontmatterField(line, key) {
13
+ const prefix = `${key}:`;
14
+ if (!line.startsWith(prefix)) {
15
+ return null;
16
+ }
17
+ const value = line.slice(prefix.length).trim();
18
+ return value.length > 0 ? value : null;
19
+ }
7
20
  /** Required H2 section titles in the markdown form per style. */
8
21
  export const MD_REQUIRED_SECTIONS = {
9
22
  scope: ["Title", "Status", "Overview", "Items"],
@@ -200,25 +213,28 @@ export function parseMarkdownMeta(md) {
200
213
  continue;
201
214
  }
202
215
  if (inFront) {
203
- const idMatch = /^id:\s*(.+)\s*$/.exec(line);
204
- if (idMatch?.[1] !== undefined)
205
- id = idMatch[1].trim();
206
- const styleMatch = /^style:\s*(.+)\s*$/.exec(line);
207
- if (styleMatch?.[1] !== undefined)
208
- style = styleMatch[1].trim();
216
+ // Linear field parse — avoids js/polynomial-redos on \s*(.+)\s* (#3174 alerts #84-#85).
217
+ const idValue = frontmatterField(line, "id");
218
+ if (idValue !== null)
219
+ id = idValue;
220
+ const styleValue = frontmatterField(line, "style");
221
+ if (styleValue !== null)
222
+ style = styleValue;
209
223
  continue;
210
224
  }
211
- const h1 = /^#\s+(.+)$/.exec(line);
212
- if (h1?.[1] !== undefined && title === null) {
213
- title = h1[1].trim();
214
- }
215
- const h2 = /^##\s+(.+)$/.exec(line);
216
- if (h2?.[1] !== undefined) {
217
- const name = h2[1].trim();
218
- sections.add(name);
219
- afterTitleH2 = name === "Title";
220
- afterStatusH2 = name === "Status";
221
- continue;
225
+ // Linear ATX heading parse — avoids js/polynomial-redos on ^#+\s+(.+)$ (#3174 alerts #86-#87).
226
+ const heading = parseMarkdownHeading(line);
227
+ if (heading !== null) {
228
+ const name = heading.text.trim();
229
+ if (heading.hashes === "#" && title === null) {
230
+ title = name;
231
+ }
232
+ else if (heading.hashes === "##") {
233
+ sections.add(name);
234
+ afterTitleH2 = name === "Title";
235
+ afterStatusH2 = name === "Status";
236
+ continue;
237
+ }
222
238
  }
223
239
  if (afterTitleH2 && line.trim().length > 0 && !line.startsWith("#")) {
224
240
  // Prefer ## Title body over H1 when present.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive-core",
3
- "version": "0.96.0",
3
+ "version": "0.97.0",
4
4
  "description": "TypeScript engine core for the Directive framework.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -251,6 +251,10 @@
251
251
  "types": "./dist/parent-turn-shape/index.d.ts",
252
252
  "default": "./dist/parent-turn-shape/index.js"
253
253
  },
254
+ "./delivery-attempt": {
255
+ "types": "./dist/delivery-attempt/index.d.ts",
256
+ "default": "./dist/delivery-attempt/index.js"
257
+ },
254
258
  "./intake": {
255
259
  "types": "./dist/intake/index.d.ts",
256
260
  "default": "./dist/intake/index.js"
@@ -350,8 +354,8 @@
350
354
  "provenance": true
351
355
  },
352
356
  "dependencies": {
353
- "@deftai/directive-content": "^0.96.0",
354
- "@deftai/directive-types": "^0.96.0",
357
+ "@deftai/directive-content": "^0.97.0",
358
+ "@deftai/directive-types": "^0.97.0",
355
359
  "archiver": "^8.0.0"
356
360
  },
357
361
  "scripts": {