@descent-vtt/spec-harness 0.2.0 → 0.3.1

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 (42) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +107 -0
  4. package/README.md +3 -3
  5. package/dist/commands.js +8 -1
  6. package/dist/commands.js.map +1 -1
  7. package/dist/context.d.ts +7 -1
  8. package/dist/context.js +14 -2
  9. package/dist/context.js.map +1 -1
  10. package/dist/document.d.ts +6 -0
  11. package/dist/document.js.map +1 -1
  12. package/dist/git.d.ts +8 -1
  13. package/dist/git.js +8 -5
  14. package/dist/git.js.map +1 -1
  15. package/dist/reader.js +3 -1
  16. package/dist/reader.js.map +1 -1
  17. package/dist/round.js +1 -1
  18. package/dist/round.js.map +1 -1
  19. package/dist/sandbox.d.ts +3 -1
  20. package/dist/sandbox.js +129 -30
  21. package/dist/sandbox.js.map +1 -1
  22. package/dist/server.js +10 -1
  23. package/dist/server.js.map +1 -1
  24. package/dist/vendor/spec-core/markdown/links.d.ts +2 -2
  25. package/dist/vendor/spec-core/markdown/links.js +92 -26
  26. package/dist/vendor/spec-core/markdown/links.js.map +1 -1
  27. package/dist/vendor/spec-core/markdown/scan.js +1 -1
  28. package/dist/vendor/spec-core/markdown/scan.js.map +1 -1
  29. package/dist/vendor/spec-core/markdown/tables.js +3 -0
  30. package/dist/vendor/spec-core/markdown/tables.js.map +1 -1
  31. package/dist/vendor/spec-core/pattern/automaton.d.ts +15 -2
  32. package/dist/vendor/spec-core/pattern/automaton.js +21 -2
  33. package/dist/vendor/spec-core/pattern/automaton.js.map +1 -1
  34. package/dist/vendor/spec-core/pattern/charset.d.ts +5 -1
  35. package/dist/vendor/spec-core/pattern/charset.js +8 -1
  36. package/dist/vendor/spec-core/pattern/charset.js.map +1 -1
  37. package/dist/vendor/spec-core/pattern/glob.d.ts +14 -3
  38. package/dist/vendor/spec-core/pattern/glob.js +55 -16
  39. package/dist/vendor/spec-core/pattern/glob.js.map +1 -1
  40. package/dist/vendor/spec-core/text/lines.js +10 -0
  41. package/dist/vendor/spec-core/text/lines.js.map +1 -1
  42. package/package.json +1 -1
package/dist/reader.js CHANGED
@@ -98,7 +98,9 @@ export function createReader() {
98
98
  const entry = front?.entries.find((candidate) => candidate.name === 'status');
99
99
  if (entry !== undefined && entry.value.kind === 'scalar')
100
100
  status = entry.value.scalar.text || null;
101
- return { title, status };
101
+ // TOML front matter gives no status, closed or not, so closing it would
102
+ // change nothing the context packet shows; only YAML's is worth saying.
103
+ return { title, status, unclosedFrontMatter: scanned.unclosedFrontMatter?.kind === 'yaml' };
102
104
  },
103
105
  };
104
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reader.js","sourceRoot":"","sources":["../src/reader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The document reader, on spec-core's Markdown scanner: nothing inside code\n * or a comment is ever taken for a section, a table, a link or a probe.\n */\n\nimport type { Citation, DocumentReader, SectionTables } from './document.js';\nimport type { CodeBlock } from './probe.js';\nimport type { TableView } from './rulings.js';\nimport { readFrontMatter, scanMarkdown, type MarkdownScan } from './vendor/spec-core/markdown/index.js';\n\n/** Section names compare without case, emphasis, a leading number or a trailing colon. */\nexport function sameSection(heading: string, name: string): boolean {\n const normal = (text: string): string =>\n text\n .replace(/[*_`]/g, '')\n .replace(/^\\s*\\d+[.)]?\\s+/, '')\n .replace(/[:\\s]+$/, '')\n .trim()\n .toLowerCase();\n return normal(heading) === normal(name);\n}\n\nfunction scanOf(text: string, cache: { text: string; scan: MarkdownScan } | null): MarkdownScan {\n return cache !== null && cache.text === text ? cache.scan : scanMarkdown(text);\n}\n\nexport function createReader(): DocumentReader {\n // Commands read the same brief several times in a row; one scan serves them.\n let last: { text: string; scan: MarkdownScan } | null = null;\n const scan = (text: string): MarkdownScan => {\n const result = scanOf(text, last);\n last = { text, scan: result };\n return result;\n };\n\n return {\n codeBlocks(text: string): CodeBlock[] {\n const scanned = scan(text);\n const spaces = (line: string): number => (/^ */.exec(line) as RegExpExecArray)[0].length;\n return scanned.blocks\n .filter((block) => block.kind === 'fenced')\n .map((block) => {\n const first = block.line + 1;\n const last = block.closed ? block.endLine - 1 : block.endLine;\n // A fence in a block quote or a list item: its content is the code\n // without the quote markers and without the fence's own indent, as\n // CommonMark reads it, so a probe file is written as it was meant.\n const indent = spaces(scanned.lines[block.line - 1]?.content ?? '');\n const lines: string[] = [];\n for (let line = first; line <= last; line += 1) {\n const content = scanned.lines[line - 1]?.content ?? '';\n lines.push(content.slice(Math.min(indent, spaces(content))));\n }\n return { info: block.info, content: lines.join('\\n'), line: block.line };\n });\n },\n\n sectionAt(text: string, line: number): string | null {\n let found: string | null = null;\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level >= 2) found = heading.text;\n }\n return found;\n },\n\n sectionsAt(text: string, line: number): string[] {\n const open: { level: number; text: string }[] = [];\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level < 2) continue;\n while (open.length > 0 && (open[open.length - 1] as { level: number }).level >= heading.level) open.pop();\n open.push({ level: heading.level, text: heading.text });\n }\n return open.map((section) => section.text);\n },\n\n sectionTables(text: string, section: string): SectionTables {\n const scanned = scan(text);\n const headings = scanned.headings;\n const index = headings.findIndex((heading) => heading.level >= 2 && sameSection(heading.text, section));\n if (index < 0) return { tables: [], headingLine: null, tableEndLine: null };\n const heading = headings[index] as (typeof headings)[number];\n const next = headings.slice(index + 1).find((candidate) => candidate.level <= heading.level);\n const endLine = next === undefined ? scanned.index.lineCount + 1 : next.line;\n const tables = scanned.tables.filter((table) => table.line > heading.endLine && table.line < endLine);\n const views: TableView[] = tables.map((table) => ({\n headers: table.headers.map((cell) => cell.text),\n rows: table.rows.map((row) => ({ cells: row.cells.map((cell) => cell.text), line: row.line })),\n }));\n const lastTable = tables[tables.length - 1];\n return { tables: views, headingLine: heading.endLine, tableEndLine: lastTable === undefined ? null : lastTable.endLine };\n },\n\n citations(text: string): Citation[] {\n return scan(text)\n .links.filter((link) => !link.image && link.form !== 'autolink' && link.form !== 'definition')\n .map((link) => ({ target: link.target, line: link.line }));\n },\n\n titleAndStatus(text: string): { title: string | null; status: string | null } {\n const scanned = scan(text);\n const title = scanned.headings.find((heading) => heading.level === 1)?.text ?? null;\n const front = readFrontMatter(text);\n let status: string | null = null;\n const entry = front?.entries.find((candidate) => candidate.name === 'status');\n if (entry !== undefined && entry.value.kind === 'scalar') status = entry.value.scalar.text || null;\n return { title, status };\n },\n };\n}\n"]}
1
+ {"version":3,"file":"reader.js","sourceRoot":"","sources":["../src/reader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAqB,MAAM,sCAAsC,CAAC;AAExG,0FAA0F;AAC1F,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAY;IACvD,MAAM,MAAM,GAAG,CAAC,IAAY,EAAU,EAAE,CACtC,IAAI;SACD,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;SAC9B,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,IAAI,EAAE;SACN,WAAW,EAAE,CAAC;IACnB,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,MAAM,CAAC,IAAY,EAAE,KAAkD;IAC9E,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,6EAA6E;IAC7E,IAAI,IAAI,GAAgD,IAAI,CAAC;IAC7D,MAAM,IAAI,GAAG,CAAC,IAAY,EAAgB,EAAE;QAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClC,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO;QACL,UAAU,CAAC,IAAY;YACrB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,CAAC,IAAY,EAAU,EAAE,CAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAqB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACzF,OAAO,OAAO,CAAC,MAAM;iBAClB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;iBAC1C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACb,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;gBAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;gBAC9D,mEAAmE;gBACnE,mEAAmE;gBACnE,mEAAmE;gBACnE,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;gBACpE,MAAM,KAAK,GAAa,EAAE,CAAC;gBAC3B,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;oBAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC;oBACvD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC/D,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YAC3E,CAAC,CAAC,CAAC;QACP,CAAC;QAED,SAAS,CAAC,IAAY,EAAE,IAAY;YAClC,IAAI,KAAK,GAAkB,IAAI,CAAC;YAChC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC1C,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI;oBAAE,MAAM;gBAC/B,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC;oBAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;YAC/C,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,UAAU,CAAC,IAAY,EAAE,IAAY;YACnC,MAAM,IAAI,GAAsC,EAAE,CAAC;YACnD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC1C,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI;oBAAE,MAAM;gBAC/B,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC;oBAAE,SAAS;gBAChC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAK,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAuB,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;oBAAE,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC1G,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC;QAED,aAAa,CAAC,IAAY,EAAE,OAAe;YACzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;YAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YACxG,IAAI,KAAK,GAAG,CAAC;gBAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;YAC5E,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAA8B,CAAC;YAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7F,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YAC7E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;YACtG,MAAM,KAAK,GAAgB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAChD,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC/C,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;aAC/F,CAAC,CAAC,CAAC;YACJ,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAC5C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QAC3H,CAAC;QAED,SAAS,CAAC,IAAY;YACpB,OAAO,IAAI,CAAC,IAAI,CAAC;iBACd,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,CAAC;iBAC7F,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/D,CAAC;QAED,cAAc,CAAC,IAAY;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;YACpF,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,MAAM,GAAkB,IAAI,CAAC;YACjC,MAAM,KAAK,GAAG,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YAC9E,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;YACnG,wEAAwE;YACxE,wEAAwE;YACxE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;QAC9F,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The document reader, on spec-core's Markdown scanner: nothing inside code\n * or a comment is ever taken for a section, a table, a link or a probe.\n */\n\nimport type { Citation, DocumentReader, SectionTables } from './document.js';\nimport type { CodeBlock } from './probe.js';\nimport type { TableView } from './rulings.js';\nimport { readFrontMatter, scanMarkdown, type MarkdownScan } from './vendor/spec-core/markdown/index.js';\n\n/** Section names compare without case, emphasis, a leading number or a trailing colon. */\nexport function sameSection(heading: string, name: string): boolean {\n const normal = (text: string): string =>\n text\n .replace(/[*_`]/g, '')\n .replace(/^\\s*\\d+[.)]?\\s+/, '')\n .replace(/[:\\s]+$/, '')\n .trim()\n .toLowerCase();\n return normal(heading) === normal(name);\n}\n\nfunction scanOf(text: string, cache: { text: string; scan: MarkdownScan } | null): MarkdownScan {\n return cache !== null && cache.text === text ? cache.scan : scanMarkdown(text);\n}\n\nexport function createReader(): DocumentReader {\n // Commands read the same brief several times in a row; one scan serves them.\n let last: { text: string; scan: MarkdownScan } | null = null;\n const scan = (text: string): MarkdownScan => {\n const result = scanOf(text, last);\n last = { text, scan: result };\n return result;\n };\n\n return {\n codeBlocks(text: string): CodeBlock[] {\n const scanned = scan(text);\n const spaces = (line: string): number => (/^ */.exec(line) as RegExpExecArray)[0].length;\n return scanned.blocks\n .filter((block) => block.kind === 'fenced')\n .map((block) => {\n const first = block.line + 1;\n const last = block.closed ? block.endLine - 1 : block.endLine;\n // A fence in a block quote or a list item: its content is the code\n // without the quote markers and without the fence's own indent, as\n // CommonMark reads it, so a probe file is written as it was meant.\n const indent = spaces(scanned.lines[block.line - 1]?.content ?? '');\n const lines: string[] = [];\n for (let line = first; line <= last; line += 1) {\n const content = scanned.lines[line - 1]?.content ?? '';\n lines.push(content.slice(Math.min(indent, spaces(content))));\n }\n return { info: block.info, content: lines.join('\\n'), line: block.line };\n });\n },\n\n sectionAt(text: string, line: number): string | null {\n let found: string | null = null;\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level >= 2) found = heading.text;\n }\n return found;\n },\n\n sectionsAt(text: string, line: number): string[] {\n const open: { level: number; text: string }[] = [];\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level < 2) continue;\n while (open.length > 0 && (open[open.length - 1] as { level: number }).level >= heading.level) open.pop();\n open.push({ level: heading.level, text: heading.text });\n }\n return open.map((section) => section.text);\n },\n\n sectionTables(text: string, section: string): SectionTables {\n const scanned = scan(text);\n const headings = scanned.headings;\n const index = headings.findIndex((heading) => heading.level >= 2 && sameSection(heading.text, section));\n if (index < 0) return { tables: [], headingLine: null, tableEndLine: null };\n const heading = headings[index] as (typeof headings)[number];\n const next = headings.slice(index + 1).find((candidate) => candidate.level <= heading.level);\n const endLine = next === undefined ? scanned.index.lineCount + 1 : next.line;\n const tables = scanned.tables.filter((table) => table.line > heading.endLine && table.line < endLine);\n const views: TableView[] = tables.map((table) => ({\n headers: table.headers.map((cell) => cell.text),\n rows: table.rows.map((row) => ({ cells: row.cells.map((cell) => cell.text), line: row.line })),\n }));\n const lastTable = tables[tables.length - 1];\n return { tables: views, headingLine: heading.endLine, tableEndLine: lastTable === undefined ? null : lastTable.endLine };\n },\n\n citations(text: string): Citation[] {\n return scan(text)\n .links.filter((link) => !link.image && link.form !== 'autolink' && link.form !== 'definition')\n .map((link) => ({ target: link.target, line: link.line }));\n },\n\n titleAndStatus(text: string): { title: string | null; status: string | null; unclosedFrontMatter: boolean } {\n const scanned = scan(text);\n const title = scanned.headings.find((heading) => heading.level === 1)?.text ?? null;\n const front = readFrontMatter(text);\n let status: string | null = null;\n const entry = front?.entries.find((candidate) => candidate.name === 'status');\n if (entry !== undefined && entry.value.kind === 'scalar') status = entry.value.scalar.text || null;\n // TOML front matter gives no status, closed or not, so closing it would\n // change nothing the context packet shows; only YAML's is worth saying.\n return { title, status, unclosedFrontMatter: scanned.unclosedFrontMatter?.kind === 'yaml' };\n },\n };\n}\n"]}
package/dist/round.js CHANGED
@@ -164,7 +164,7 @@ async function citedDocuments(workspace, brief, text, reader) {
164
164
  continue;
165
165
  const content = document ? await readText(join(workspace.root, path)) : null;
166
166
  const meta = content === null ? { title: null, status: null } : reader.titleAndStatus(content);
167
- out.push({ path, title: meta.title, status: meta.status, text: content });
167
+ out.push({ path, ...meta, text: content });
168
168
  }
