@oevery/rsp 3.3.0 → 4.1.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 (76) hide show
  1. package/README.md +24 -17
  2. package/README.zh-CN.md +25 -18
  3. package/dist/chunk-JO6NDJM4.mjs +360 -0
  4. package/dist/chunk-LGDCIQYL.mjs +725 -0
  5. package/dist/{chunk-7HXQNBIG.mjs → chunk-P3KO3GZW.mjs} +420 -268
  6. package/dist/chunk-U56XMIOO.mjs +66 -0
  7. package/dist/{chunk-TSUDEX5Y.mjs → chunk-VDRXFKBP.mjs} +142 -867
  8. package/dist/cli.mjs +1111 -2347
  9. package/dist/client-EHRWH5Q7.mjs +108 -0
  10. package/dist/{entry-GIDNR7CJ.mjs → entry-NDASTAI5.mjs} +128 -38
  11. package/dist/viewer/NOTICE.txt +299 -0
  12. package/dist/viewer/app.css +2 -0
  13. package/dist/viewer/app.js +670 -0
  14. package/dist/viewer-build.json +1 -0
  15. package/dist/viewer-service.mjs +551 -0
  16. package/package.json +51 -25
  17. package/skills/rsp/SKILL.md +44 -38
  18. package/skills/rsp/references/architecture.md +39 -0
  19. package/skills/rsp/references/closeout.md +38 -0
  20. package/skills/rsp/references/context-migration.md +22 -0
  21. package/skills/rsp/references/control-outcome.md +3 -29
  22. package/skills/rsp/references/coordination-recovery.md +25 -0
  23. package/skills/rsp/references/coordination-review.md +15 -0
  24. package/skills/rsp/references/coordination.md +59 -0
  25. package/skills/rsp/references/durable-review.md +60 -12
  26. package/skills/rsp/references/focus-continuation.md +12 -4
  27. package/skills/rsp/references/groups-dependencies.md +6 -4
  28. package/skills/rsp/references/response-language.md +2 -0
  29. package/skills/rsp/references/setup-repair.md +42 -7
  30. package/skills/rsp/references/work-model.md +37 -0
  31. package/skills/rsp-commit/SKILL.md +76 -31
  32. package/skills/rsp-commit/references/message.md +34 -0
  33. package/skills/rsp-doc/SKILL.md +68 -0
  34. package/skills/rsp-doc/agents/openai.yaml +3 -0
  35. package/skills/rsp-doc/references/release-communication.md +39 -0
  36. package/skills/rsp-doc/references/skill-authoring.md +36 -0
  37. package/skills/rsp-implement/SKILL.md +60 -25
  38. package/skills/rsp-implement/references/diagnosis.md +10 -0
  39. package/skills/rsp-implement/references/finding-resolution.md +21 -0
  40. package/skills/rsp-implement/references/test-first.md +7 -0
  41. package/skills/rsp-review/SKILL.md +46 -45
  42. package/skills/rsp-review/references/code-review.md +30 -9
  43. package/skills/rsp-review/references/document-review.md +24 -9
  44. package/skills/rsp-review/references/release-communication.md +21 -0
  45. package/skills/rsp-shape/SKILL.md +26 -45
  46. package/skills/rsp-shape/references/complex-shaping.md +3 -1
  47. package/skills/rsp-shape/references/deep-clarification.md +4 -4
  48. package/skills/rsp-shape/references/domain-modeling.md +13 -0
  49. package/skills/rsp-shape/references/module-seams.md +13 -0
  50. package/skills/rsp-shape/references/planning.md +42 -0
  51. package/skills/{rsp-design → rsp-shape}/references/reversible-exploration.md +2 -2
  52. package/skills/rsp-verify/SKILL.md +39 -12
  53. package/dist/chunk-JOMFFQRG.mjs +0 -99
  54. package/dist/chunk-QM2UVPQK.mjs +0 -375
  55. package/dist/entry-YAW3PYFA.mjs +0 -174
  56. package/rules/rsp-rules.md +0 -48
  57. package/skills/rsp/references/implementation-evidence.md +0 -12
  58. package/skills/rsp/references/managed-routing.md +0 -45
  59. package/skills/rsp-design/SKILL.md +0 -53
  60. package/skills/rsp-design/references/domain-modeling.md +0 -11
  61. package/skills/rsp-design/references/module-seams.md +0 -11
  62. package/skills/rsp-diagnose/SKILL.md +0 -48
  63. package/skills/rsp-manage/SKILL.md +0 -101
  64. package/skills/rsp-manage/references/closeout.md +0 -13
  65. package/skills/rsp-manage/references/delegation.md +0 -47
  66. package/skills/rsp-manage/references/interruption-recovery.md +0 -19
  67. package/skills/rsp-manage/references/review-convergence.md +0 -9
  68. package/skills/rsp-release-docs/SKILL.md +0 -77
  69. package/skills/rsp-release-docs/references/convention-discovery.md +0 -65
  70. package/skills/rsp-release-docs/references/evidence-and-surfaces.md +0 -21
  71. package/skills/rsp-release-docs/references/output-contracts.md +0 -97
  72. package/skills/rsp-release-docs/references/publication-lifecycle.md +0 -28
  73. package/skills/rsp-resolve-findings/SKILL.md +0 -61
  74. package/skills/rsp-structural-audit/SKILL.md +0 -71
  75. package/skills/rsp-structural-audit/references/structural-lenses.md +0 -41
  76. package/skills/rsp-tdd/SKILL.md +0 -56
@@ -5,10 +5,191 @@ import { readFile } from "fs/promises";
5
5
  import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import pc from "picocolors";
8
- import { parseDocument, stringify, visit } from "yaml";
8
+ import { stringify } from "yaml";
9
9
 