169
169
  return out;
170
170
  }
package/dist/round.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"round.js","sourceRoot":"","sources":["../src/round.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A round, measured: the operations the command line and the MCP server\n * share. Each gathers what a pure module needs - from git, the disk and the\n * siblings - and hands it over; the decisions are made in `context.ts`,\n * `audit.ts`, `guard.ts` and `rulings.ts`.\n */\n\nimport { createHash } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { readdir, readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { audit, type ArchiveReason, type AssertionOutcome, type AuditReport } from './audit.js';\nimport { sameId } from './branch.js';\nimport {\n enabledPlugin,\n holdsGuard,\n loadsPlugin,\n registersServer,\n SPEC_BRIEF_CONFIGS,\n type BaseSource,\n type ClaudeCodeWiring,\n type ClaudeSettings,\n type PluginState,\n} from './configure.js';\nimport { readRules, renderContext, type CitedDocument, type ContextPacket, type Rules } from './context.js';\nimport type { DocumentReader } from './document.js';\nimport { readJsonObject, readText, repositoryPath, stateDirectory, writeAtomic } from './fs.js';\nimport { blameLine, changes, mergeBase, remoteDefault, revision, show, verifyCommit } from './git.js';\nimport { decide, type Decision, type VerifiedRuling } from './guard.js';\nimport { diffManifest, ecosystemOf, manifestMatcher, type DependencyChange } from './manifests.js';\nimport {\n addRulingRow,\n nextId,\n readRulings,\n renderMemo,\n renderRow,\n type EscalationOption,\n type EscalationRequest,\n type RulingRow,\n} from './rulings.js';\nimport type { BriefRow, Finding } from './types.js';\nimport { compileGlob } from './vendor/spec-core/pattern/index.js';\nimport { dirname, isRelativeReference, resolveInside, splitReference } from './vendor/spec-core/path/index.js';\nimport { SiblingError } from './siblings.js';\nimport { UsageError, type CliIO, type Workspace } from './workspace.js';\n\n/* -------------------------------------------------------------------- base */\n\nexport type Base =\n | { readonly kind: 'resolved'; readonly ref: string; readonly source: BaseSource; readonly sha: string; readonly mergeBase: string; readonly head: string }\n | { readonly kind: 'unresolved'; readonly reason: string };\n\n/** What the round is measured from: the flag, the configuration, then the remote's default branch. */\nexport async function resolveBase(workspace: Workspace, flag: string | undefined): Promise<Base> {\n const source: BaseSource = flag !== undefined ? 'flag' : workspace.config.base !== null ? 'config' : 'remote';\n const ref = flag ?? workspace.config.base ?? (await remoteDefault(workspace.root));\n if (ref === null) {\n return { kind: 'unresolved', reason: 'no base is named and the remote has no default branch; pass --base <ref> or set \"base\"' };\n }\n const sha = await revision(ref, workspace.root);\n const head = await revision('HEAD', workspace.root);\n if (sha === null || head === null) return { kind: 'unresolved', reason: `\"${ref}\" names no commit` };\n const common = await mergeBase(sha, head, workspace.root);\n if (common === null) return { kind: 'unresolved', reason: `\"${ref}\" and HEAD share no history` };\n return { kind: 'resolved', ref, source, sha, mergeBase: common, head };\n}\n\n/* ----------------------------------------------------------------- rulings */\n\nexport interface RulingCheck {\n readonly rows: readonly RulingRow[];\n readonly verified: readonly VerifiedRuling[];\n readonly unverified: readonly { readonly id: string; readonly reason: string }[];\n readonly problems: readonly Finding[];\n}\n\n/**\n * The brief's rulings, each checked against its signature: the commit that\n * last changed its row must be signed by a principal the base branch's\n * allowed-signers file lists (ADR-0006).\n */\nexport async function checkRulings(workspace: Workspace, brief: BriefRow, text: string, reader: DocumentReader, base: Base): Promise<RulingCheck> {\n const { rulings: rows, problems } = readRulings(reader.sectionTables(text, workspace.config.rulings.section).tables);\n const findings: Finding[] = problems.map((problem) => ({\n rule: 'ruling-unreadable',\n severity: 'warning',\n message: problem.message,\n hint: 'a ruling row is | id | `paths` | allow or deny | note |',\n file: brief.file,\n line: problem.line,\n }));\n const allows = rows.filter((row) => row.decision === 'allow');\n if (allows.length === 0) return { rows, verified: [], unverified: [], problems: findings };\n if (base.kind === 'unresolved') {\n return { rows, verified: [], unverified: allows.map((row) => ({ id: row.id, reason: `no base to read the allowed signers from: ${base.reason}` })), problems: findings };\n }\n const signers = await show(base.sha, workspace.config.rulings.allowedSigners, workspace.root);\n if (signers === null) {\n const reason = `${base.ref} has no ${workspace.config.rulings.allowedSigners}, so no signature can count`;\n return { rows, verified: [], unverified: allows.map((row) => ({ id: row.id, reason })), problems: findings };\n }\n const directory = await stateDirectory(workspace.commonDir, 'signers');\n const file = join(directory, `${createHash('sha256').update(signers).digest('hex').slice(0, 16)}`);\n await writeAtomic(file, signers);\n const verified: VerifiedRuling[] = [];\n const unverified: { id: string; reason: string }[] = [];\n for (const row of allows) {\n // The row's line is a line of the text on disk, so it is blamed there: at\n // HEAD the same number can be another line, one a signed commit wrote,\n // and an uncommitted row would borrow that commit's signature.\n const commit = await blameLine(null, brief.file, row.line, workspace.root);\n if (commit === null || /^0+$/.test(commit)) {\n unverified.push({ id: row.id, reason: 'its row is not committed' });\n continue;\n }\n const signature = await verifyCommit(commit, file, workspace.root);\n if (signature.good && signature.principal !== null) verified.push({ id: row.id, paths: row.paths, signer: signature.principal });\n else unverified.push({ id: row.id, reason: `commit ${commit.slice(0, 12)} last changed its row, and ${signature.detail || 'it is not signed'}` });\n }\n return { rows, verified, unverified, problems: findings };\n}\n\n/* ------------------------------------------------------------------- guard */\n\nexport async function briefText(workspace: Workspace, brief: BriefRow): Promise<string> {\n const text = await readText(join(workspace.root, brief.file));\n if (text === null) throw new UsageError(`${brief.file} cannot be read`);\n return text;\n}\n\n/** Decides paths against the brief, with the rulings verified against the base `baseFlag` names, or the configured one. */\nexport async function checkPaths(\n workspace: Workspace,\n brief: BriefRow | null,\n noBrief: string | undefined,\n given: readonly string[],\n cwd: string,\n reader: DocumentReader,\n baseFlag: string | undefined,\n): Promise<Decision[]> {\n let rulings: readonly VerifiedRuling[] = [];\n const resolved = given.map((path) => ({ given: path, path: repositoryPath(path, workspace.root, cwd) }));\n if (brief !== null) {\n // Rulings cost a blame and a signature check each, so they are read only\n // when a write reaches a protected file.\n const first = resolved.map(({ path, given: g }) =>\n decide({ path, given: g, brief, noBrief, rulings: [], outOfScope: workspace.config.outOfScope }),\n );\n if (first.some((decision) => decision.reason === 'protected')) {\n const base = await resolveBase(workspace, baseFlag);\n rulings = (await checkRulings(workspace, brief, await briefText(workspace, brief), reader, base)).verified;\n } else {\n return first;\n }\n }\n return resolved.map(({ path, given: g }) => decide({ path, given: g, brief, noBrief, rulings, outOfScope: workspace.config.outOfScope }));\n}\n\n/* ----------------------------------------------------------------- context */\n\nfunction scopeBases(patterns: readonly string[]): string[] {\n const bases = new Set<string>();\n for (const pattern of patterns) {\n try {\n for (const base of compileGlob(pattern, { dialect: 'path', caseSensitive: true }).bases) bases.add(base === '' ? '.' : base);\n } catch {\n // spec-brief lint reports a pattern it cannot read; the rules for the rest still count.\n }\n }\n return [...bases].sort();\n}\n\nasync function rulesFor(workspace: Workspace, brief: BriefRow): Promise<Rules> {\n const paths = scopeBases(brief.affectedFiles);\n if (paths.length === 0) return [];\n try {\n const answer = await workspace.siblings.json('spec-guard', ['query', ...paths, '--json']);\n return 'absent' in answer ? { unavailable: answer.absent } : readRules(answer);\n } catch (error) {\n // spec-guard printed no JSON: what it said instead is the reason, and\n // the rest of the packet stands without the rules.\n if (error instanceof SiblingError) return { unavailable: error.message };\n throw error;\n }\n}\n\nasync function citedDocuments(workspace: Workspace, brief: BriefRow, text: string, reader: DocumentReader): Promise<CitedDocument[]> {\n const seen = new Set<string>();\n const out: CitedDocument[] = [];\n for (const citation of reader.citations(text)) {\n if (!isRelativeReference(citation.target)) continue;\n let target = splitReference(citation.target).path;\n try {\n target = decodeURIComponent(target);\n } catch {\n // A destination that is not valid percent-encoding is read as written.\n }\n const path = resolveInside(dirname(brief.file), target);\n if (path === null || path === '' || path === brief.file || seen.has(path)) continue;\n seen.add(path);\n const document = /\\.(?:md|markdown|mdx|txt)$/i.test(path);\n // A link to code or a directory that exists is not a document to include,\n // and must not be reported as a link that resolves to nothing.\n if (!document && existsSync(join(workspace.root, path))) continue;\n const content = document ? await readText(join(workspace.root, path)) : null;\n const meta = content === null ? { title: null, status: null } : reader.titleAndStatus(content);\n out.push({ path, title: meta.title, status: meta.status, text: content });\n }\n return out;\n}\n\nexport async function buildContext(\n workspace: Workspace,\n brief: BriefRow,\n briefs: readonly BriefRow[],\n reader: DocumentReader,\n baseFlag: string | undefined,\n): Promise<ContextPacket> {\n const text = await briefText(workspace, brief);\n const base = await resolveBase(workspace, baseFlag);\n const [rules, cited, rulings] = await Promise.all([\n rulesFor(workspace, brief),\n citedDocuments(workspace, brief, text, reader),\n checkRulings(workspace, brief, text, reader, base),\n ]);\n return renderContext({\n brief,\n briefText: text,\n dependencies: brief.dependsOn.map(\n (id) =>\n // spec-brief reports a dependency as its front matter spells it, `7` for brief 007.\n briefs.find((candidate) => sameId(candidate.id, id)) ?? {\n ...brief,\n id,\n title: '(no such brief)',\n file: '',\n phase: 'live',\n status: 'unknown',\n },\n ),\n cited,\n rules,\n rulings: rulings.verified,\n branch: workspace.branch,\n base: base.kind === 'resolved' ? base.ref : null,\n budget: workspace.config.context.budget,\n });\n}\n\n/* ------------------------------------------------------------------- audit */\n\nasync function dependencyChanges(workspace: Workspace, base: Base): Promise<{ changes: DependencyChange[]; unread: string[] }> {\n if (base.kind === 'unresolved') return { changes: [], unread: [] };\n const isManifest = manifestMatcher(workspace.config.dependencies.manifests);\n const out: DependencyChange[] = [];\n const unread: string[] = [];\n for (const change of await changes(base.mergeBase, base.head, workspace.root)) {\n if (!isManifest(change.path) && !(change.from !== undefined && isManifest(change.from))) continue;\n const ecosystem = ecosystemOf(change.path);\n if (ecosystem === null) {\n unread.push(change.path);\n continue;\n }\n const before = change.status === 'A' ? null : await show(base.mergeBase, change.from ?? change.path, workspace.root);\n const after = change.status === 'D' ? null : await show(base.head, change.path, workspace.root);\n const diff = diffManifest(change.path, ecosystem, before, after);\n if ('error' in diff) unread.push(change.path);\n else out.push(...diff.changes);\n }\n return { changes: out, unread };\n}\n\n/** Whether spec-brief loads this package's plugin, as its configuration at the root says. */\nexport async function specBriefPlugin(root: string): Promise<PluginState> {\n const file = SPEC_BRIEF_CONFIGS.find((name) => existsSync(join(root, name)));\n if (file === undefined) return { kind: 'unconfigured' };\n const config = await readJsonObject(join(root, file));\n if (config === null || config === 'unreadable') return { kind: 'unreadable', file };\n return { kind: loadsPlugin(config) ? 'loaded' : 'not-loaded', file };\n}\n\n/**\n * Claude Code's settings files that can turn a plugin on or hold a hook,\n * lowest precedence first: the user's, the project's, and the person's own\n * for the project. The user's is in `CLAUDE_CONFIG_DIR` when that is set,\n * otherwise in `.claude` under the home directory Claude Code uses,\n * `USERPROFILE` on Windows and `HOME` elsewhere; with neither it is not read.\n * Managed settings and `--settings` are not files a project can see.\n */\nexport function claudeSettingsFiles(root: string, env: CliIO['env'], platform: NodeJS.Platform = process.platform): { file: string; path: string }[] {\n const home = (platform === 'win32' ? env['USERPROFILE'] : env['HOME']) || undefined;\n const configDir = env['CLAUDE_CONFIG_DIR'] || (home === undefined ? undefined : join(home, '.claude'));\n const user = configDir === undefined ? null : join(configDir, 'settings.json');\n return [\n ...(user === null ? [] : [{ file: user, path: user }]),\n { file: '.claude/settings.json', path: join(root, '.claude', 'settings.json') },\n { file: '.claude/settings.local.json', path: join(root, '.claude', 'settings.local.json') },\n ];\n}\n\n/** The settings files Claude Code reads here, as read; one that is missing or cannot be read as JSON holds nothing. */\nexport async function claudeSettings(root: string, env: CliIO['env']): Promise<ClaudeSettings[]> {\n return Promise.all(claudeSettingsFiles(root, env).map(async ({ file, path }) => ({ file, settings: await readJsonObject(path) })));\n}\n\n/** Whether the plugin, init's entries, both or neither wire Claude Code to the harness here. */\nexport async function claudeCodeWiring(root: string, env: CliIO['env']): Promise<ClaudeCodeWiring> {\n const sources = await claudeSettings(root, env);\n return {\n plugin: enabledPlugin(sources),\n hooks: sources.filter(({ settings }) => holdsGuard(settings)).map(({ file }) => file),\n server: registersServer(await readJsonObject(join(root, '.mcp.json'))),\n };\n}\n\nasync function archiveReasons(workspace: Workspace, brief: BriefRow, base: Base): Promise<{ blocking: ArchiveReason[]; warnings: ArchiveReason[] } | { unavailable: string }> {\n const args = ['archive', brief.id, '--dry-run', '--format', 'json', '--no-color'];\n if (base.kind === 'resolved') args.push('--base', base.ref);\n const answer = await workspace.siblings.json('spec-brief', args);\n if ('absent' in answer) return { unavailable: answer.absent };\n if (answer.code === 2) return { unavailable: 'spec-brief could not plan the archive' };\n const plan = (answer.document as { plan?: { blocking?: ArchiveReason[]; warnings?: ArchiveReason[] } }).plan;\n return { blocking: plan?.blocking ?? [], warnings: plan?.warnings ?? [] };\n}\n\nasync function briefAssertions(workspace: Workspace, brief: BriefRow, text: string, reader: DocumentReader): Promise<AssertionOutcome[] | { unavailable: string }> {\n const answer = await workspace.siblings.json('spec-guard', [brief.file, '--ignore-status', '--json']);\n if ('absent' in answer) return { unavailable: answer.absent };\n if (answer.code === 2) return { unavailable: 'spec-guard could not run the brief\\'s assertions' };\n const report = answer.document as { results?: { ok: boolean; description: string; message: string; spec?: { file: string; line: number } }[] };\n return (report.results ?? [])\n .filter((result) => result.spec === undefined || result.spec.file.replace(/\\\\/g, '/') === brief.file)\n .map((result) => {\n const line = result.spec?.line ?? 0;\n return { ok: result.ok, description: result.description, message: result.message, line, section: reader.sectionAt(text, line), enclosing: reader.sectionsAt(text, line) };\n });\n}\n\nexport interface AuditResult {\n readonly brief: BriefRow;\n readonly base: Base;\n readonly report: AuditReport;\n readonly dependencies: readonly DependencyChange[];\n}\n\nexport async function runAudit(workspace: Workspace, brief: BriefRow, reader: DocumentReader, baseFlag: string | undefined): Promise<AuditResult> {\n const base = await resolveBase(workspace, baseFlag);\n const text = await briefText(workspace, brief);\n const [dependencies, archive, assertions, rulings, plugin] = await Promise.all([\n dependencyChanges(workspace, base),\n archiveReasons(workspace, brief, base),\n briefAssertions(workspace, brief, text, reader),\n checkRulings(workspace, brief, text, reader, base),\n specBriefPlugin(workspace.root),\n ]);\n const unmeasured =\n base.kind === 'unresolved' ? base.reason : base.mergeBase === base.head ? `${base.ref} and HEAD are the same commit: there is nothing to measure` : null;\n const report = audit({\n brief,\n unmeasured,\n dependencies,\n archive,\n assertions,\n premiseSections: workspace.config.assertions.premises,\n unverifiedRulings: rulings.unverified,\n verifiedRulings: rulings.verified,\n pluginLoaded: plugin.kind === 'loaded',\n });\n return {\n brief,\n base,\n dependencies: dependencies.changes,\n report: { findings: [...rulings.problems, ...report.findings], counts: tally([...rulings.problems, ...report.findings]) },\n };\n}\n\nfunction tally(findings: readonly Finding[]): AuditReport['counts'] {\n const counts = { error: 0, warning: 0, note: 0 };\n for (const finding of findings) counts[finding.severity] += 1;\n return counts;\n}\n\n/* -------------------------------------------------------------- escalation */\n\nasync function escalationDirectory(workspace: Workspace): Promise<string> {\n return stateDirectory(workspace.commonDir, 'escalations');\n}\n\nexport async function listEscalations(workspace: Workspace): Promise<EscalationRequest[]> {\n const directory = await escalationDirectory(workspace);\n const out: EscalationRequest[] = [];\n for (const name of (await readdir(directory)).filter((n) => n.endsWith('.json')).sort()) {\n try {\n out.push(JSON.parse(await readFile(join(directory, name), 'utf8')) as EscalationRequest);\n } catch {\n // A request that cannot be read is skipped; listing it would list nothing true.\n }\n }\n return out;\n}\n\nexport async function raiseEscalation(\n workspace: Workspace,\n brief: BriefRow,\n request: { paths: readonly string[]; reason: string; options: readonly EscalationOption[]; recommendation: string | null },\n): Promise<{ request: EscalationRequest; memo: string }> {\n const existing = await listEscalations(workspace);\n const id = nextId('E', brief.id, existing.map((e) => e.id));\n const full: EscalationRequest = {\n id,\n brief: brief.id,\n briefFile: brief.file,\n paths: request.paths,\n reason: request.reason,\n options: request.options,\n recommendation: request.recommendation,\n created: new Date().toISOString(),\n branch: workspace.branch,\n head: await revision('HEAD', workspace.root),\n };\n await writeAtomic(join(await escalationDirectory(workspace), `${id}.json`), `${JSON.stringify(full, null, 2)}\\n`);\n return { request: full, memo: renderMemo(full) };\n}\n\n/** Writes a person's ruling into the brief and retires the request. Never commits: the signature is the person's. */\nexport async function recordRuling(\n workspace: Workspace,\n escalationId: string,\n decision: 'allow' | 'deny',\n note: string,\n reader: DocumentReader,\n): Promise<{ id: string; file: string; row: string }> {\n const request = (await listEscalations(workspace)).find((candidate) => candidate.id === escalationId.toUpperCase() || candidate.id === escalationId);\n if (request === undefined) throw new UsageError(`no escalation \"${escalationId}\" is waiting; spec-harness escalate --list shows those that are`);\n const path = join(workspace.root, request.briefFile);\n const text = await readText(path);\n if (text === null) throw new UsageError(`${request.briefFile} cannot be read`);\n const where = reader.sectionTables(text, workspace.config.rulings.section);\n const taken = readRulings(where.tables).rulings.map((row) => row.id);\n const id = nextId('R', request.brief, taken);\n const row = renderRow(id, request.paths, decision, note);\n await writeAtomic(path, addRulingRow(text, workspace.config.rulings.section, row, where));\n await rm(join(await escalationDirectory(workspace), `${request.id}.json`), { force: true });\n return { id, file: request.briefFile, row };\n}\n"]}
1
+ {"version":3,"file":"round.js","sourceRoot":"","sources":["../src/round.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A round, measured: the operations the command line and the MCP server\n * share. Each gathers what a pure module needs - from git, the disk and the\n * siblings - and hands it over; the decisions are made in `context.ts`,\n * `audit.ts`, `guard.ts` and `rulings.ts`.\n */\n\nimport { createHash } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { readdir, readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { audit, type ArchiveReason, type AssertionOutcome, type AuditReport } from './audit.js';\nimport { sameId } from './branch.js';\nimport {\n enabledPlugin,\n holdsGuard,\n loadsPlugin,\n registersServer,\n SPEC_BRIEF_CONFIGS,\n type BaseSource,\n type ClaudeCodeWiring,\n type ClaudeSettings,\n type PluginState,\n} from './configure.js';\nimport { readRules, renderContext, type CitedDocument, type ContextPacket, type Rules } from './context.js';\nimport type { DocumentReader } from './document.js';\nimport { readJsonObject, readText, repositoryPath, stateDirectory, writeAtomic } from './fs.js';\nimport { blameLine, changes, mergeBase, remoteDefault, revision, show, verifyCommit } from './git.js';\nimport { decide, type Decision, type VerifiedRuling } from './guard.js';\nimport { diffManifest, ecosystemOf, manifestMatcher, type DependencyChange } from './manifests.js';\nimport {\n addRulingRow,\n nextId,\n readRulings,\n renderMemo,\n renderRow,\n type EscalationOption,\n type EscalationRequest,\n type RulingRow,\n} from './rulings.js';\nimport type { BriefRow, Finding } from './types.js';\nimport { compileGlob } from './vendor/spec-core/pattern/index.js';\nimport { dirname, isRelativeReference, resolveInside, splitReference } from './vendor/spec-core/path/index.js';\nimport { SiblingError } from './siblings.js';\nimport { UsageError, type CliIO, type Workspace } from './workspace.js';\n\n/* -------------------------------------------------------------------- base */\n\nexport type Base =\n | { readonly kind: 'resolved'; readonly ref: string; readonly source: BaseSource; readonly sha: string; readonly mergeBase: string; readonly head: string }\n | { readonly kind: 'unresolved'; readonly reason: string };\n\n/** What the round is measured from: the flag, the configuration, then the remote's default branch. */\nexport async function resolveBase(workspace: Workspace, flag: string | undefined): Promise<Base> {\n const source: BaseSource = flag !== undefined ? 'flag' : workspace.config.base !== null ? 'config' : 'remote';\n const ref = flag ?? workspace.config.base ?? (await remoteDefault(workspace.root));\n if (ref === null) {\n return { kind: 'unresolved', reason: 'no base is named and the remote has no default branch; pass --base <ref> or set \"base\"' };\n }\n const sha = await revision(ref, workspace.root);\n const head = await revision('HEAD', workspace.root);\n if (sha === null || head === null) return { kind: 'unresolved', reason: `\"${ref}\" names no commit` };\n const common = await mergeBase(sha, head, workspace.root);\n if (common === null) return { kind: 'unresolved', reason: `\"${ref}\" and HEAD share no history` };\n return { kind: 'resolved', ref, source, sha, mergeBase: common, head };\n}\n\n/* ----------------------------------------------------------------- rulings */\n\nexport interface RulingCheck {\n readonly rows: readonly RulingRow[];\n readonly verified: readonly VerifiedRuling[];\n readonly unverified: readonly { readonly id: string; readonly reason: string }[];\n readonly problems: readonly Finding[];\n}\n\n/**\n * The brief's rulings, each checked against its signature: the commit that\n * last changed its row must be signed by a principal the base branch's\n * allowed-signers file lists (ADR-0006).\n */\nexport async function checkRulings(workspace: Workspace, brief: BriefRow, text: string, reader: DocumentReader, base: Base): Promise<RulingCheck> {\n const { rulings: rows, problems } = readRulings(reader.sectionTables(text, workspace.config.rulings.section).tables);\n const findings: Finding[] = problems.map((problem) => ({\n rule: 'ruling-unreadable',\n severity: 'warning',\n message: problem.message,\n hint: 'a ruling row is | id | `paths` | allow or deny | note |',\n file: brief.file,\n line: problem.line,\n }));\n const allows = rows.filter((row) => row.decision === 'allow');\n if (allows.length === 0) return { rows, verified: [], unverified: [], problems: findings };\n if (base.kind === 'unresolved') {\n return { rows, verified: [], unverified: allows.map((row) => ({ id: row.id, reason: `no base to read the allowed signers from: ${base.reason}` })), problems: findings };\n }\n const signers = await show(base.sha, workspace.config.rulings.allowedSigners, workspace.root);\n if (signers === null) {\n const reason = `${base.ref} has no ${workspace.config.rulings.allowedSigners}, so no signature can count`;\n return { rows, verified: [], unverified: allows.map((row) => ({ id: row.id, reason })), problems: findings };\n }\n const directory = await stateDirectory(workspace.commonDir, 'signers');\n const file = join(directory, `${createHash('sha256').update(signers).digest('hex').slice(0, 16)}`);\n await writeAtomic(file, signers);\n const verified: VerifiedRuling[] = [];\n const unverified: { id: string; reason: string }[] = [];\n for (const row of allows) {\n // The row's line is a line of the text on disk, so it is blamed there: at\n // HEAD the same number can be another line, one a signed commit wrote,\n // and an uncommitted row would borrow that commit's signature.\n const commit = await blameLine(null, brief.file, row.line, workspace.root);\n if (commit === null || /^0+$/.test(commit)) {\n unverified.push({ id: row.id, reason: 'its row is not committed' });\n continue;\n }\n const signature = await verifyCommit(commit, file, workspace.root);\n if (signature.good && signature.principal !== null) verified.push({ id: row.id, paths: row.paths, signer: signature.principal });\n else unverified.push({ id: row.id, reason: `commit ${commit.slice(0, 12)} last changed its row, and ${signature.detail || 'it is not signed'}` });\n }\n return { rows, verified, unverified, problems: findings };\n}\n\n/* ------------------------------------------------------------------- guard */\n\nexport async function briefText(workspace: Workspace, brief: BriefRow): Promise<string> {\n const text = await readText(join(workspace.root, brief.file));\n if (text === null) throw new UsageError(`${brief.file} cannot be read`);\n return text;\n}\n\n/** Decides paths against the brief, with the rulings verified against the base `baseFlag` names, or the configured one. */\nexport async function checkPaths(\n workspace: Workspace,\n brief: BriefRow | null,\n noBrief: string | undefined,\n given: readonly string[],\n cwd: string,\n reader: DocumentReader,\n baseFlag: string | undefined,\n): Promise<Decision[]> {\n let rulings: readonly VerifiedRuling[] = [];\n const resolved = given.map((path) => ({ given: path, path: repositoryPath(path, workspace.root, cwd) }));\n if (brief !== null) {\n // Rulings cost a blame and a signature check each, so they are read only\n // when a write reaches a protected file.\n const first = resolved.map(({ path, given: g }) =>\n decide({ path, given: g, brief, noBrief, rulings: [], outOfScope: workspace.config.outOfScope }),\n );\n if (first.some((decision) => decision.reason === 'protected')) {\n const base = await resolveBase(workspace, baseFlag);\n rulings = (await checkRulings(workspace, brief, await briefText(workspace, brief), reader, base)).verified;\n } else {\n return first;\n }\n }\n return resolved.map(({ path, given: g }) => decide({ path, given: g, brief, noBrief, rulings, outOfScope: workspace.config.outOfScope }));\n}\n\n/* ----------------------------------------------------------------- context */\n\nfunction scopeBases(patterns: readonly string[]): string[] {\n const bases = new Set<string>();\n for (const pattern of patterns) {\n try {\n for (const base of compileGlob(pattern, { dialect: 'path', caseSensitive: true }).bases) bases.add(base === '' ? '.' : base);\n } catch {\n // spec-brief lint reports a pattern it cannot read; the rules for the rest still count.\n }\n }\n return [...bases].sort();\n}\n\nasync function rulesFor(workspace: Workspace, brief: BriefRow): Promise<Rules> {\n const paths = scopeBases(brief.affectedFiles);\n if (paths.length === 0) return [];\n try {\n const answer = await workspace.siblings.json('spec-guard', ['query', ...paths, '--json']);\n return 'absent' in answer ? { unavailable: answer.absent } : readRules(answer);\n } catch (error) {\n // spec-guard printed no JSON: what it said instead is the reason, and\n // the rest of the packet stands without the rules.\n if (error instanceof SiblingError) return { unavailable: error.message };\n throw error;\n }\n}\n\nasync function citedDocuments(workspace: Workspace, brief: BriefRow, text: string, reader: DocumentReader): Promise<CitedDocument[]> {\n const seen = new Set<string>();\n const out: CitedDocument[] = [];\n for (const citation of reader.citations(text)) {\n if (!isRelativeReference(citation.target)) continue;\n let target = splitReference(citation.target).path;\n try {\n target = decodeURIComponent(target);\n } catch {\n // A destination that is not valid percent-encoding is read as written.\n }\n const path = resolveInside(dirname(brief.file), target);\n if (path === null || path === '' || path === brief.file || seen.has(path)) continue;\n seen.add(path);\n const document = /\\.(?:md|markdown|mdx|txt)$/i.test(path);\n // A link to code or a directory that exists is not a document to include,\n // and must not be reported as a link that resolves to nothing.\n if (!document && existsSync(join(workspace.root, path))) continue;\n const content = document ? await readText(join(workspace.root, path)) : null;\n const meta = content === null ? { title: null, status: null } : reader.titleAndStatus(content);\n out.push({ path, ...meta, text: content });\n }\n return out;\n}\n\nexport async function buildContext(\n workspace: Workspace,\n brief: BriefRow,\n briefs: readonly BriefRow[],\n reader: DocumentReader,\n baseFlag: string | undefined,\n): Promise<ContextPacket> {\n const text = await briefText(workspace, brief);\n const base = await resolveBase(workspace, baseFlag);\n const [rules, cited, rulings] = await Promise.all([\n rulesFor(workspace, brief),\n citedDocuments(workspace, brief, text, reader),\n checkRulings(workspace, brief, text, reader, base),\n ]);\n return renderContext({\n brief,\n briefText: text,\n dependencies: brief.dependsOn.map(\n (id) =>\n // spec-brief reports a dependency as its front matter spells it, `7` for brief 007.\n briefs.find((candidate) => sameId(candidate.id, id)) ?? {\n ...brief,\n id,\n title: '(no such brief)',\n file: '',\n phase: 'live',\n status: 'unknown',\n },\n ),\n cited,\n rules,\n rulings: rulings.verified,\n branch: workspace.branch,\n base: base.kind === 'resolved' ? base.ref : null,\n budget: workspace.config.context.budget,\n });\n}\n\n/* ------------------------------------------------------------------- audit */\n\nasync function dependencyChanges(workspace: Workspace, base: Base): Promise<{ changes: DependencyChange[]; unread: string[] }> {\n if (base.kind === 'unresolved') return { changes: [], unread: [] };\n const isManifest = manifestMatcher(workspace.config.dependencies.manifests);\n const out: DependencyChange[] = [];\n const unread: string[] = [];\n for (const change of await changes(base.mergeBase, base.head, workspace.root)) {\n if (!isManifest(change.path) && !(change.from !== undefined && isManifest(change.from))) continue;\n const ecosystem = ecosystemOf(change.path);\n if (ecosystem === null) {\n unread.push(change.path);\n continue;\n }\n const before = change.status === 'A' ? null : await show(base.mergeBase, change.from ?? change.path, workspace.root);\n const after = change.status === 'D' ? null : await show(base.head, change.path, workspace.root);\n const diff = diffManifest(change.path, ecosystem, before, after);\n if ('error' in diff) unread.push(change.path);\n else out.push(...diff.changes);\n }\n return { changes: out, unread };\n}\n\n/** Whether spec-brief loads this package's plugin, as its configuration at the root says. */\nexport async function specBriefPlugin(root: string): Promise<PluginState> {\n const file = SPEC_BRIEF_CONFIGS.find((name) => existsSync(join(root, name)));\n if (file === undefined) return { kind: 'unconfigured' };\n const config = await readJsonObject(join(root, file));\n if (config === null || config === 'unreadable') return { kind: 'unreadable', file };\n return { kind: loadsPlugin(config) ? 'loaded' : 'not-loaded', file };\n}\n\n/**\n * Claude Code's settings files that can turn a plugin on or hold a hook,\n * lowest precedence first: the user's, the project's, and the person's own\n * for the project. The user's is in `CLAUDE_CONFIG_DIR` when that is set,\n * otherwise in `.claude` under the home directory Claude Code uses,\n * `USERPROFILE` on Windows and `HOME` elsewhere; with neither it is not read.\n * Managed settings and `--settings` are not files a project can see.\n */\nexport function claudeSettingsFiles(root: string, env: CliIO['env'], platform: NodeJS.Platform = process.platform): { file: string; path: string }[] {\n const home = (platform === 'win32' ? env['USERPROFILE'] : env['HOME']) || undefined;\n const configDir = env['CLAUDE_CONFIG_DIR'] || (home === undefined ? undefined : join(home, '.claude'));\n const user = configDir === undefined ? null : join(configDir, 'settings.json');\n return [\n ...(user === null ? [] : [{ file: user, path: user }]),\n { file: '.claude/settings.json', path: join(root, '.claude', 'settings.json') },\n { file: '.claude/settings.local.json', path: join(root, '.claude', 'settings.local.json') },\n ];\n}\n\n/** The settings files Claude Code reads here, as read; one that is missing or cannot be read as JSON holds nothing. */\nexport async function claudeSettings(root: string, env: CliIO['env']): Promise<ClaudeSettings[]> {\n return Promise.all(claudeSettingsFiles(root, env).map(async ({ file, path }) => ({ file, settings: await readJsonObject(path) })));\n}\n\n/** Whether the plugin, init's entries, both or neither wire Claude Code to the harness here. */\nexport async function claudeCodeWiring(root: string, env: CliIO['env']): Promise<ClaudeCodeWiring> {\n const sources = await claudeSettings(root, env);\n return {\n plugin: enabledPlugin(sources),\n hooks: sources.filter(({ settings }) => holdsGuard(settings)).map(({ file }) => file),\n server: registersServer(await readJsonObject(join(root, '.mcp.json'))),\n };\n}\n\nasync function archiveReasons(workspace: Workspace, brief: BriefRow, base: Base): Promise<{ blocking: ArchiveReason[]; warnings: ArchiveReason[] } | { unavailable: string }> {\n const args = ['archive', brief.id, '--dry-run', '--format', 'json', '--no-color'];\n if (base.kind === 'resolved') args.push('--base', base.ref);\n const answer = await workspace.siblings.json('spec-brief', args);\n if ('absent' in answer) return { unavailable: answer.absent };\n if (answer.code === 2) return { unavailable: 'spec-brief could not plan the archive' };\n const plan = (answer.document as { plan?: { blocking?: ArchiveReason[]; warnings?: ArchiveReason[] } }).plan;\n return { blocking: plan?.blocking ?? [], warnings: plan?.warnings ?? [] };\n}\n\nasync function briefAssertions(workspace: Workspace, brief: BriefRow, text: string, reader: DocumentReader): Promise<AssertionOutcome[] | { unavailable: string }> {\n const answer = await workspace.siblings.json('spec-guard', [brief.file, '--ignore-status', '--json']);\n if ('absent' in answer) return { unavailable: answer.absent };\n if (answer.code === 2) return { unavailable: 'spec-guard could not run the brief\\'s assertions' };\n const report = answer.document as { results?: { ok: boolean; description: string; message: string; spec?: { file: string; line: number } }[] };\n return (report.results ?? [])\n .filter((result) => result.spec === undefined || result.spec.file.replace(/\\\\/g, '/') === brief.file)\n .map((result) => {\n const line = result.spec?.line ?? 0;\n return { ok: result.ok, description: result.description, message: result.message, line, section: reader.sectionAt(text, line), enclosing: reader.sectionsAt(text, line) };\n });\n}\n\nexport interface AuditResult {\n readonly brief: BriefRow;\n readonly base: Base;\n readonly report: AuditReport;\n readonly dependencies: readonly DependencyChange[];\n}\n\nexport async function runAudit(workspace: Workspace, brief: BriefRow, reader: DocumentReader, baseFlag: string | undefined): Promise<AuditResult> {\n const base = await resolveBase(workspace, baseFlag);\n const text = await briefText(workspace, brief);\n const [dependencies, archive, assertions, rulings, plugin] = await Promise.all([\n dependencyChanges(workspace, base),\n archiveReasons(workspace, brief, base),\n briefAssertions(workspace, brief, text, reader),\n checkRulings(workspace, brief, text, reader, base),\n specBriefPlugin(workspace.root),\n ]);\n const unmeasured =\n base.kind === 'unresolved' ? base.reason : base.mergeBase === base.head ? `${base.ref} and HEAD are the same commit: there is nothing to measure` : null;\n const report = audit({\n brief,\n unmeasured,\n dependencies,\n archive,\n assertions,\n premiseSections: workspace.config.assertions.premises,\n unverifiedRulings: rulings.unverified,\n verifiedRulings: rulings.verified,\n pluginLoaded: plugin.kind === 'loaded',\n });\n return {\n brief,\n base,\n dependencies: dependencies.changes,\n report: { findings: [...rulings.problems, ...report.findings], counts: tally([...rulings.problems, ...report.findings]) },\n };\n}\n\nfunction tally(findings: readonly Finding[]): AuditReport['counts'] {\n const counts = { error: 0, warning: 0, note: 0 };\n for (const finding of findings) counts[finding.severity] += 1;\n return counts;\n}\n\n/* -------------------------------------------------------------- escalation */\n\nasync function escalationDirectory(workspace: Workspace): Promise<string> {\n return stateDirectory(workspace.commonDir, 'escalations');\n}\n\nexport async function listEscalations(workspace: Workspace): Promise<EscalationRequest[]> {\n const directory = await escalationDirectory(workspace);\n const out: EscalationRequest[] = [];\n for (const name of (await readdir(directory)).filter((n) => n.endsWith('.json')).sort()) {\n try {\n out.push(JSON.parse(await readFile(join(directory, name), 'utf8')) as EscalationRequest);\n } catch {\n // A request that cannot be read is skipped; listing it would list nothing true.\n }\n }\n return out;\n}\n\nexport async function raiseEscalation(\n workspace: Workspace,\n brief: BriefRow,\n request: { paths: readonly string[]; reason: string; options: readonly EscalationOption[]; recommendation: string | null },\n): Promise<{ request: EscalationRequest; memo: string }> {\n const existing = await listEscalations(workspace);\n const id = nextId('E', brief.id, existing.map((e) => e.id));\n const full: EscalationRequest = {\n id,\n brief: brief.id,\n briefFile: brief.file,\n paths: request.paths,\n reason: request.reason,\n options: request.options,\n recommendation: request.recommendation,\n created: new Date().toISOString(),\n branch: workspace.branch,\n head: await revision('HEAD', workspace.root),\n };\n await writeAtomic(join(await escalationDirectory(workspace), `${id}.json`), `${JSON.stringify(full, null, 2)}\\n`);\n return { request: full, memo: renderMemo(full) };\n}\n\n/** Writes a person's ruling into the brief and retires the request. Never commits: the signature is the person's. */\nexport async function recordRuling(\n workspace: Workspace,\n escalationId: string,\n decision: 'allow' | 'deny',\n note: string,\n reader: DocumentReader,\n): Promise<{ id: string; file: string; row: string }> {\n const request = (await listEscalations(workspace)).find((candidate) => candidate.id === escalationId.toUpperCase() || candidate.id === escalationId);\n if (request === undefined) throw new UsageError(`no escalation \"${escalationId}\" is waiting; spec-harness escalate --list shows those that are`);\n const path = join(workspace.root, request.briefFile);\n const text = await readText(path);\n if (text === null) throw new UsageError(`${request.briefFile} cannot be read`);\n const where = reader.sectionTables(text, workspace.config.rulings.section);\n const taken = readRulings(where.tables).rulings.map((row) => row.id);\n const id = nextId('R', request.brief, taken);\n const row = renderRow(id, request.paths, decision, note);\n await writeAtomic(path, addRulingRow(text, workspace.config.rulings.section, row, where));\n await rm(join(await escalationDirectory(workspace), `${request.id}.json`), { force: true });\n return { id, file: request.briefFile, row };\n}\n"]}
package/dist/sandbox.d.ts CHANGED
@@ -18,7 +18,9 @@ export interface CommandRun {
18
18
  }
19
19
  /**
20
20
  * Runs a command line in a directory, output and errors interleaved, stopped
21
- * with everything it started when it outlives `timeoutSeconds`.
21
+ * with everything it started when it outlives `timeoutSeconds` or the process
22
+ * is interrupted or exits. Once the process is interrupted it never answers:
23
+ * the job waiting on it would otherwise carry on in a worktree being removed.
22
24
  *
23
25
  * Through the shell, on purpose: a probe's command is a line from the
24
26
  * repository's own brief - `npm test -- x` - which the person approved with
package/dist/sandbox.js CHANGED
@@ -11,41 +11,134 @@
11
11
  */
12
12
  import { execFileSync, spawn } from 'node:child_process';
13
13
  import { mkdtempSync, rmSync } from 'node:fs';
14
- import { tmpdir } from 'node:os';
14
+ import { constants, tmpdir } from 'node:os';
15
15
  import { join } from 'node:path';
16
16
  import { addWorktree, removeWorktree } from './git.js';
17
17
  const live = new Map();
18
+ const running = new Set();
18
19
  let installed = false;
19
- /** Removes every live worktree synchronously: the only kind of work an exit handler may do. */
20
- function sweep() {
21
- for (const [directory, repository] of live) {
20
+ /** Set once the process is interrupted: from then on a command's end is never answered. */
21
+ let ending = false;
22
+ /**
23
+ * How long the sandbox waits for the commands it stopped to let go of their
24
+ * output and their directory. They were stopped forced, so this is only the
25
+ * time the system takes to end them; whatever still holds either after it has
26
+ * left the tree, and no wait would end it.
27
+ */
28
+ const SETTLE_MS = 3_000;
29
+ /**
30
+ * Stops a command with everything it started, forced: SIGKILL to its process
31
+ * group on Linux and macOS, taskkill over its tree on Windows.
32
+ *
33
+ * `windowsHide` here and below only keeps a console window from opening on
34
+ * Windows, which nothing reads, so its mutants are equivalent.
35
+ */
36
+ function stop(child) {
37
+ // No pid means the spawn failed and nothing runs. Past the tick it failed
38
+ // in, the kill below throws on the missing id and the fallback finds no
39
+ // process, so the mutant that lets it through is equivalent; within that
40
+ // tick nothing here stops a command.
41
+ if (child.pid === undefined)
42
+ return;
43
+ try {
44
+ // False already on Linux and macOS, so the mutants that make it false
45
+ // are equivalent there. On Windows the tests fail without taskkill:
46
+ // stopping the shell alone leaves node holding the output. True already
47
+ // on Windows, so the mutant that makes it true is equivalent there;
48
+ // elsewhere there is no taskkill, and the shell alone is stopped.
49
+ if (process.platform === 'win32')
50
+ execFileSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true });
51
+ else
52
+ process.kill(-child.pid, 'SIGKILL');
53
+ }
54
+ catch {
55
+ // Reached when the group or the tree is already gone, and the command's
56
+ // shell with it, as when the command left something holding its output;
57
+ // and on Windows when taskkill cannot be run, where stopping the shell is
58
+ // what is left. No test takes that second path.
59
+ child.kill('SIGKILL');
60
+ }
61
+ }
62
+ /**
63
+ * Deletes a worktree's directory, trying again for up to `SETTLE_MS` while
64
+ * something holds it. On Windows a command just stopped lets go of its
65
+ * directory a moment after taskkill returns, and an exit handler, which runs
66
+ * synchronously, has no other way to wait; elsewhere a directory in use is
67
+ * deleted all the same.
68
+ */
69
+ function discard(directory) {
70
+ const deadline = Date.now() + SETTLE_MS;
71
+ for (;;) {
22
72
  try {
23
- execFileSync('git', ['worktree', 'remove', '--force', directory], { cwd: repository, stdio: 'ignore', windowsHide: true });
73
+ rmSync(directory, { recursive: true, force: true });
74
+ return;
24
75
  }
25
- catch {
26
- // Removed below regardless; `git worktree prune` then forgets it.
76
+ catch (error) {
77
+ // `>=` would differ only in the millisecond the deadline falls on, so
78
+ // that mutant is equivalent.
79
+ if (Date.now() > deadline)
80
+ throw error;
81
+ // The pause only spares the processor between attempts: without it
82
+ // the directory is deleted, or given up on, at the same moment, so
83
+ // that mutant is equivalent.
84
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);
27
85
  }
28
- rmSync(directory, { recursive: true, force: true });
86
+ }
87
+ }
88
+ /**
89
+ * Stops every command still running, then removes every live worktree:
90
+ * synchronously, the only kind of work an exit handler may do, so at exit
91
+ * nothing waits for the commands to end but `discard`'s retries.
92
+ */
93
+ function sweep() {
94
+ for (const child of running)
95
+ stop(child);
96
+ for (const [directory, repository] of live) {
97
+ // Deleted first, as at the end of a job, so that git forgets it however
98
+ // the job left it (`removeWorktree` says why).
99
+ discard(directory);
29
100
  try {
30
- execFileSync('git', ['worktree', 'prune'], { cwd: repository, stdio: 'ignore', windowsHide: true });
101
+ execFileSync('git', ['worktree', 'remove', '--force', directory], { cwd: repository, stdio: 'ignore', windowsHide: true });
31
102
  }
32
103
  catch {
33
- // Nothing more can be done from an exit handler.
104
+ // Git holds nothing of it any more, or cannot be run; nothing more can
105
+ // be done from an exit handler.
34
106
  }
35
107
  }
108
+ // A signal handler ends in an exit, which sweeps again over worktrees
109
+ // already gone and forgotten, so a mutant that keeps them changes nothing.
36
110
  live.clear();
37
111
  }