10
- // src/core/content.ts
11
- import { parse } from "yaml";
10
+ // src/core/markdown.mjs
11
+ import { fromHtml } from "hast-util-from-html";
12
+ import { fromMarkdown } from "mdast-util-from-markdown";
13
+ import { frontmatterFromMarkdown } from "mdast-util-frontmatter";
14
+ import { gfmFromMarkdown } from "mdast-util-gfm";
15
+ import { frontmatter } from "micromark-extension-frontmatter";
16
+ import { gfm } from "micromark-extension-gfm";
17
+ var htmlTokenAnalyses = /* @__PURE__ */ new WeakMap();
18
+ var matters = ["yaml", { type: "yaml", fence: { open: "---", close: "..." } }];
19
+ var extensions = [gfm(), frontmatter(matters)];
20
+ var mdastExtensions = [gfmFromMarkdown(), frontmatterFromMarkdown(matters)];
21
+ function parseMarkdown(input, options = {}) {
22
+ if (typeof input !== "string")
23
+ return input;
24
+ const syntax = options.frontmatter === false ? { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] } : { extensions, mdastExtensions };
25
+ return { source: input, root: fromMarkdown(input, syntax), start: 0, end: input.length };
26
+ }
27
+ function walkMarkdown(node, visit) {
28
+ if (visit(node) === false)
29
+ return;
30
+ if ("children" in node) {
31
+ for (const child of node.children)
32
+ walkMarkdown(child, visit);
33
+ }
34
+ }
35
+ function markdownNodeSource(input, node) {
36
+ const { source } = parseMarkdown(input);
37
+ return source.slice(node.position?.start.offset ?? 0, node.position?.end.offset ?? 0);
38
+ }
39
+ function isMarkdownComment(node) {
40
+ return node.type === "html" && !readHtmlToken(node, node.value).maskedSource.trim();
41
+ }
42
+ function htmlComments(value) {
43
+ if (!value.includes("<!--"))
44
+ return [];
45
+ const comments = [];
46
+ function visit(node, topLevel) {
47
+ if (node.type === "comment" && node.position)
48
+ comments.push({ start: node.position.start.offset ?? 0, end: node.position.end.offset ?? value.length, topLevel });
49
+ if ("children" in node)
50
+ node.children.forEach((child) => visit(child, node.type === "root"));
51
+ }
52
+ visit(fromHtml(value, { fragment: true }), false);
53
+ return comments;
54
+ }
55
+ function readHtmlToken(node, value) {
56
+ let analyses = htmlTokenAnalyses.get(node);
57
+ const cached = analyses?.get(value);
58
+ if (cached)
59
+ return cached;
60
+ const comments = htmlComments(value);
61
+ const analysis = {
62
+ comments,
63
+ maskedSource: maskSourceRanges(value, comments.map((comment) => [comment.start, comment.end]), 0, value.length)
64
+ };
65
+ if (!analyses) {
66
+ analyses = /* @__PURE__ */ new Map();
67
+ htmlTokenAnalyses.set(node, analyses);
68
+ }
69
+ analyses.set(value, analysis);
70
+ return analysis;
71
+ }
72
+ function markdownTopLevelComments(input) {
73
+ const document = parseMarkdown(input);
74
+ return document.root.children.flatMap((node) => {
75
+ if (node.type !== "html")
76
+ return [];
77
+ const source = markdownNodeSource(document, node);
78
+ const offset = node.position?.start.offset ?? document.start;
79
+ return readHtmlToken(node, source).comments.filter((comment) => comment.topLevel).map((comment) => ({
80
+ start: offset + comment.start,
81
+ end: offset + comment.end,
82
+ source: source.slice(comment.start, comment.end)
83
+ }));
84
+ });
85
+ }
86
+ function maskSourceRanges(source, ranges, start, end) {
87
+ let result = "";
88
+ let offset = start;
89
+ for (const [from, until] of ranges.sort((a, b) => a[0] - b[0])) {
90
+ result += source.slice(offset, from);
91
+ result += source.slice(from, until).replace(/[^\r\n]/g, " ");
92
+ offset = until;
93
+ }
94
+ return result + source.slice(offset, end);
95
+ }
96
+ function markdownText(node) {
97
+ if (node.type === "yaml")
98
+ return "";
99
+ if (node.type === "html")
100
+ return readHtmlToken(node, node.value).maskedSource;
101
+ if (node.type === "image" || node.type === "imageReference")
102
+ return node.alt ?? "";
103
+ if ("value" in node)
104
+ return node.value;
105
+ if ("children" in node) {
106
+ const separator = ["root", "list", "listItem", "blockquote"].includes(node.type) ? "\n" : "";
107
+ return node.children.map((child) => markdownText(child)).filter(Boolean).join(separator);
108
+ }
109
+ return node.type === "break" ? "\n" : "";
110
+ }
111
+ function markdownSection(document, nodes, start, end) {
112
+ return { source: document.source, root: { type: "root", children: nodes }, start, end };
113
+ }
114
+ function visibleMarkdownSource(input, options = {}) {
115
+ const document = parseMarkdown(input);
116
+ const visibleNodes = /* @__PURE__ */ new Set();
117
+ function markVisibleContent(node) {
118
+ if (node.type === "yaml" || node.type === "code" && !options.includeCode)
119
+ return false;
120
+ let visible = false;
121
+ if (node.type === "image" || node.type === "imageReference" || node.type === "footnoteReference")
122
+ visible = true;
123
+ else if (node.type === "html")
124
+ visible = !isMarkdownComment(node);
125
+ else if ("value" in node)
126
+ visible = Boolean(node.value.trim());
127
+ else if ("children" in node)
128
+ visible = node.children.map(markVisibleContent).some(Boolean);
129
+ if (visible)
130
+ visibleNodes.add(node);
131
+ return visible;
132
+ }
133
+ markVisibleContent(document.root);
134
+ const ranges = [];
135
+ walkMarkdown(document.root, (node) => {
136
+ if (!visibleNodes.has(node)) {
137
+ const start = Math.max(document.start, node.position?.start.offset ?? document.start);
138
+ const end = Math.min(document.end, node.position?.end.offset ?? document.end);
139
+ if (end > start)
140
+ ranges.push([start, end]);
141
+ return false;
142
+ }
143
+ if (node.type === "html") {
144
+ const nodeStart = node.position?.start.offset ?? document.start;
145
+ for (const comment of readHtmlToken(node, markdownNodeSource(document, node)).comments)
146
+ ranges.push([nodeStart + comment.start, nodeStart + comment.end]);
147
+ return false;
148
+ }
149
+ });
150
+ return maskSourceRanges(document.source, ranges, document.start, document.end);
151
+ }
152
+ function visibleMarkdownLines(input) {
153
+ return visibleMarkdownSource(input).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
154
+ }
155
+ function markdownListItems(input) {
156
+ const document = parseMarkdown(input);
157
+ const items = [];
158
+ walkMarkdown(document.root, (node) => {
159
+ if (node.type === "blockquote" || node.type === "footnoteDefinition")
160
+ return false;
161
+ if (node.type === "listItem") {
162
+ const source = markdownNodeSource(document, node);
163
+ items.push({ node, line: source.split(/\r?\n/, 1)[0].trim() });
164
+ }
165
+ });
166
+ return items;
167
+ }
168
+ function markdownFrontmatter(input) {
169
+ const document = parseMarkdown(input);
170
+ const node = document.root.children[0];
171
+ if (node?.type !== "yaml" || node.position?.start.offset !== 0)
172
+ return null;
173
+ const raw = markdownNodeSource(document, node);
174
+ const lines = raw.split(/\r?\n/);
175
+ if (lines[0] !== "---" || !["---", "..."].includes(lines.at(-1) ?? ""))
176
+ return null;
177
+ let end = node.position.end.offset ?? raw.length;
178
+ if (document.source.slice(end, end + 2) === "\r\n")
179
+ end += 2;
180
+ else if (document.source[end] === "\n")
181
+ end++;
182
+ return { node, value: node.value, end };
183
+ }
184
+ function markdownProse(input) {
185
+ const document = parseMarkdown(input);
186
+ const fragments = [];
187
+ walkMarkdown(document.root, (node) => {
188
+ if (node.type === "text" || node.type === "html")
189
+ fragments.push({ value: markdownText(node), line: node.position?.start.line ?? 1 });
190
+ });
191
+ return fragments;
192
+ }
12
193
 