112
+ /**
113
+ * Stops every running command with its tree, waits a bounded time for them to
114
+ * let go of their output, removes the worktrees, and exits as a shell reports
115
+ * a process the signal ended: 128 and the signal's number, the same on every
116
+ * platform Node runs on. The commands lead process groups of their own on
117
+ * Linux and macOS, so the terminal's signal never reached them.
118
+ */
119
+ async function interrupted(signal) {
120
+ ending = true;
121
+ const stopping = [...running];
122
+ for (const child of stopping)
123
+ stop(child);
124
+ await Promise.race([
125
+ Promise.all(stopping.map((child) => new Promise((resolve) => child.once('close', resolve)))),
126
+ new Promise((resolve) => setTimeout(resolve, SETTLE_MS)),
127
+ ]);
128
+ sweep();
129
+ process.exit(128 + constants.signals[signal]);
130
+ }
38
131
  function install() {
39
132
  if (installed)
40
133
  return;
41
134
  installed = true;
42
135
  process.once('exit', sweep);
43
- for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
44
- process.once(signal, () => {
45
- sweep();
46
- process.exit(signal === 'SIGINT' ? 130 : 143);
47
- });
48
- }
136
+ // `on`, not `once`: a second signal while the first waits would otherwise
137
+ // meet Node's default, which ends the process before any 'exit' handler
138
+ // runs, with the worktrees still in place. It runs the same steps again,
139
+ // and the first to finish exits.
140
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'])
141
+ process.on(signal, interrupted);
49
142
  }
50
143
  /** Runs `work` in a detached worktree of `repository` at `revision`, and removes the worktree after. */
51
144
  export async function withWorktree(repository, revision, work) {
@@ -57,14 +150,16 @@ export async function withWorktree(repository, revision, work) {
57
150
  return await work(directory);
58
151
  }
59
152
  finally {
153
+ discard(directory);
60
154
  await removeWorktree(directory, repository);
61
- rmSync(directory, { recursive: true, force: true });
62
155
  live.delete(directory);
63
156
  }
64
157
  }
65
158
  /**
66
159
  * Runs a command line in a directory, output and errors interleaved, stopped
67
- * with everything it started when it outlives `timeoutSeconds`.
160
+ * with everything it started when it outlives `timeoutSeconds` or the process
161
+ * is interrupted or exits. Once the process is interrupted it never answers:
162
+ * the job waiting on it would otherwise carry on in a worktree being removed.
68
163
  *
69
164
  * Through the shell, on purpose: a probe's command is a line from the
70
165
  * repository's own brief - `npm test -- x` - which the person approved with
@@ -72,38 +167,42 @@ export async function withWorktree(repository, revision, work) {
72
167
  * outside the repository reaches it.
73
168
  */
74
169
  export function runCommand(line, cwd, timeoutSeconds) {
170
+ install();
75
171
  return new Promise((resolve) => {
172
+ // Elsewhere the command leads a process group of its own, so the timeout
173
+ // can stop the group. Windows has none to signal, and taskkill follows
174
+ // the tree instead; a command detached there loses its output, which the
175
+ // tests catch on Windows. Everywhere else `detached` is true already, so
176
+ // the mutants that make it true are equivalent there, as are
177
+ // `windowsHide`'s.
76
178
  const child = spawn(line, { cwd, shell: true, windowsHide: true, detached: process.platform !== 'win32', env: { ...process.env, CI: '1' } });
179
+ running.add(child);
77
180
  let output = '';
78
181
  const keep = (chunk) => {
79
182
  // Enough to find a signature in; a runaway log is not kept whole.
80
183
  if (output.length < 4 * 1024 * 1024)
81
184
  output += chunk.toString('utf8');
82
185
  };
186
+ // Both streams are piped, so both exist: `?.` only satisfies the type,
187
+ // and its mutants are equivalent.
83
188
  child.stdout?.on('data', keep);
84
189
  child.stderr?.on('data', keep);
85
190
  let stopped = false;
86
191
  const timer = setTimeout(() => {
87
192
  stopped = true;
88
- if (child.pid === undefined)
89
- return;
90
- try {
91
- if (process.platform === 'win32')
92
- execFileSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true });
93
- else
94
- process.kill(-child.pid, 'SIGKILL');
95
- }
96
- catch {
97
- child.kill('SIGKILL');
98
- }
193
+ stop(child);
99
194
  }, timeoutSeconds * 1000);
100
195
  child.on('error', (error) => {
196
+ // 'close' follows the error of a spawn that failed, and clears the
197
+ // timer too, so the mutant that drops this call is equivalent.
101
198
  clearTimeout(timer);
102
199
  resolve({ exitCode: 127, output: `${output}${error.message}\n` });
103
200
  });
104
201
  child.on('close', (code) => {
105
202
  clearTimeout(timer);
106
- resolve({ exitCode: stopped ? null : (code ?? 1), output });
203
+ running.delete(child);
204
+ if (!ending)
205
+ resolve({ exitCode: stopped ? null : (code ?? 1), output });
107
206
  });
108
207
  });