13
194
  // src/core/document-model.ts
14
195
  var CHANGE_DOCUMENT_SCHEMA = {
@@ -44,9 +225,11 @@ var DocumentSectionCardinalityError = class extends Error {
44
225
  sectionId;
45
226
  count;
46
227
  };
47
- function parseRspDocument(source, schema) {
228
+ function parseRspDocument(input, schema) {
229
+ const markdown = parseMarkdown(input);
230
+ const source = markdown.source;
48
231
  const definitionsByHeading = new Map(schema.sections.map((definition) => [definition.heading, definition]));
49
- const observedHeadings = collectLevelTwoHeadings(source);
232
+ const observedHeadings = collectLevelTwoHeadings(markdown);
50
233
  const sections = /* @__PURE__ */ new Map();
51
234
  const unknownHeadings = [];
52
235
  for (let index = 0; index < observedHeadings.length; index++) {
@@ -62,11 +245,8 @@ function parseRspDocument(source, schema) {
62
245
  id: definition.id,
63
246
  heading: definition.heading,
64
247
  canonical: observed.canonical,
65
- body: source.slice(observed.end, bodyEnd).trim(),
66
- headingStart: observed.start,
67
- headingEnd: observed.end,
68
- bodyStart: observed.end,
69
- bodyEnd
248
+ bodyEnd,
249
+ markdown: markdownSection(markdown, markdown.root.children.filter((node) => (node.position?.start.offset ?? 0) >= observed.end && (node.position?.end.offset ?? source.length) <= bodyEnd), observed.end, bodyEnd)
70
250
  };
71
251
  const matches = sections.get(definition.id) ?? [];
72
252
  matches.push(section);
@@ -74,10 +254,11 @@ function parseRspDocument(source, schema) {
74
254
  }
75
255
  const missingSections = schema.sections.filter((definition) => !sections.has(definition.id)).map((definition) => definition.id);
76
256
  const duplicateSections = schema.sections.filter((definition) => (sections.get(definition.id)?.length ?? 0) > 1).map((definition) => definition.id);
77
- const titleOccurrences = collectDocumentTitles(source, schema.titlePrefix);
257
+ const titleOccurrences = collectDocumentTitles(markdown, schema.titlePrefix);
78
258
  const titles = titleOccurrences.map((title) => title.value);
79
259
  return {
80
260
  source,
261
+ markdown,
81
262
  schema,
82
263
  title: titles[0] ?? null,
83
264
  titles,
@@ -102,8 +283,8 @@ function getDocumentSections(document, sectionId) {
102
283
  function getDocumentSection(document, sectionId) {
103
284
  return getDocumentSections(document, sectionId)[0];
104
285
  }
105
- function getDocumentSectionBody(document, sectionId) {
106
- return getDocumentSection(document, sectionId)?.body ?? "";
286
+ function getDocumentSectionMarkdown(document, sectionId) {
287
+ return getDocumentSection(document, sectionId)?.markdown ?? markdownSection(document.markdown, [], 0, 0);
107
288
  }
108
289
  function getDocumentSectionDefinitionByHeading(schema, heading) {
109
290
  return schema.sections.find((candidate) => candidate.heading === heading);
@@ -130,24 +311,33 @@ function appendDocumentSectionItem(document, sectionId, item) {
130
311
  return `${before}${separator}${item}
131
312
  ${document.source.slice(insertAt)}`;
132
313
  }
133
- function collectLevelTwoHeadings(source) {
314
+ function collectLevelTwoHeadings(document) {
134
315
  const headings = [];
135
- const pattern = /^## ([^\r\n]+)\r?$/gm;
136
- let match;
137
- while ((match = pattern.exec(source)) !== null) {
138
- const heading = match[1].trimEnd();
316
+ for (const node of document.root.children) {
317
+ if (node.type !== "heading" || node.depth !== 2 || node.position?.start.column !== 1)
318
+ continue;
319
+ const raw = markdownNodeSource(document, node);
320
+ if (!raw.startsWith("## ") || raw.includes("\n"))
321
+ continue;
322
+ const value = raw.slice(3);
323
+ const heading = value.trimEnd();
139
324
  headings.push({
140
325
  heading,
141
- canonical: heading === match[1],
142
- start: match.index,
143
- end: match.index + match[0].length
326
+ canonical: heading === value,
327
+ start: node.position.start.offset,
328
+ end: node.position.end.offset
144
329
  });
145
330
  }
146
331
  return headings;
147
332
  }
148
- function collectDocumentTitles(source, prefix) {
333
+ function collectDocumentTitles(document, prefix) {
149
334
  const titles = [];
150
- for (const line of source.split(/\r?\n/)) {
335
+ for (const node of document.root.children) {
336
+ if (node.type !== "heading" || node.depth !== 1)
337
+ continue;
338
+ const start = node.position.start.offset;
339
+ const lineStart = document.source.lastIndexOf("\n", start - 1) + 1;
340
+ const line = document.source.slice(lineStart, node.position.end.offset);
151
341
  const trimmed = line.trim();
152
342
  if (!trimmed.startsWith(prefix))
153
343
  continue;
@@ -166,110 +356,185 @@ function lineBreakStart(source, headingStart) {
166
356
  return headingStart;
167
357
  }
168
358
 
169
- // src/core/content.ts
170
- function parseYamlText(text) {
171
- const parsed = parse(text);
172
- if (parsed === null || parsed === void 0)
359
+ // src/core/yaml.mjs
360
+ import { parseDocument } from "yaml";
361
+ function parseYamlDocument(source) {
362
+ const document = parseDocument(source, { keepSourceTokens: true });
363
+ if (document.errors.length)
364
+ throw new Error(document.errors.map((error) => error.message).join("; "));
365
+ return document;
366
+ }
367
+ function yamlMapping(document) {
368
+ const value = document.toJS();
369
+ if (value === null || value === void 0)
173
370
  return {};
174
- if (typeof parsed !== "object" || Array.isArray(parsed))
371
+ if (typeof value !== "object" || Array.isArray(value))
175
372
  throw new Error("YAML document must be a mapping/object");
176
- return parsed;
373
+ return value;
374
+ }
375
+
376
+ // src/core/content.ts
377
+ function parseYamlText(text) {
378
+ return yamlMapping(parseYamlDocument(text));
177
379
  }
178
380
  function parseFrontmatter(content) {
179
- const m = content.match(/^---\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)\r?\n/);
180
- if (!m)
181
- return null;
182
- return parseYamlText(m[1]);
381
+ const matter = markdownFrontmatter(content);
382
+ return matter ? parseYamlText(matter.value) : null;
383
+ }
384
+ function checkboxItem(line) {
385
+ const marker = line.match(/^[-*+]\s+\[([ /x-])\]/i);
386
+ return marker ? { state: marker[1].toLowerCase(), line } : null;
387
+ }
388
+ function collectCheckboxItems(content) {
389
+ return markdownListItems(content).flatMap(({ line }) => {
390
+ const item = checkboxItem(line);
391
+ return item ? [item] : [];
392
+ });
393
+ }
394
+ function countCheckboxItems(items) {
395
+ const count = { todo: 0, progress: 0, done: 0, dropped: 0, total: items.length };
396
+ for (const { state } of items) {
397
+ if (state === " ")
398
+ count.todo++;
399
+ else if (state === "/")
400
+ count.progress++;
401
+ else if (state === "x")
402
+ count.done++;
403
+ else
404
+ count.dropped++;
405
+ }
406
+ return count;
183
407
  }
184
408
  function countCheckboxes(content) {
185
- const todo = (content.match(/\[ \]/g) || []).length;
186
- const progress = (content.match(/\[\/\]/g) || []).length;
187
- const done = (content.match(/\[x\]/g) || []).length;
188
- const dropped = (content.match(/\[-\]/g) || []).length;
189
- return { todo, progress, done, dropped, total: todo + progress + done + dropped };
190
- }
191
- function classifyVerifyCheckboxes(sectionText) {
192
- const buckets = collectVerifyCheckboxLines(sectionText);
193
- return {
194
- required: countCheckboxes([...buckets.required, ...buckets.unclassified].join("\n")),
195
- optional: countCheckboxes(buckets.optional.join("\n")),
196
- unclassified: countCheckboxes(buckets.unclassified.join("\n")),
197
- legacy: buckets.legacy
198
- };
409
+ return countCheckboxItems(collectCheckboxItems(content));
199
410
  }
200
- function collectVerifyCheckboxLines(sectionText) {
201
- const required = [];
202
- const optional = [];
203
- const unclassified = [];
411
+ function collectVerifyCheckboxes(content) {
412
+ const document = parseMarkdown(content);
413
+ const buckets = { required: [], optional: [], unclassified: [] };
204
414
  let current = "unclassified";
205
415
  let sawClassificationHeading = false;
206
- for (const line of sectionText.split("\n")) {
207
- const heading = line.match(/^###\s+(Required|Optional)\s*$/i);
208
- if (heading) {
209
- current = heading[1].toLowerCase();
210
- sawClassificationHeading = true;
211
- continue;
416
+ walkMarkdown(document.root, (node) => {
417
+ if (node.type === "blockquote" || node.type === "footnoteDefinition")
418
+ return false;
419
+ if (node.type === "heading" && node.depth === 3 && node.position?.start.column === 1) {
420
+ const heading = markdownNodeSource(document, node).match(/^###\s+(Required|Optional)\s*$/i);
421
+ if (heading) {
422
+ current = heading[1].toLowerCase();
423
+ sawClassificationHeading = true;
424
+ }
212
425
  }
213
- if (!/^- \[[ /x-]\]/i.test(line.trim()))
214
- continue;
215
- if (current === "required")
216
- required.push(line);
217
- else if (current === "optional")
218
- optional.push(line);
219
- else
220
- unclassified.push(line);
221
- }
222
- return { required, optional, unclassified, legacy: unclassified.length > 0 || !sawClassificationHeading };
426
+ if (node.type === "listItem") {
427
+ const line = markdownNodeSource(document, node).split(/\r?\n/, 1)[0].trim();
428
+ const item = checkboxItem(line);
429
+ if (item)
430
+ buckets[current].push(item);
431
+ }
432
+ });
433
+ return { ...buckets, legacy: buckets.unclassified.length > 0 || !sawClassificationHeading };
434
+ }
435
+ function summarizeVerifyCheckboxes(buckets) {
436
+ return {
437
+ required: countCheckboxItems([...buckets.required, ...buckets.unclassified]),
438
+ optional: countCheckboxItems(buckets.optional),
439
+ unclassified: countCheckboxItems(buckets.unclassified),
440
+ legacy: buckets.legacy
441
+ };
442
+ }
443
+ function openCheckboxLines(items) {
444
+ return items.filter((item) => item.state === " " || item.state === "/").map((item) => item.line);
223
445
  }
224
- function getOpenCheckboxes(sectionText) {
225
- return sectionText.split("\n").map((line) => line.trim()).filter((line) => /^- \[[ /]\]/.test(line));
446
+ function getOpenCheckboxes(content) {
447
+ return openCheckboxLines(collectCheckboxItems(content));
226
448
  }
227
- function extractBlockerLines(content) {
228
- const document = parseRspDocument(content, CHANGE_DOCUMENT_SCHEMA);
229
- return normalizeBlockerBody(getDocumentSectionBody(document, "blockers"));
449
+ function changeDocument(content) {
450
+ return typeof content === "string" ? parseRspDocument(content, CHANGE_DOCUMENT_SCHEMA) : content;
230
451
  }
231
452
  function hasMeaningfulBlockerBody(body) {
232
- return hasMeaningfulBlockerLines(normalizeBlockerBody(body));
453
+ return readBlockerEntries(body).length > 0;
233
454
  }
234
- function normalizeBlockerBody(body) {
235
- return body.replace(/<!--[\s\S]*?-->/g, "").split("\n").map((line) => line.trim()).filter(Boolean);
455
+ function getVisibleBodyLines(body) {
456
+ return visibleMarkdownLines(body);
236
457
  }
237
458
  function hasMeaningfulBlockers(content) {
238
- return hasMeaningfulBlockerLines(extractBlockerLines(content));
459
+ return hasMeaningfulBlockerBody(getDocumentSectionMarkdown(changeDocument(content), "blockers"));
239
460
  }
240
461
  function isEmptyBlockerLine(line) {
241
462
  const normalized = line.trim();
242
463
  return /^none[.。]?$/i.test(normalized) || /^[-*]\s*(?:none[.。]?)?$/i.test(normalized);
243
464
  }
244
- function hasMeaningfulBlockerLines(lines) {
245
- if (lines.length === 0)
246
- return false;
247
- return lines.some((line) => !isEmptyBlockerLine(line));
465
+ var REQUIRES_RE = /^[-*]\s+requires\s+`([^`]+)`:[ \t]*(\S.*)$/i;
466
+ function readBlockerEntries(input) {
467
+ const document = parseMarkdown(input);
468
+ const entries = [];
469
+ const nodeLines = (node, start = node.position.start.offset) => getVisibleBodyLines(
470
+ markdownSection(document, [node], start, node.position.end.offset)
471
+ );
472
+ const text = (line) => line.replace(/^[-*]\s+/, "").trim();
473
+ const appendExternal = (lines) => {
474
+ for (const line of lines) {
475
+ if (!isEmptyBlockerLine(line))
476
+ entries.push({ kind: "external", line, text: text(line) });
477
+ }
478
+ };
479
+ const visit = (node) => {
480
+ if (node.type === "code")
481
+ return;
482
+ if (node.type !== "list") {
483
+ appendExternal(nodeLines(node));
484
+ return;
485
+ }
486
+ for (const item of node.children) {
487
+ for (const [index, child] of item.children.entries()) {
488
+ if (child.type === "list" || child.type === "code") {
489
+ visit(child);
490
+ continue;
491
+ }
492
+ const lines = nodeLines(child, index === 0 ? item.position.start.offset : child.position.start.offset);
493
+ if (index === 0 && child.type === "paragraph") {
494
+ const line = lines[0] ?? "";
495
+ const dependency = line.match(REQUIRES_RE);
496
+ if (dependency) {
497
+ entries.push({ kind: "dependency", line, text: text(line), name: dependency[1], reason: dependency[2] });
498
+ lines.shift();
499
+ } else if (/^[-*]\s+requires\b/i.test(line)) {
500
+ entries.push({ kind: "malformed-dependency", line, text: text(line) });
501
+ lines.shift();
502
+ }
503
+ }
504
+ appendExternal(lines);
505
+ }
506
+ }
507
+ };
508
+ document.root.children.forEach(visit);
509
+ return entries;
248
510
  }
249
511
  function detectDeltaSections(content) {
250
- const document = parseRspDocument(content, CHANGE_DOCUMENT_SCHEMA);
251
- const body = getDocumentSectionBody(document, "spec");
252
- return {
253
- added: /^###\s*ADDED/im.test(body),
254
- modified: /^###\s*MODIFIED/im.test(body),
255
- removed: /^###\s*REMOVED/im.test(body)
256
- };
512
+ const body = getDocumentSectionMarkdown(changeDocument(content), "spec");
513
+ const labels = new Set(body.root.children.filter((node) => node.type === "heading" && node.depth === 3).map((node) => markdownText(node).trim().toUpperCase()));
514
+ return { added: labels.has("ADDED"), modified: labels.has("MODIFIED"), removed: labels.has("REMOVED") };
257
515
  }
258
516
  function parseScenarios(content) {
517
+ const document = parseMarkdown(content);
259
518
  const scenarios = [];
260
- const regex = /^#{3,4}\s*Scenario:\s*(.+)$/gim;
261
- let match;
262
- while ((match = regex.exec(content)) !== null) {
263
- const heading = match[1].trim();
264
- const startPos = match.index + match[0].length;
265
- const restContent = content.slice(startPos);
266
- const nextSection = restContent.match(/^#{3,4}\s/m);
267
- const block = nextSection ? restContent.slice(0, nextSection.index) : restContent;
268
- const steps = block.split("\n").map((s) => s.trim()).filter((s) => s.startsWith("-") && /GIVEN|WHEN|THEN|AND|BUT/i.test(s)).map((s) => s.replace(/^[-*]\s*/, ""));
269
- if (steps.length > 0)
270
- scenarios.push({ heading, steps });
519
+ let scenario = null;
520
+ for (const node of document.root.children) {
521
+ if (node.type === "heading" && node.depth <= 4) {
522
+ const title = markdownText(node);
523
+ scenario = (node.depth === 3 || node.depth === 4) && /^Scenario:\s*\S/i.test(title) ? { heading: title.slice(title.indexOf(":") + 1).trim(), steps: [] } : null;
524
+ if (scenario)
525
+ scenarios.push(scenario);
526
+ continue;
527
+ }
528
+ if (scenario && node.type === "list") {
529
+ for (const item of node.children) {
530
+ const line = markdownNodeSource(document, item).split(/\r?\n/, 1)[0].trim();
531
+ const step = line.replace(/^[-*+]\s+/, "");
532
+ if (/^(?:GIVEN|WHEN|THEN|AND|BUT)\b/i.test(step))
533
+ scenario.steps.push(step);
534
+ }
535
+ }
271
536
  }
272
- return scenarios;
537
+ return scenarios.filter((item) => item.steps.length > 0);
273
538
  }
274
539
 
275
540
  // src/core/decisions.ts
@@ -385,11 +650,11 @@ async function inspectManagedFileTree(root, label, options = {}) {
385
650
  if (!rootInspection.exists)
386
651
  return result;
387
652
  result.rootExists = true;
388
- await collectManagedFiles(root, label, result);
653
+ await collectManagedFiles(root, label, result, options.includeHidden);
389
654
  result.files.sort();
390
655
  return result;
391
656
  }
392
- async function collectManagedFiles(directory, label, result) {
657
+ async function collectManagedFiles(directory, label, result, includeHidden = false) {
393
658
  let entries;
394
659
  try {
395
660
  entries = await readdir(directory, { withFileTypes: true });
@@ -399,11 +664,11 @@ async function collectManagedFiles(directory, label, result) {
399
664
  return;
400
665
  }
401
666
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
402
- if (entry.name.startsWith("."))
667
+ if (!includeHidden && entry.name.startsWith("."))
403
668
  continue;
404
669
  const path = join(directory, entry.name);
405
670
  if (entry.isDirectory()) {
406
- await collectManagedFiles(path, label, result);
671
+ await collectManagedFiles(path, label, result, includeHidden);
407
672
  continue;
408
673
  }
409
674
  if (entry.isFile()) {
@@ -448,21 +713,6 @@ function resolveDecisionRecordsPath(config) {
448
713
  const configured = config.decisions?.path;
449
714
  return configured && validateDecisionRecordsPath(configured) === null ? normalizeDecisionRecordsPath(configured) : DEFAULT_DECISION_RECORDS_PATH;
450
715
  }
451
- async function ensureDecisionRecordsDirectory(pathValue) {
452
- const filesystemError = await validateDecisionRecordsFilesystemPath(pathValue);
453
- if (filesystemError)
454
- throw new Error(filesystemError);
455
- const existed = existsSync(pathValue);
456
- await mkdir(pathValue, { recursive: true });
457
- const entries = await readdir2(pathValue);
458
- if (entries.length === 0) {
459
- await ensureManagedFile(join2(pathValue, ".gitkeep"), "", "Decision Record placeholder");
460
- return true;
461
- }
462
- if (entries.includes(".gitkeep"))
463
- requireManagedFile(join2(pathValue, ".gitkeep"), "Decision Record placeholder");
464
- return !existed;
465
- }
466
716
  async function validateDecisionRecordsFilesystemPath(pathValue, cwd = process.cwd()) {
467
717
  const projectRoot = await realpath(cwd);
468
718
  const targetPath = resolve(projectRoot, pathValue);
@@ -496,18 +746,18 @@ var LOCK_PATH = join3(RSP_DIR, ".lock");
496
746
  var CONFIG_PATH = join3(RSP_DIR, "config.yaml");
497
747
  var VALID_KINDS = ["feature", "fix", "refactor", "docs", "ops", "research"];
498
748
  var DEFAULT_REQUIRED_SECTIONS = getCanonicalSectionHeadings(CHANGE_DOCUMENT_SCHEMA);
499
- var LEGACY_MISSING_CONFIG_MANAGE_POLICY = { activation: "explicit", closeout: "local" };
500
- var INVALID_CONFIG_MANAGE_POLICY = { activation: "explicit", closeout: "manual" };
749
+ var DEFAULT_WORKFLOW_POLICY = { delegation: "off", closeout: "ask" };
750
+ var INVALID_CONFIG_WORKFLOW_POLICY = { delegation: "off", closeout: "manual" };
501
751
  var CONFIG_DEFAULTS = {
502
752
  kinds: [],
503
753
  decisions: { path: ".rsp/specs/decisions" },
504
754
  language: { default: "en" },
505
- manage: { activation: "auto", closeout: "local" }
755
+ workflow: { delegation: "off", closeout: "ask" }
506
756
  };
507
757
  function generateConfigTemplate(config = {}) {
508
758
  const kinds = config.kinds ?? CONFIG_DEFAULTS.kinds;
509
759
  const decisions = { ...CONFIG_DEFAULTS.decisions, ...config.decisions };
510
- const manage = { ...CONFIG_DEFAULTS.manage, ...config.manage };
760
+ const workflow = { ...CONFIG_DEFAULTS.workflow, ...config.workflow };
511
761
  const language = { ...CONFIG_DEFAULTS.language, ...config.language };
512
762
  const kindsYaml = stringify(kinds).trimEnd();
513
763
  const renderedKinds = kinds.length === 0 ? `kinds: ${kindsYaml}` : `kinds:
@@ -528,12 +778,12 @@ ${renderedKinds}
528
778
  decisions:
529
779
  path: ${stringify(decisions.path).trim()}
530
780
 
531
- # New projects default to automatic Manage routing with local closeout.
532
- # Use activation: explicit to require a named managed request.
533
- # closeout accepts manual, lifecycle, or local; local routes a qualified clean terminal non-small boundary to one local commit but never push or publication.
534
- manage:
535
- activation: ${manage.activation}
536
- closeout: ${manage.closeout}
781
+ # Automatic delegation is off; explicit worker requests remain allowed.
782
+ # Closeout applies only to established final delivery: ask, auto, or manual.
783
+ # Ordinary completion and checkpoint commits keep work open. No setting grants Git authority.
784
+ workflow:
785
+ delegation: ${workflow.delegation}
786
+ closeout: ${workflow.closeout}
537
787
 
538
788
  # Set one shared project default for durable artifact and commit prose.
539
789
  # Response language remains user/session-owned and is never read from this project mapping.
@@ -543,102 +793,8 @@ ${artifactsLine}
543
793
  ${commitLine}
544
794
  `;
545
795
  }
546
- var MANAGE_ACTIVATIONS = ["explicit", "auto"];
547
- var MANAGE_CLOSEOUTS = ["manual", "lifecycle", "local"];
548
- function reconcileRspConfigDefaults(raw) {
549
- const document = parseDocument(raw);
550
- if (document.errors.length > 0)
551
- throw new Error(document.errors.map((error) => error.message).join("; "));
552
- const parsed = parseYamlText(raw);
553
- const issues = validateRspConfig(parsed);
554
- if (issues.length > 0)
555
- throw new Error(issues.join("; "));
556
- const added = collectMissingConfigDefaults(parsed);
557
- const customComments = hasCustomConfigComments(document, parsed);
558
- if (customComments) {
559
- backfillConfigDocument(document, parsed);
560
- }
561
- if (customComments && added.length === 0)
562
- return { content: raw, added, changed: false };
563
- const content = customComments ? String(document) : generateConfigTemplate(parsed);
564
- return { content, added, changed: content !== raw };
565
- }
566
- function collectMissingConfigDefaults(parsed) {
567
- const added = [];
568
- if (!("kinds" in parsed))
569
- added.push("kinds");
570
- if (!("decisions" in parsed) || !hasNestedKey(parsed.decisions, "path"))
571
- added.push("decisions.path");
572
- if (!("language" in parsed))
573
- added.push("language.default");
574
- if (!("manage" in parsed)) {
575
- added.push("manage.activation", "manage.closeout");
576
- } else {
577
- if (!hasNestedKey(parsed.manage, "activation"))
578
- added.push("manage.activation");
579
- if (!hasNestedKey(parsed.manage, "closeout"))
580
- added.push("manage.closeout");
581
- }
582
- return added;
583
- }
584
- function backfillConfigDocument(document, parsed) {
585
- if (!("kinds" in parsed))
586
- document.set("kinds", [...CONFIG_DEFAULTS.kinds]);
587
- if (!("decisions" in parsed))
588
- document.set("decisions", { path: CONFIG_DEFAULTS.decisions.path });
589
- else if (!hasNestedKey(parsed.decisions, "path"))
590
- document.setIn(["decisions", "path"], CONFIG_DEFAULTS.decisions.path);
591
- if (!("language" in parsed))
592
- document.set("language", { default: CONFIG_DEFAULTS.language.default });
593
- if (!("manage" in parsed)) {
594
- document.set("manage", { ...CONFIG_DEFAULTS.manage });
595
- } else {
596
- if (!hasNestedKey(parsed.manage, "activation"))
597
- document.setIn(["manage", "activation"], CONFIG_DEFAULTS.manage.activation);
598
- if (!hasNestedKey(parsed.manage, "closeout"))
599
- document.setIn(["manage", "closeout"], CONFIG_DEFAULTS.manage.closeout);
600
- }
601
- }
602
- function hasNestedKey(value, key) {
603
- return Boolean(value && typeof value === "object" && !Array.isArray(value) && key in value);
604
- }
605
- var LEGACY_GENERATED_CONFIG_COMMENTS = /* @__PURE__ */ new Set([
606
- "Omit kinds or use [] to retain the built-in defaults.",
607
- "New projects default to automatic Manage routing with lifecycle-only closeout.",
608
- "Self-host automatic Manage routing with bounded deterministic local commits; push and publication stay explicit.",
609
- "Set exactly one project-relative authoritative directory when the Host Project already owns Decision Records elsewhere.",
610
- "kinds:",
611
- "decisions:",
612
- "path: docs/adr",
613
- ...VALID_KINDS.map((kind) => `- ${kind}`)
614
- ]);
615
- function hasCustomConfigComments(document, parsed) {
616
- const generated = new Set(collectConfigComments(parseDocument(generateConfigTemplate(parsed))));
617
- for (const comment of LEGACY_GENERATED_CONFIG_COMMENTS)
618
- generated.add(comment);
619
- return collectConfigComments(document).some((comment) => !generated.has(comment));
620
- }
621
- function collectConfigComments(document) {
622
- const comments = [];
623
- const append = (value) => {
624
- if (!value)
625
- return;
626
- for (const line of value.split("\n")) {
627
- const comment = line.trim();
628
- if (comment)
629
- comments.push(comment);
630
- }
631
- };
632
- append(document.commentBefore);
633
- append(document.comment);
634
- visit(document, {
635
- Node(_key, node) {
636
- append(node.commentBefore);
637
- append(node.comment);
638
- }
639
- });
640
- return comments;
641
- }
796
+ var DELEGATION_POLICIES = ["off", "auto"];
797
+ var CLOSEOUT_POLICIES = ["ask", "auto", "manual"];
642
798
  var _configCache = null;
643
799
  async function loadRspConfig() {
644
800
  const inspection = await inspectRspConfig();
@@ -664,17 +820,17 @@ async function inspectRspConfig(cwd = process.cwd()) {
664
820
  const issues = validateRspConfig(parsed);
665
821
  const decisions = parsed.decisions && typeof parsed.decisions === "object" && !Array.isArray(parsed.decisions) ? parsed.decisions : void 0;
666
822
  const decisionPath = decisions?.path;
667
- const manageValue = parsed.manage && typeof parsed.manage === "object" && !Array.isArray(parsed.manage) ? parsed.manage : void 0;
823
+ const workflowValue = parsed.workflow && typeof parsed.workflow === "object" && !Array.isArray(parsed.workflow) ? parsed.workflow : void 0;
668
824
  const languageValue = parsed.language && typeof parsed.language === "object" && !Array.isArray(parsed.language) ? parsed.language : void 0;
669
- const activation = MANAGE_ACTIVATIONS.includes(manageValue?.activation) ? manageValue?.activation : void 0;
670
- const closeout = MANAGE_CLOSEOUTS.includes(manageValue?.closeout) ? manageValue?.closeout : void 0;
825
+ const delegation = DELEGATION_POLICIES.includes(workflowValue?.delegation) ? workflowValue?.delegation : void 0;
826
+ const closeout = CLOSEOUT_POLICIES.includes(workflowValue?.closeout) ? workflowValue?.closeout : void 0;
671
827
  const kinds = Array.isArray(parsed.kinds) && parsed.kinds.every((value) => typeof value === "string" && value.trim() !== "") ? parsed.kinds.map((value) => value.trim()) : void 0;
672
828
  const decisionRecordsIssue = getDecisionRecordsConfigIssue(parsed);
673
829
  const inspection = {
674
830
  config: {
675
831
  kinds,
676
832
  decisions: decisionRecordsIssue === null && validateDecisionRecordsPath(decisionPath) === null ? { path: normalizeDecisionRecordsPath(String(decisionPath)) } : void 0,
677
- manage: manageValue ? { activation, closeout } : void 0,
833
+ workflow: workflowValue ? { delegation, closeout } : void 0,
678
834
  language: parseLanguageConfig(languageValue)
679
835
  },
680
836
  issues
@@ -684,9 +840,11 @@ async function inspectRspConfig(cwd = process.cwd()) {
684
840
  }
685
841
  function validateRspConfig(parsed) {
686
842
  const issues = [];
687
- const supported = /* @__PURE__ */ new Set(["kinds", "decisions", "manage", "language"]);
843
+ const supported = /* @__PURE__ */ new Set(["kinds", "decisions", "workflow", "language"]);
688
844
  for (const key of Object.keys(parsed)) {
689
- if (key === "required_sections") {
845
+ if (key === "manage") {
846
+ issues.push('config.yaml field "manage" requires explicit migration: remove manage and choose workflow.delegation (off | auto) and workflow.closeout (ask | auto | manual); recommended defaults are off/ask. Old local closeout no longer grants Git authority. rsp update will not convert it automatically');
847
+ } else if (key === "required_sections") {
690
848
  issues.push('config.yaml field "required_sections" is no longer supported');
691
849
  } else if (!supported.has(key)) {
692
850
  issues.push(`config.yaml field "${key}" is not supported`);
@@ -707,20 +865,20 @@ function validateRspConfig(parsed) {
707
865
  const decisionRecordsIssue = getDecisionRecordsConfigIssue(parsed);
708
866
  if (decisionRecordsIssue)
709
867
  issues.push(decisionRecordsIssue);
710
- if ("manage" in parsed) {
711
- if (!parsed.manage || typeof parsed.manage !== "object" || Array.isArray(parsed.manage)) {
712
- issues.push('config.yaml field "manage" must be a YAML mapping');
868
+ if ("workflow" in parsed) {
869
+ if (!parsed.workflow || typeof parsed.workflow !== "object" || Array.isArray(parsed.workflow)) {
870
+ issues.push('config.yaml field "workflow" must be a YAML mapping');
713
871
  } else {
714
- const manage = parsed.manage;
715
- const supportedManageFields = /* @__PURE__ */ new Set(["activation", "closeout"]);
716
- for (const key of Object.keys(manage)) {
717
- if (!supportedManageFields.has(key))
718
- issues.push(`config.yaml field "manage.${key}" is not supported`);
872
+ const workflow = parsed.workflow;
873
+ const supportedWorkflowFields = /* @__PURE__ */ new Set(["delegation", "closeout"]);
874
+ for (const key of Object.keys(workflow)) {
875
+ if (!supportedWorkflowFields.has(key))
876
+ issues.push(`config.yaml field "workflow.${key}" is not supported`);
719
877
  }
720
- if ("activation" in manage && !MANAGE_ACTIVATIONS.includes(manage.activation))
721
- issues.push('config.yaml field "manage.activation" must be one of: explicit, auto');
722
- if ("closeout" in manage && !MANAGE_CLOSEOUTS.includes(manage.closeout))
723
- issues.push('config.yaml field "manage.closeout" must be one of: manual, lifecycle, local');
878
+ if ("delegation" in workflow && !DELEGATION_POLICIES.includes(workflow.delegation))
879
+ issues.push('config.yaml field "workflow.delegation" must be one of: off, auto');
880
+ if ("closeout" in workflow && !CLOSEOUT_POLICIES.includes(workflow.closeout))
881
+ issues.push('config.yaml field "workflow.closeout" must be one of: ask, auto, manual');
724
882
  }
725
883
  }
726
884
  if ("language" in parsed) {
@@ -752,12 +910,12 @@ function resolveKinds(config) {
752
910
  function resolveRequiredSections(_config) {
753
911
  return [...DEFAULT_REQUIRED_SECTIONS];
754
912
  }
755
- function resolveManagePolicy(config, options = {}) {
913
+ function resolveWorkflowPolicy(config, options = {}) {
756
914
  if (options.configValid === false)
757
- return { ...INVALID_CONFIG_MANAGE_POLICY };
915
+ return { ...INVALID_CONFIG_WORKFLOW_POLICY };
758
916
  return {
759
- activation: config.manage?.activation ?? LEGACY_MISSING_CONFIG_MANAGE_POLICY.activation,
760
- closeout: config.manage?.closeout ?? LEGACY_MISSING_CONFIG_MANAGE_POLICY.closeout
917
+ delegation: config.workflow?.delegation ?? DEFAULT_WORKFLOW_POLICY.delegation,
918
+ closeout: config.workflow?.closeout ?? DEFAULT_WORKFLOW_POLICY.closeout
761
919
  };
762
920
  }
763
921
  function resolveLanguagePolicy(config, options = {}) {
@@ -796,18 +954,15 @@ async function getVersion() {
796
954
  return version;
797
955
  }
798
956
 
799
- // src/core/output.ts
800
- import pc2 from "picocolors";
801
- function toErrorMessage(error) {
802
- return error instanceof Error ? error.message : String(error);
803
- }
804
- function emitJson(data, options = {}) {
805
- const indentation = options.compact ? void 0 : 2;
806
- process.stdout.write(`${JSON.stringify(data, null, indentation)}
807
- `);
808
- }
809
-
810
957
  export {
958
+ parseMarkdown,
959
+ walkMarkdown,
960
+ isMarkdownComment,
961
+ markdownTopLevelComments,
962
+ markdownText,
963
+ visibleMarkdownSource,
964
+ markdownListItems,
965
+ markdownProse,
811
966
  CHANGE_DOCUMENT_SCHEMA,
812
967
  GROUP_BRIEF_DOCUMENT_SCHEMA,
813
968
  DocumentSectionCardinalityError,
@@ -815,7 +970,7 @@ export {
815
970
  getDocumentTitles,
816
971
  getDocumentTitle,
817
972
  getDocumentSections,
818
- getDocumentSectionBody,
973
+ getDocumentSectionMarkdown,
819
974
  getDocumentSectionDefinitionByHeading,
820
975
  getCanonicalSectionHeadings,
821
976
  renderDocumentSectionHeading,
@@ -823,13 +978,14 @@ export {
823
978
  appendDocumentSectionItem,
824
979
  parseFrontmatter,
825
980
  countCheckboxes,
826
- classifyVerifyCheckboxes,
827
- collectVerifyCheckboxLines,
981
+ collectVerifyCheckboxes,
982
+ summarizeVerifyCheckboxes,
983
+ openCheckboxLines,
828
984
  getOpenCheckboxes,
829
- extractBlockerLines,
830
985
  hasMeaningfulBlockerBody,
986
+ getVisibleBodyLines,
831
987
  hasMeaningfulBlockers,
832
- isEmptyBlockerLine,
988
+ readBlockerEntries,
833
989
  detectDeltaSections,
834
990
  parseScenarios,
835
991
  ManagedPathError,
@@ -843,7 +999,6 @@ export {
843
999
  inspectManagedFileTree,
844
1000
  DEFAULT_DECISION_RECORDS_PATH,
845
1001
  resolveDecisionRecordsPath,
846
- ensureDecisionRecordsDirectory,
847
1002
  validateDecisionRecordsFilesystemPath,
848
1003
  pc,
849
1004
  PKG_ROOT,
@@ -857,15 +1012,12 @@ export {
857
1012
  LOCK_PATH,
858
1013
  CONFIG_PATH,
859
1014
  generateConfigTemplate,
860
- reconcileRspConfigDefaults,
861
1015
  loadRspConfig,
862
1016
  inspectRspConfig,
863
1017
  clearConfigCache,
864
1018
  resolveKinds,
865
1019
  resolveRequiredSections,
866
- resolveManagePolicy,
1020
+ resolveWorkflowPolicy,
867
1021
  resolveLanguagePolicy,
868
- getVersion,
869
- toErrorMessage,
870
- emitJson
1022
+ getVersion
871
1023
  };