109
208
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Temporary worktrees: somewhere to run a command at a commit without\n * touching the person's own checkout.\n *\n * The family's tools read git and never write it (spec-core ADR-0005). The one\n * exception is this: a detached worktree under the system's temporary\n * directory, added for one job and removed after it, whether the job\n * succeeded, threw, or the process was interrupted (ADR-0003). The person's\n * branch, index, stash and working files are never touched; a failed round is\n * discarded by discarding its worktree, never by resetting theirs.\n */\n\nimport { execFileSync, spawn } from 'node:child_process';\nimport { mkdtempSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { addWorktree, removeWorktree } from './git.js';\n\nconst live = new Map<string, string>();\nlet installed = false;\n\n/** Removes every live worktree synchronously: the only kind of work an exit handler may do. */\nfunction sweep(): void {\n for (const [directory, repository] of live) {\n try {\n execFileSync('git', ['worktree', 'remove', '--force', directory], { cwd: repository, stdio: 'ignore', windowsHide: true });\n } catch {\n // Removed below regardless; `git worktree prune` then forgets it.\n }\n rmSync(directory, { recursive: true, force: true });\n try {\n execFileSync('git', ['worktree', 'prune'], { cwd: repository, stdio: 'ignore', windowsHide: true });\n } catch {\n // Nothing more can be done from an exit handler.\n }\n }\n live.clear();\n}\n\nfunction install(): void {\n if (installed) return;\n installed = true;\n process.once('exit', sweep);\n for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) {\n process.once(signal, () => {\n sweep();\n process.exit(signal === 'SIGINT' ? 130 : 143);\n });\n }\n}\n\n/** Runs `work` in a detached worktree of `repository` at `revision`, and removes the worktree after. */\nexport async function withWorktree<T>(repository: string, revision: string, work: (directory: string) => Promise<T>): Promise<T> {\n install();\n const directory = mkdtempSync(join(tmpdir(), 'spec-harness-'));\n live.set(directory, repository);\n try {\n await addWorktree(directory, revision, repository);\n return await work(directory);\n } finally {\n await removeWorktree(directory, repository);\n rmSync(directory, { recursive: true, force: true });\n live.delete(directory);\n }\n}\n\nexport interface CommandRun {\n /** `null` when the command was stopped at its timeout. */\n readonly exitCode: number | null;\n readonly output: string;\n}\n\n/**\n * Runs a command line in a directory, output and errors interleaved, stopped\n * with everything it started when it outlives `timeoutSeconds`.\n *\n * Through the shell, on purpose: a probe's command is a line from the\n * repository's own brief - `npm test -- x` - which the person approved with\n * the brief, exactly as CI runs the repository's own scripts. Nothing from\n * outside the repository reaches it.\n */\nexport function runCommand(line: string, cwd: string, timeoutSeconds: number): Promise<CommandRun> {\n return new Promise((resolve) => {\n const child = spawn(line, { cwd, shell: true, windowsHide: true, detached: process.platform !== 'win32', env: { ...process.env, CI: '1' } });\n let output = '';\n const keep = (chunk: Buffer): void => {\n // Enough to find a signature in; a runaway log is not kept whole.\n if (output.length < 4 * 1024 * 1024) output += chunk.toString('utf8');\n };\n child.stdout?.on('data', keep);\n child.stderr?.on('data', keep);\n let stopped = false;\n const timer = setTimeout(() => {\n stopped = true;\n if (child.pid === undefined) return;\n try {\n if (process.platform === 'win32') execFileSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true });\n else process.kill(-child.pid, 'SIGKILL');\n } catch {\n child.kill('SIGKILL');\n }\n }, timeoutSeconds * 1000);\n child.on('error', (error) => {\n clearTimeout(timer);\n resolve({ exitCode: 127, output: `${output}${error.message}\\n` });\n });\n child.on('close', (code) => {\n clearTimeout(timer);\n resolve({ exitCode: stopped ? null : (code ?? 1), output });\n });\n });\n}\n"]}
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Temporary worktrees: somewhere to run a command at a commit without\n * touching the person's own checkout.\n *\n * The family's tools read git and never write it (spec-core ADR-0005). The one\n * exception is this: a detached worktree under the system's temporary\n * directory, added for one job and removed after it, whether the job\n * succeeded, threw, or the process was interrupted (ADR-0003). The person's\n * branch, index, stash and working files are never touched; a failed round is\n * discarded by discarding its worktree, never by resetting theirs.\n */\n\nimport { execFileSync, spawn, type ChildProcess } from 'node:child_process';\nimport { mkdtempSync, rmSync } from 'node:fs';\nimport { constants, tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { addWorktree, removeWorktree } from './git.js';\n\nconst live = new Map<string, string>();\nconst running = new Set<ChildProcess>();\nlet installed = false;\n/** Set once the process is interrupted: from then on a command's end is never answered. */\nlet ending = false;\n\n/**\n * How long the sandbox waits for the commands it stopped to let go of their\n * output and their directory. They were stopped forced, so this is only the\n * time the system takes to end them; whatever still holds either after it has\n * left the tree, and no wait would end it.\n */\nconst SETTLE_MS = 3_000;\n\n/**\n * Stops a command with everything it started, forced: SIGKILL to its process\n * group on Linux and macOS, taskkill over its tree on Windows.\n *\n * `windowsHide` here and below only keeps a console window from opening on\n * Windows, which nothing reads, so its mutants are equivalent.\n */\nfunction stop(child: ChildProcess): void {\n // No pid means the spawn failed and nothing runs. Past the tick it failed\n // in, the kill below throws on the missing id and the fallback finds no\n // process, so the mutant that lets it through is equivalent; within that\n // tick nothing here stops a command.\n if (child.pid === undefined) return;\n try {\n // False already on Linux and macOS, so the mutants that make it false\n // are equivalent there. On Windows the tests fail without taskkill:\n // stopping the shell alone leaves node holding the output. True already\n // on Windows, so the mutant that makes it true is equivalent there;\n // elsewhere there is no taskkill, and the shell alone is stopped.\n if (process.platform === 'win32') execFileSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true });\n else process.kill(-child.pid, 'SIGKILL');\n } catch {\n // Reached when the group or the tree is already gone, and the command's\n // shell with it, as when the command left something holding its output;\n // and on Windows when taskkill cannot be run, where stopping the shell is\n // what is left. No test takes that second path.\n child.kill('SIGKILL');\n }\n}\n\n/**\n * Deletes a worktree's directory, trying again for up to `SETTLE_MS` while\n * something holds it. On Windows a command just stopped lets go of its\n * directory a moment after taskkill returns, and an exit handler, which runs\n * synchronously, has no other way to wait; elsewhere a directory in use is\n * deleted all the same.\n */\nfunction discard(directory: string): void {\n const deadline = Date.now() + SETTLE_MS;\n for (;;) {\n try {\n rmSync(directory, { recursive: true, force: true });\n return;\n } catch (error) {\n // `>=` would differ only in the millisecond the deadline falls on, so\n // that mutant is equivalent.\n if (Date.now() > deadline) throw error;\n // The pause only spares the processor between attempts: without it\n // the directory is deleted, or given up on, at the same moment, so\n // that mutant is equivalent.\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);\n }\n }\n}\n\n/**\n * Stops every command still running, then removes every live worktree:\n * synchronously, the only kind of work an exit handler may do, so at exit\n * nothing waits for the commands to end but `discard`'s retries.\n */\nfunction sweep(): void {\n for (const child of running) stop(child);\n for (const [directory, repository] of live) {\n // Deleted first, as at the end of a job, so that git forgets it however\n // the job left it (`removeWorktree` says why).\n discard(directory);\n try {\n execFileSync('git', ['worktree', 'remove', '--force', directory], { cwd: repository, stdio: 'ignore', windowsHide: true });\n } catch {\n // Git holds nothing of it any more, or cannot be run; nothing more can\n // be done from an exit handler.\n }\n }\n // A signal handler ends in an exit, which sweeps again over worktrees\n // already gone and forgotten, so a mutant that keeps them changes nothing.\n live.clear();\n}\n\n/**\n * Stops every running command with its tree, waits a bounded time for them to\n * let go of their output, removes the worktrees, and exits as a shell reports\n * a process the signal ended: 128 and the signal's number, the same on every\n * platform Node runs on. The commands lead process groups of their own on\n * Linux and macOS, so the terminal's signal never reached them.\n */\nasync function interrupted(signal: NodeJS.Signals): Promise<void> {\n ending = true;\n const stopping = [...running];\n for (const child of stopping) stop(child);\n await Promise.race([\n Promise.all(stopping.map((child) => new Promise((resolve) => child.once('close', resolve)))),\n new Promise((resolve) => setTimeout(resolve, SETTLE_MS)),\n ]);\n sweep();\n process.exit(128 + constants.signals[signal]);\n}\n\nfunction install(): void {\n if (installed) return;\n installed = true;\n process.once('exit', sweep);\n // `on`, not `once`: a second signal while the first waits would otherwise\n // meet Node's default, which ends the process before any 'exit' handler\n // runs, with the worktrees still in place. It runs the same steps again,\n // and the first to finish exits.\n for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) process.on(signal, interrupted);\n}\n\n/** Runs `work` in a detached worktree of `repository` at `revision`, and removes the worktree after. */\nexport async function withWorktree<T>(repository: string, revision: string, work: (directory: string) => Promise<T>): Promise<T> {\n install();\n const directory = mkdtempSync(join(tmpdir(), 'spec-harness-'));\n live.set(directory, repository);\n try {\n await addWorktree(directory, revision, repository);\n return await work(directory);\n } finally {\n discard(directory);\n await removeWorktree(directory, repository);\n live.delete(directory);\n }\n}\n\nexport interface CommandRun {\n /** `null` when the command was stopped at its timeout. */\n readonly exitCode: number | null;\n readonly output: string;\n}\n\n/**\n * Runs a command line in a directory, output and errors interleaved, stopped\n * with everything it started when it outlives `timeoutSeconds` or the process\n * is interrupted or exits. Once the process is interrupted it never answers:\n * the job waiting on it would otherwise carry on in a worktree being removed.\n *\n * Through the shell, on purpose: a probe's command is a line from the\n * repository's own brief - `npm test -- x` - which the person approved with\n * the brief, exactly as CI runs the repository's own scripts. Nothing from\n * outside the repository reaches it.\n */\nexport function runCommand(line: string, cwd: string, timeoutSeconds: number): Promise<CommandRun> {\n install();\n return new Promise((resolve) => {\n // Elsewhere the command leads a process group of its own, so the timeout\n // can stop the group. Windows has none to signal, and taskkill follows\n // the tree instead; a command detached there loses its output, which the\n // tests catch on Windows. Everywhere else `detached` is true already, so\n // the mutants that make it true are equivalent there, as are\n // `windowsHide`'s.\n const child = spawn(line, { cwd, shell: true, windowsHide: true, detached: process.platform !== 'win32', env: { ...process.env, CI: '1' } });\n running.add(child);\n let output = '';\n const keep = (chunk: Buffer): void => {\n // Enough to find a signature in; a runaway log is not kept whole.\n if (output.length < 4 * 1024 * 1024) output += chunk.toString('utf8');\n };\n // Both streams are piped, so both exist: `?.` only satisfies the type,\n // and its mutants are equivalent.\n child.stdout?.on('data', keep);\n child.stderr?.on('data', keep);\n let stopped = false;\n const timer = setTimeout(() => {\n stopped = true;\n stop(child);\n }, timeoutSeconds * 1000);\n child.on('error', (error) => {\n // 'close' follows the error of a spawn that failed, and clears the\n // timer too, so the mutant that drops this call is equivalent.\n clearTimeout(timer);\n resolve({ exitCode: 127, output: `${output}${error.message}\\n` });\n });\n child.on('close', (code) => {\n clearTimeout(timer);\n running.delete(child);\n if (!ending) resolve({ exitCode: stopped ? null : (code ?? 1), output });\n });\n });\n}\n"]}
package/dist/server.js CHANGED
@@ -67,7 +67,16 @@ export function tools(workspace, env) {
67
67
  if ('text' in found)
68
68
  return found;
69
69
  const packet = await buildContext(workspace, found.brief, found.briefs, reader, base.base);
70
- return { text: packet.markdown, structured: { brief: found.brief.id, included: [...packet.included], omitted: [...packet.omitted], unresolved: [...packet.unresolved] } };
70
+ return {
71
+ text: packet.markdown,
72
+ structured: {
73
+ brief: found.brief.id,
74
+ included: [...packet.included],
75
+ omitted: [...packet.omitted],
76
+ unresolved: [...packet.unresolved],
77
+ unclosedFrontMatter: [...packet.unclosedFrontMatter],
78
+ },
79
+ };
71
80
  },
72
81
  },
73
82
  {