@descent-vtt/spec-harness 0.3.0 → 0.4.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.
@@ -10,7 +10,7 @@
10
10
  "name": "spec-harness",
11
11
  "source": "./",
12
12
  "description": "Rounds of agent work under a brief.",
13
- "version": "0.3.0"
13
+ "version": "0.4.0"
14
14
  }
15
15
  ]
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-harness",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Rounds of agent work under a brief: context, a guard on every write, escalations a person signs, an audit at the end. Needs Claude Code 2.1.139 or later, and @descent-vtt/spec-harness and @descent-vtt/spec-brief 0.2.0 or later installed in the project.",
5
5
  "author": {
6
6
  "name": "DescentVTT",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,112 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ The audit names a `dependencies.manifests` name it cannot read, with
6
+ spec-core's reason, where it dropped the name in silence; `context` asks
7
+ spec-guard about a plain name in the scope as it is on disk; and spec-core is
8
+ at 56c7e54, which refuses a brace alternative that names no path, such as
9
+ `{./,src}`, where it read as every path.
10
+
11
+ ### Added
12
+
13
+ - `readManifestNames` in the programmatic API: the configured manifest
14
+ names read, a predicate over the paths they name and the names that could
15
+ not be read, each with spec-core's reason. `AuditInput.dependencies` takes
16
+ those as `unreadNames`, optional. `manifestMatcher` is unchanged.
17
+
18
+ ### Changed
19
+
20
+ - spec-core at 56c7e54. Its glob refuses braces that expand to a text
21
+ naming no path, as it refuses that text written alone. `{./,src}`,
22
+ `{src,./}`, `.{/,src}` and `{.,src}/` read `./` as the root's contents and
23
+ matched every path; each is now a pattern that cannot be read, with
24
+ spec-core's reason, `the braces expand to "./", which names no path`:
25
+ - In a brief's `protectedFiles` it protected every path: the guard refused
26
+ every write as `protected`, and a ruling over the path waived it. The
27
+ guard now refuses every write as `unreadable-protection`, naming the
28
+ pattern and the reason, which no ruling waives.
29
+ - In `affectedFiles` it put every path in the scope. It is now named
30
+ among the patterns that could not be read, and the rest of the scope
31
+ still counts. `context` and `start_round` no longer ask spec-guard about
32
+ the whole repository for it, so the packet no longer holds rules over
33
+ code the rest of the scope leaves out.
34
+ - In a ruling's paths it allowed every protected path; it is passed over,
35
+ as a malformed path is.
36
+ - In `dependencies.manifests` it made every file the round changed a
37
+ manifest, each one no reader understands reported as `manifest-unread`.
38
+ It is now the `manifest-name-unread` warning, with the reason.
39
+ - In spec-graph's `patterns` it read the briefs; the list now reads
40
+ nothing, and `init` says spec-graph does not read the briefs.
41
+ - `/./` and `/.//` are refused as `the pattern names no path`. They matched
42
+ only paths from the filesystem's root, none of which the guard is given,
43
+ so `protectedFiles: ["/./"]` protected nothing; it now refuses every write
44
+ as `unreadable-protection`.
45
+ - A pattern refused before names what its braces expand to: `{.,src}` is
46
+ `the braces expand to ".", which names no path`, `{/,src}` `"/"`, and
47
+ `{,src}` and `{}` `the braces expand to an empty pattern`, where each was
48
+ `the pattern names no path`. `src/{./,a}` reads as before, what `src`
49
+ holds.
50
+
51
+ ### Fixed
52
+
53
+ - A name in `dependencies.manifests` that spec-core's glob cannot read,
54
+ malformed or too large to compile, is a finding in `audit` and
55
+ `audit_round`: a warning, `manifest-name-unread`, on `.spec-harness.json`,
56
+ with the name and spec-core's reason, such as `a "[" is never closed` or
57
+ `the pattern compiles to more than 65536 states`, whether or not there is
58
+ a base to measure from. It was passed over in silence, so every manifest
59
+ it was meant to name went unmeasured. The other names are read as before,
60
+ and the warning fails the audit only under `--strict`.
61
+ - `context` and `start_round` ask spec-guard for the rules over a name with
62
+ no glob syntax in `affectedFiles` as it is on disk: `src`, a directory, is
63
+ asked about as `src`, and `src/auth/a.ts`, a file, as that file. The
64
+ directory holding the name was asked about, the whole repository for a
65
+ name at the top, so the packet held rules over code outside the scope, one
66
+ over `lib/` say, where `src/` asked about `src` alone. A name the round
67
+ has yet to create is asked about through the directory that will hold it,
68
+ as before, since it may become a directory. The guard's answer is
69
+ unchanged.
70
+
71
+ ## 0.3.1
72
+
73
+ spec-core at f9ce375. A trailing `/` on a brace alternative in a brief's
74
+ patterns means that directory's contents, and a pattern too large to compile
75
+ is refused as a pattern the guard cannot read, where it crashed the guard and
76
+ the hook refused every write.
77
+
78
+ ### Changed
79
+
80
+ - spec-core at f9ce375. Its glob reads a trailing `/` on a brace
81
+ alternative as it reads one written alone, as that directory's contents,
82
+ where the slash was dropped and the alternative read as a literal:
83
+ - In a brief's `affectedFiles` and `protectedFiles`, and in a ruling's
84
+ paths, `{src/,docs/*.md}` is `src/` or `docs/*.md`. The guard allows or
85
+ protects `src/deep/a.ts` as before, and no longer a path named `src`
86
+ itself.
87
+ - `context` and `start_round` ask spec-guard for the rules over `src` for
88
+ such a scope, where they asked over the whole repository, so a rule that
89
+ reaches only code outside the scope, one over `lib/` say, is no longer
90
+ in the packet.
91
+ - In `dependencies.manifests`, `{tools/,Gemfile}` names every file under
92
+ `tools/` at the root, where it named a file called `tools` at any depth.
93
+
94
+ ### Fixed
95
+
96
+ - A pattern that spec-core's glob compiles to more than 65536 states is a
97
+ pattern that cannot be read, with spec-core's reason, `the pattern
98
+ compiles to more than 65536 states`, where the command stopped with an
99
+ uncaught `AutomatonTooLarge`. In a brief's `protectedFiles` the guard
100
+ refuses every write as `unreadable-protection`, naming the pattern and the
101
+ reason, as it does a malformed protection; the PreToolUse hook refused
102
+ every write with "cannot check this write" and now gives that refusal and
103
+ the fix. In `affectedFiles` the pattern is named among those that could
104
+ not be read, and the rest of the scope still counts. A ruling's path, a
105
+ name in `dependencies.manifests` and a pattern in spec-graph's
106
+ configuration are passed over as a malformed one is: no path is ruled by
107
+ it, no manifest is read by it, and `init` says spec-graph does not read
108
+ the briefs.
109
+
3
110
  ## 0.3.0
4
111
 
5
112
  An interrupted `probe` stops its commands before it removes their
package/README.md CHANGED
@@ -32,7 +32,7 @@ person approves brief 012 ─▶ agent on branch brief/012-rotate-tokens
32
32
 
33
33
  ### `context [brief]`
34
34
 
35
- The brief in full - it is the contract - then what it implies: the files the round may write and must not change as the guard reads them, the rulings already signed, the briefs it depends on and whether they are done, the architecture rules spec-guard holds that code to, and the documents the brief links to, whole, until the packet reaches its budget (`context.budget`, 60,000 characters). Documents left out are named by path, not dropped. So is a document whose front matter opens on its first line and is never closed, which spec-core's scanner reads as no front matter: its status was not read, and the packet says so, with the fix - close the block with `---` on a line of its own - rather than showing a document without one. The note fails nothing; `context` exits 0. Rules spec-guard could not read are named as unread, with what it said, never as none; a repository where no spec file matches spec-guard's patterns has none. `--base` names the base the rulings are verified against.
35
+ The brief in full - it is the contract - then what it implies: the files the round may write and must not change as the guard reads them, the rulings already signed, the briefs it depends on and whether they are done, the architecture rules spec-guard holds that code to, and the documents the brief links to, whole, until the packet reaches its budget (`context.budget`, 60,000 characters). Documents left out are named by path, not dropped. So is a document whose front matter opens on its first line and is never closed, which spec-core's scanner reads as no front matter: its status was not read, and the packet says so, with the fix - close the block with `---` on a line of its own - rather than showing a document without one. The note fails nothing; `context` exits 0. Rules spec-guard could not read are named as unread, with what it said, never as none; a repository where no spec file matches spec-guard's patterns has none. The rules are those over the scope: spec-guard is asked about where `affectedFiles` can reach, `src` for `src/**` or `src/`, not the whole repository. A name with no glob syntax that exists, `src` or `src/a.ts`, is asked about as itself; one the round has yet to create is asked about through the directory that will hold it, the root for a name at the top, since it may become a directory as well as a file ([ADR-0001](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0001-the-harness-decides-the-agent-writes.md)). `--base` names the base the rulings are verified against.
36
36
 
37
37
  ### `guard <path...>` and the hooks
38
38
 
@@ -63,7 +63,7 @@ spec-brief's archive refuses a round that changed a protected file, and learns t
63
63
 
64
64
  ### `audit [brief]`
65
65
 
66
- One report: what spec-brief's archive would refuse or warn about, run with `--dry-run`; the brief's own assertions through spec-guard - a goal that fails, or a premise (under a heading such as *The Defect, Measured*) that still holds after the round meant to change it; rulings whose signatures do not verify; and every dependency the round added to `package.json`, `Cargo.toml`, `go.mod`, `pyproject.toml`, `requirements*.txt`, NuGet project files or a `Gemfile`. A part that could not be measured is a finding, never a silence.
66
+ One report: what spec-brief's archive would refuse or warn about, run with `--dry-run`; the brief's own assertions through spec-guard - a goal that fails, or a premise (under a heading such as *The Defect, Measured*) that still holds after the round meant to change it; rulings whose signatures do not verify; and every dependency the round added to `package.json`, `Cargo.toml`, `go.mod`, `pyproject.toml`, `requirements*.txt`, NuGet project files or a `Gemfile`. A part that could not be measured is a finding, never a silence. So is a name in `dependencies.manifests` that spec-core's glob cannot read, malformed or too large to compile: the audit names it and the reason in a warning, `manifest-name-unread`, which fails the audit only under `--strict`, and reads the manifests the other names name.
67
67
 
68
68
  ### `probe [brief]`
69
69
 
@@ -128,7 +128,7 @@ The plugin's hooks and server are the ones `init` writes into `.claude/settings.
128
128
  | `base` | the remote's default branch | What rounds are measured from, and where allowed signers are read. `init` names it, since a repository git did not clone may have no default branch recorded. |
129
129
  | `rulings.section` | `"Rulings"` | The brief section holding the rulings table. |
130
130
  | `rulings.allowedSigners` | `".github/allowed_signers"` | The allowed-signers file, read from the base branch. |
131
- | `dependencies.manifests` | the list above | Manifest names whose added dependencies the audit reports. |
131
+ | `dependencies.manifests` | the list above | Manifest names whose added dependencies the audit reports. A name that cannot be read is a warning in the audit. |
132
132
  | `context.budget` | `60000` | Characters a context packet may hold; cited documents fill what the rest leaves, and those that do not fit are named by path. |
133
133
  | `assertions.premises` | `The Defect, Measured`, `Premises`, `Preconditions` | Sections whose assertions state what was true before the round. |
134
134
  | `probes.runs`, `probes.timeout` | `2`, `600` | Runs per probe, and seconds per run. |
package/dist/audit.d.ts CHANGED
@@ -43,6 +43,11 @@ export interface AuditInput {
43
43
  readonly dependencies: {
44
44
  readonly changes: readonly DependencyChange[];
45
45
  readonly unread: readonly string[];
46
+ /** Names in `dependencies.manifests` that could not be read, with spec-core's reason; none when not given. */
47
+ readonly unreadNames?: readonly {
48
+ readonly name: string;
49
+ readonly reason: string;
50
+ }[] | undefined;
46
51
  };
47
52
  readonly archive: {
48
53
  readonly blocking: readonly ArchiveReason[];
package/dist/audit.js CHANGED
@@ -19,6 +19,7 @@
19
19
  *
20
20
  * A part that could not be measured is a finding, never a silence.
21
21
  */
22
+ import { CONFIG_FILE } from './config.js';
22
23
  import { PLUGIN } from './configure.js';
23
24
  import { rulingFor } from './guard.js';
24
25
  import { sameSection } from './reader.js';
@@ -99,6 +100,9 @@ export function audit(input) {
99
100
  for (const ruling of input.unverifiedRulings) {
100
101
  out.push(finding('ruling-unverified', 'warning', `ruling ${ruling.id} allows nothing: ${ruling.reason}`, 'a ruling counts when the commit that last changed its row is signed by a key in the base branch\'s allowed signers', brief.file));
101
102
  }
103
+ for (const { name, reason } of input.dependencies.unreadNames ?? []) {
104
+ out.push(finding('manifest-name-unread', 'warning', `"dependencies.manifests" names "${name}", which cannot be read: ${reason}; no manifest it names was read`, `fix or remove the name in ${CONFIG_FILE}; the other names were read`, CONFIG_FILE));
105
+ }
102
106
  for (const file of input.dependencies.unread) {
103
107
  out.push(finding('manifest-unread', 'warning', `${file} changed and could not be read for dependencies`, 'check its dependencies by hand; the audit cannot', file));
104
108
  }
package/dist/audit.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Did the round stay inside the lines?\n *\n * One report, from three sources and one question of its own:\n *\n * - spec-brief's archive, asked with `--dry-run`: open boxes, protected files\n * changed, files outside the scope, a tree that was not committed. The\n * archive is spec-brief's decision, so its reasons are carried as they are\n * - one defect, one finding - and the harness does not measure the scope a\n * second time.\n * - spec-guard, over the brief's own assertions: the goals must hold now. A\n * premise - an assertion in a section that states what was true before the\n * round, such as \"The Defect, Measured\" - is expected to stop holding, and\n * one that still holds says the defect may still be there.\n * - The rulings: each one whose signature does not verify is reported, since\n * an unverified ruling allows nothing.\n * - And the one question nobody else asks: which dependencies the round\n * added. A dependency is code no reviewer read.\n *\n * A part that could not be measured is a finding, never a silence.\n */\n\nimport { PLUGIN } from './configure.js';\nimport { rulingFor, type VerifiedRuling } from './guard.js';\nimport type { DependencyChange } from './manifests.js';\nimport { sameSection } from './reader.js';\nimport type { BriefRow, Finding, Severity } from './types.js';\n\nexport interface AssertionOutcome {\n readonly ok: boolean;\n readonly description: string;\n readonly message: string;\n readonly line: number;\n /** The brief section the directive sits in, or `null`. */\n readonly section: string | null;\n /** Every section it sits in, outermost first; `[section]` when not given. */\n readonly enclosing?: readonly string[] | undefined;\n}\n\n/** A reason spec-brief's archive gives; a `protected-file` refusal names its `path`. */\nexport interface ArchiveReason extends Finding {\n readonly path?: string | undefined;\n}\n\nexport interface AuditInput {\n readonly brief: BriefRow;\n /** Why the round's changes could not be measured, when they could not. */\n readonly unmeasured: string | null;\n readonly dependencies: { readonly changes: readonly DependencyChange[]; readonly unread: readonly string[] };\n readonly archive: { readonly blocking: readonly ArchiveReason[]; readonly warnings: readonly ArchiveReason[] } | { readonly unavailable: string };\n readonly assertions: readonly AssertionOutcome[] | { readonly unavailable: string };\n /** Section names whose assertions are premises. Compared without case, emphasis, a leading number or a trailing colon. */\n readonly premiseSections: readonly string[];\n readonly unverifiedRulings: readonly { readonly id: string; readonly reason: string }[];\n /** The rulings whose signatures verify, which a protected file the archive refuses may be covered by. */\n readonly verifiedRulings: readonly VerifiedRuling[];\n /** Whether spec-brief's configuration loads this package's plugin, through which its archive learns of them. */\n readonly pluginLoaded: boolean;\n}\n\nexport interface AuditReport {\n readonly findings: readonly Finding[];\n readonly counts: Readonly<Record<Severity, number>>;\n}\n\nfunction finding(rule: string, severity: Severity, message: string, hint: string, file?: string, line?: number): Finding {\n return { rule, severity, message, hint, ...(file === undefined ? {} : { file }), ...(line === undefined ? {} : { line }) };\n}\n\n/**\n * Whether an assertion's section is a premise section. Compared as the\n * rulings section is, and as spec-brief compares section names: a heading\n * written `## 3. **The Defect, Measured:**` is still the premise section, and\n * an assertion under it read as a goal would fail the round for retiring it.\n */\nexport function isPremise(sections: string | null | readonly string[], premises: readonly string[]): boolean {\n const list = sections === null ? [] : typeof sections === 'string' ? [sections] : sections;\n // A premise section's subsections are still premises: `### Before` under\n // `## The Defect, Measured` states what was true before the round.\n return list.some((section) => premises.some((premise) => sameSection(section, premise)));\n}\n\n/**\n * A live brief's premise that no longer holds, as `premises` reports it. On\n * the brief a round is working on - the one the flag, SPEC_BRIEF or the\n * branch names - that is what the round set out to do, and it is reported as\n * the audit reports it; on any other, what the brief was written against has\n * changed under it.\n */\nexport function premiseFinding(\n brief: Pick<BriefRow, 'id' | 'file'>,\n outcome: { readonly description: string; readonly message: string; readonly line: number },\n active: boolean,\n): Finding {\n if (active) {\n return finding(\n 'premise-retired',\n 'note',\n `brief ${brief.id}'s premise no longer holds, as the round on it intends: ${outcome.description}`,\n 'nothing to do: this is the round that changes it, and audit measures it',\n brief.file,\n outcome.line,\n );\n }\n return finding(\n 'stale-premise',\n 'error',\n `brief ${brief.id}'s premise no longer holds: ${outcome.description}: ${outcome.message}`,\n 'what the brief was written against has changed; archive the brief if its work is done, or rewrite its premise before a round is run on it',\n brief.file,\n outcome.line,\n );\n}\n\n/**\n * The next step for a reason the archive gives. A protected file a verified\n * ruling covers is refused only because the archive did not learn of the\n * ruling, and recording a departure in the brief would not fix that.\n */\nfunction archiveHint(reason: ArchiveReason, input: AuditInput): string {\n const ruling = reason.rule === 'protected-file' && reason.path !== undefined ? rulingFor(input.verifiedRulings, reason.path) : undefined;\n if (ruling === undefined) return reason.hint;\n const allowed = `ruling ${ruling.id}, signed by ${ruling.signer}, allows it`;\n return input.pluginLoaded\n ? `${allowed}, and the archive still refused it: check that spec-brief loads the spec-harness installed here and measures from the same base (spec-harness doctor)`\n : `${allowed}, but spec-brief does not load spec-harness's plugin, which is how its archive learns of signed rulings: add \"${PLUGIN}\" to \"plugins\" in its configuration, or run spec-harness init --write`;\n}\n\nexport function audit(input: AuditInput): AuditReport {\n const { brief } = input;\n const out: Finding[] = [];\n\n if (input.unmeasured !== null) {\n out.push(\n finding(\n 'unmeasured',\n 'warning',\n `the round's changes were not measured: ${input.unmeasured}`,\n 'pass --base <ref>, set \"base\" in .spec-harness.json, or audit on the round\\'s branch before it merges',\n brief.file,\n ),\n );\n }\n\n if ('unavailable' in input.archive) {\n out.push(finding('archive-unchecked', 'warning', `what the archive would say is unknown: ${input.archive.unavailable}`, 'install spec-brief, or fix what stopped it', brief.file));\n } else {\n for (const reason of [...input.archive.blocking, ...input.archive.warnings]) {\n out.push(finding(`archive/${reason.rule}`, reason.severity, reason.message, archiveHint(reason, input), reason.file, reason.line));\n }\n }\n\n if (!Array.isArray(input.assertions)) {\n const unavailable = (input.assertions as { unavailable: string }).unavailable;\n out.push(finding('assertions-unchecked', 'warning', `the brief's assertions were not run: ${unavailable}`, 'install spec-guard, or fix what stopped it', brief.file));\n } else {\n for (const outcome of input.assertions) {\n const premise = isPremise(outcome.enclosing ?? outcome.section, input.premiseSections);\n if (premise && outcome.ok) {\n out.push(\n finding(\n 'premise-holds',\n 'warning',\n `a premise still holds after the round: ${outcome.description}`,\n 'the round set out to change what this premise states; check that it did, or move the assertion out of the premises',\n brief.file,\n outcome.line,\n ),\n );\n } else if (premise) {\n out.push(finding('premise-retired', 'note', `a premise no longer holds, as the round intended: ${outcome.description}`, 'nothing to do', brief.file, outcome.line));\n } else if (!outcome.ok) {\n out.push(finding('goal-failed', 'error', `${outcome.description}: ${outcome.message}`, 'the round is not done until this holds', brief.file, outcome.line));\n }\n }\n }\n\n for (const ruling of input.unverifiedRulings) {\n out.push(\n finding(\n 'ruling-unverified',\n 'warning',\n `ruling ${ruling.id} allows nothing: ${ruling.reason}`,\n 'a ruling counts when the commit that last changed its row is signed by a key in the base branch\\'s allowed signers',\n brief.file,\n ),\n );\n }\n\n for (const file of input.dependencies.unread) {\n out.push(finding('manifest-unread', 'warning', `${file} changed and could not be read for dependencies`, 'check its dependencies by hand; the audit cannot', file));\n }\n for (const change of input.dependencies.changes) {\n const where = `${change.file} (${change.section})`;\n if (change.before === null) {\n out.push(\n finding(\n 'new-dependency',\n 'warning',\n `the round added ${change.ecosystem} dependency \"${change.name}\"${change.after === '' ? '' : ` ${change.after}`} in ${where}`,\n 'say in the brief why it is needed, or remove it; a new dependency is code no reviewer read',\n change.file,\n ),\n );\n } else if (change.after === null) {\n out.push(finding('dependency-removed', 'note', `the round removed \"${change.name}\" from ${where}`, 'nothing to do', change.file));\n } else {\n out.push(finding('dependency-changed', 'note', `the round moved \"${change.name}\" from ${change.before} to ${change.after} in ${where}`, 'nothing to do, if the brief meant it', change.file));\n }\n }\n\n const counts: Record<Severity, number> = { error: 0, warning: 0, note: 0 };\n for (const item of out) counts[item.severity] += 1;\n return { findings: out, counts };\n}\n"]}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Did the round stay inside the lines?\n *\n * One report, from three sources and one question of its own:\n *\n * - spec-brief's archive, asked with `--dry-run`: open boxes, protected files\n * changed, files outside the scope, a tree that was not committed. The\n * archive is spec-brief's decision, so its reasons are carried as they are\n * - one defect, one finding - and the harness does not measure the scope a\n * second time.\n * - spec-guard, over the brief's own assertions: the goals must hold now. A\n * premise - an assertion in a section that states what was true before the\n * round, such as \"The Defect, Measured\" - is expected to stop holding, and\n * one that still holds says the defect may still be there.\n * - The rulings: each one whose signature does not verify is reported, since\n * an unverified ruling allows nothing.\n * - And the one question nobody else asks: which dependencies the round\n * added. A dependency is code no reviewer read.\n *\n * A part that could not be measured is a finding, never a silence.\n */\n\nimport { CONFIG_FILE } from './config.js';\nimport { PLUGIN } from './configure.js';\nimport { rulingFor, type VerifiedRuling } from './guard.js';\nimport type { DependencyChange } from './manifests.js';\nimport { sameSection } from './reader.js';\nimport type { BriefRow, Finding, Severity } from './types.js';\n\nexport interface AssertionOutcome {\n readonly ok: boolean;\n readonly description: string;\n readonly message: string;\n readonly line: number;\n /** The brief section the directive sits in, or `null`. */\n readonly section: string | null;\n /** Every section it sits in, outermost first; `[section]` when not given. */\n readonly enclosing?: readonly string[] | undefined;\n}\n\n/** A reason spec-brief's archive gives; a `protected-file` refusal names its `path`. */\nexport interface ArchiveReason extends Finding {\n readonly path?: string | undefined;\n}\n\nexport interface AuditInput {\n readonly brief: BriefRow;\n /** Why the round's changes could not be measured, when they could not. */\n readonly unmeasured: string | null;\n readonly dependencies: {\n readonly changes: readonly DependencyChange[];\n readonly unread: readonly string[];\n /** Names in `dependencies.manifests` that could not be read, with spec-core's reason; none when not given. */\n readonly unreadNames?: readonly { readonly name: string; readonly reason: string }[] | undefined;\n };\n readonly archive: { readonly blocking: readonly ArchiveReason[]; readonly warnings: readonly ArchiveReason[] } | { readonly unavailable: string };\n readonly assertions: readonly AssertionOutcome[] | { readonly unavailable: string };\n /** Section names whose assertions are premises. Compared without case, emphasis, a leading number or a trailing colon. */\n readonly premiseSections: readonly string[];\n readonly unverifiedRulings: readonly { readonly id: string; readonly reason: string }[];\n /** The rulings whose signatures verify, which a protected file the archive refuses may be covered by. */\n readonly verifiedRulings: readonly VerifiedRuling[];\n /** Whether spec-brief's configuration loads this package's plugin, through which its archive learns of them. */\n readonly pluginLoaded: boolean;\n}\n\nexport interface AuditReport {\n readonly findings: readonly Finding[];\n readonly counts: Readonly<Record<Severity, number>>;\n}\n\nfunction finding(rule: string, severity: Severity, message: string, hint: string, file?: string, line?: number): Finding {\n return { rule, severity, message, hint, ...(file === undefined ? {} : { file }), ...(line === undefined ? {} : { line }) };\n}\n\n/**\n * Whether an assertion's section is a premise section. Compared as the\n * rulings section is, and as spec-brief compares section names: a heading\n * written `## 3. **The Defect, Measured:**` is still the premise section, and\n * an assertion under it read as a goal would fail the round for retiring it.\n */\nexport function isPremise(sections: string | null | readonly string[], premises: readonly string[]): boolean {\n const list = sections === null ? [] : typeof sections === 'string' ? [sections] : sections;\n // A premise section's subsections are still premises: `### Before` under\n // `## The Defect, Measured` states what was true before the round.\n return list.some((section) => premises.some((premise) => sameSection(section, premise)));\n}\n\n/**\n * A live brief's premise that no longer holds, as `premises` reports it. On\n * the brief a round is working on - the one the flag, SPEC_BRIEF or the\n * branch names - that is what the round set out to do, and it is reported as\n * the audit reports it; on any other, what the brief was written against has\n * changed under it.\n */\nexport function premiseFinding(\n brief: Pick<BriefRow, 'id' | 'file'>,\n outcome: { readonly description: string; readonly message: string; readonly line: number },\n active: boolean,\n): Finding {\n if (active) {\n return finding(\n 'premise-retired',\n 'note',\n `brief ${brief.id}'s premise no longer holds, as the round on it intends: ${outcome.description}`,\n 'nothing to do: this is the round that changes it, and audit measures it',\n brief.file,\n outcome.line,\n );\n }\n return finding(\n 'stale-premise',\n 'error',\n `brief ${brief.id}'s premise no longer holds: ${outcome.description}: ${outcome.message}`,\n 'what the brief was written against has changed; archive the brief if its work is done, or rewrite its premise before a round is run on it',\n brief.file,\n outcome.line,\n );\n}\n\n/**\n * The next step for a reason the archive gives. A protected file a verified\n * ruling covers is refused only because the archive did not learn of the\n * ruling, and recording a departure in the brief would not fix that.\n */\nfunction archiveHint(reason: ArchiveReason, input: AuditInput): string {\n const ruling = reason.rule === 'protected-file' && reason.path !== undefined ? rulingFor(input.verifiedRulings, reason.path) : undefined;\n if (ruling === undefined) return reason.hint;\n const allowed = `ruling ${ruling.id}, signed by ${ruling.signer}, allows it`;\n return input.pluginLoaded\n ? `${allowed}, and the archive still refused it: check that spec-brief loads the spec-harness installed here and measures from the same base (spec-harness doctor)`\n : `${allowed}, but spec-brief does not load spec-harness's plugin, which is how its archive learns of signed rulings: add \"${PLUGIN}\" to \"plugins\" in its configuration, or run spec-harness init --write`;\n}\n\nexport function audit(input: AuditInput): AuditReport {\n const { brief } = input;\n const out: Finding[] = [];\n\n if (input.unmeasured !== null) {\n out.push(\n finding(\n 'unmeasured',\n 'warning',\n `the round's changes were not measured: ${input.unmeasured}`,\n 'pass --base <ref>, set \"base\" in .spec-harness.json, or audit on the round\\'s branch before it merges',\n brief.file,\n ),\n );\n }\n\n if ('unavailable' in input.archive) {\n out.push(finding('archive-unchecked', 'warning', `what the archive would say is unknown: ${input.archive.unavailable}`, 'install spec-brief, or fix what stopped it', brief.file));\n } else {\n for (const reason of [...input.archive.blocking, ...input.archive.warnings]) {\n out.push(finding(`archive/${reason.rule}`, reason.severity, reason.message, archiveHint(reason, input), reason.file, reason.line));\n }\n }\n\n if (!Array.isArray(input.assertions)) {\n const unavailable = (input.assertions as { unavailable: string }).unavailable;\n out.push(finding('assertions-unchecked', 'warning', `the brief's assertions were not run: ${unavailable}`, 'install spec-guard, or fix what stopped it', brief.file));\n } else {\n for (const outcome of input.assertions) {\n const premise = isPremise(outcome.enclosing ?? outcome.section, input.premiseSections);\n if (premise && outcome.ok) {\n out.push(\n finding(\n 'premise-holds',\n 'warning',\n `a premise still holds after the round: ${outcome.description}`,\n 'the round set out to change what this premise states; check that it did, or move the assertion out of the premises',\n brief.file,\n outcome.line,\n ),\n );\n } else if (premise) {\n out.push(finding('premise-retired', 'note', `a premise no longer holds, as the round intended: ${outcome.description}`, 'nothing to do', brief.file, outcome.line));\n } else if (!outcome.ok) {\n out.push(finding('goal-failed', 'error', `${outcome.description}: ${outcome.message}`, 'the round is not done until this holds', brief.file, outcome.line));\n }\n }\n }\n\n for (const ruling of input.unverifiedRulings) {\n out.push(\n finding(\n 'ruling-unverified',\n 'warning',\n `ruling ${ruling.id} allows nothing: ${ruling.reason}`,\n 'a ruling counts when the commit that last changed its row is signed by a key in the base branch\\'s allowed signers',\n brief.file,\n ),\n );\n }\n\n for (const { name, reason } of input.dependencies.unreadNames ?? []) {\n out.push(\n finding(\n 'manifest-name-unread',\n 'warning',\n `\"dependencies.manifests\" names \"${name}\", which cannot be read: ${reason}; no manifest it names was read`,\n `fix or remove the name in ${CONFIG_FILE}; the other names were read`,\n CONFIG_FILE,\n ),\n );\n }\n for (const file of input.dependencies.unread) {\n out.push(finding('manifest-unread', 'warning', `${file} changed and could not be read for dependencies`, 'check its dependencies by hand; the audit cannot', file));\n }\n for (const change of input.dependencies.changes) {\n const where = `${change.file} (${change.section})`;\n if (change.before === null) {\n out.push(\n finding(\n 'new-dependency',\n 'warning',\n `the round added ${change.ecosystem} dependency \"${change.name}\"${change.after === '' ? '' : ` ${change.after}`} in ${where}`,\n 'say in the brief why it is needed, or remove it; a new dependency is code no reviewer read',\n change.file,\n ),\n );\n } else if (change.after === null) {\n out.push(finding('dependency-removed', 'note', `the round removed \"${change.name}\" from ${where}`, 'nothing to do', change.file));\n } else {\n out.push(finding('dependency-changed', 'note', `the round moved \"${change.name}\" from ${change.before} to ${change.after} in ${where}`, 'nothing to do, if the brief meant it', change.file));\n }\n }\n\n const counts: Record<Severity, number> = { error: 0, warning: 0, note: 0 };\n for (const item of out) counts[item.severity] += 1;\n return { findings: out, counts };\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -19,8 +19,8 @@ export { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './h
19
19
  export type { HookRequest, HookResponse } from './hooks.js';
20
20
  export { readJUnit } from './junit.js';
21
21
  export type { JUnitCase, JUnitRead } from './junit.js';
22
- export { diffManifest, ecosystemOf, manifestMatcher, readManifest } from './manifests.js';
23
- export type { Dependency, DependencyChange, Ecosystem } from './manifests.js';
22
+ export { diffManifest, ecosystemOf, manifestMatcher, readManifest, readManifestNames } from './manifests.js';
23
+ export type { Dependency, DependencyChange, Ecosystem, ManifestNames } from './manifests.js';
24
24
  export { classify, readProbes, renderEvidence, verdictOf } from './probe.js';
25
25
  export type { Classified, CodeBlock, ProbeFile, ProbeResult, ProbeRun, ProbeSet, ProbeSpec } from './probe.js';
26
26
  export { createReader } from './reader.js';
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ export { renderContext } from './context.js';
11
11
  export { decide } from './guard.js';
12
12
  export { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './hooks.js';
13
13
  export { readJUnit } from './junit.js';
14
- export { diffManifest, ecosystemOf, manifestMatcher, readManifest } from './manifests.js';
14
+ export { diffManifest, ecosystemOf, manifestMatcher, readManifest, readManifestNames } from './manifests.js';
15
15
  export { classify, readProbes, renderEvidence, verdictOf } from './probe.js';
16
16
  export { createReader } from './reader.js';
17
17
  export { addRulingRow, nextId, pathsOf, readRulings, renderMemo, renderRow } from './rulings.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAG5F,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzF,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE1F,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEjG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1H,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["/**\n * Programmatic API. The command line is a thin shell over these; a harness of\n * another kind - a CI job, an orchestrator - can ask the same questions.\n */\n\nexport { main, run, EXIT_ERROR, EXIT_FAILED, EXIT_OK, UsageError, version } from './cli.js';\nexport type { CliIO } from './cli.js';\n\nexport { audit } from './audit.js';\nexport type { AssertionOutcome, AuditInput, AuditReport } from './audit.js';\nexport { briefIdFromBranch, idFromBranch, sameId, templateError } from './branch.js';\nexport { findActive, parseBriefList, SiblingOutputError, SUPPORTED_SCHEMA_VERSIONS } from './briefs.js';\nexport type { ActiveBrief, ActiveSource } from './briefs.js';\nexport { ConfigError, CONFIG_FILE, DEFAULT_CONFIG, parseConfig, SIBLINGS } from './config.js';\nexport type { HarnessConfig, OutOfScope, SiblingName } from './config.js';\nexport { renderContext } from './context.js';\nexport type { CitedDocument, ContextInput, ContextPacket, RuleInForce, RulingInForce } from './context.js';\nexport { decide } from './guard.js';\nexport type { Decision, GuardInput, Reason, Verdict, VerifiedRuling } from './guard.js';\nexport { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './hooks.js';\nexport type { HookRequest, HookResponse } from './hooks.js';\nexport { readJUnit } from './junit.js';\nexport type { JUnitCase, JUnitRead } from './junit.js';\nexport { diffManifest, ecosystemOf, manifestMatcher, readManifest } from './manifests.js';\nexport type { Dependency, DependencyChange, Ecosystem } from './manifests.js';\nexport { classify, readProbes, renderEvidence, verdictOf } from './probe.js';\nexport type { Classified, CodeBlock, ProbeFile, ProbeResult, ProbeRun, ProbeSet, ProbeSpec } from './probe.js';\nexport { createReader } from './reader.js';\nexport { addRulingRow, nextId, pathsOf, readRulings, renderMemo, renderRow } from './rulings.js';\nexport type { EscalationOption, EscalationRequest, RulingRow, TableView } from './rulings.js';\nexport { buildContext, checkPaths, checkRulings, raiseEscalation, recordRuling, resolveBase, runAudit } from './round.js';\nexport { GUARD_HOOK, mcpServer, mergeClaudeSettings, mergeMcp, mergeSpecGraph, PROJECT_DIR, PROJECT_DIR_OR_HERE } from './configure.js';\nexport type { BriefRow, FileChange, Finding, Severity } from './types.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAG5F,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzF,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAE7G,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEjG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1H,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["/**\n * Programmatic API. The command line is a thin shell over these; a harness of\n * another kind - a CI job, an orchestrator - can ask the same questions.\n */\n\nexport { main, run, EXIT_ERROR, EXIT_FAILED, EXIT_OK, UsageError, version } from './cli.js';\nexport type { CliIO } from './cli.js';\n\nexport { audit } from './audit.js';\nexport type { AssertionOutcome, AuditInput, AuditReport } from './audit.js';\nexport { briefIdFromBranch, idFromBranch, sameId, templateError } from './branch.js';\nexport { findActive, parseBriefList, SiblingOutputError, SUPPORTED_SCHEMA_VERSIONS } from './briefs.js';\nexport type { ActiveBrief, ActiveSource } from './briefs.js';\nexport { ConfigError, CONFIG_FILE, DEFAULT_CONFIG, parseConfig, SIBLINGS } from './config.js';\nexport type { HarnessConfig, OutOfScope, SiblingName } from './config.js';\nexport { renderContext } from './context.js';\nexport type { CitedDocument, ContextInput, ContextPacket, RuleInForce, RulingInForce } from './context.js';\nexport { decide } from './guard.js';\nexport type { Decision, GuardInput, Reason, Verdict, VerifiedRuling } from './guard.js';\nexport { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './hooks.js';\nexport type { HookRequest, HookResponse } from './hooks.js';\nexport { readJUnit } from './junit.js';\nexport type { JUnitCase, JUnitRead } from './junit.js';\nexport { diffManifest, ecosystemOf, manifestMatcher, readManifest, readManifestNames } from './manifests.js';\nexport type { Dependency, DependencyChange, Ecosystem, ManifestNames } from './manifests.js';\nexport { classify, readProbes, renderEvidence, verdictOf } from './probe.js';\nexport type { Classified, CodeBlock, ProbeFile, ProbeResult, ProbeRun, ProbeSet, ProbeSpec } from './probe.js';\nexport { createReader } from './reader.js';\nexport { addRulingRow, nextId, pathsOf, readRulings, renderMemo, renderRow } from './rulings.js';\nexport type { EscalationOption, EscalationRequest, RulingRow, TableView } from './rulings.js';\nexport { buildContext, checkPaths, checkRulings, raiseEscalation, recordRuling, resolveBase, runAudit } from './round.js';\nexport { GUARD_HOOK, mcpServer, mergeClaudeSettings, mergeMcp, mergeSpecGraph, PROJECT_DIR, PROJECT_DIR_OR_HERE } from './configure.js';\nexport type { BriefRow, FileChange, Finding, Severity } from './types.js';\n"]}
@@ -6,7 +6,8 @@
6
6
  * that can be answered exactly, and it matters: a dependency is code nobody in
7
7
  * the review read. Each reader here understands the part of its format that
8
8
  * declares dependencies and nothing more - no resolver, no lockfile, no
9
- * network. A manifest it cannot read is reported as unread, never as clean.
9
+ * network. A manifest it cannot read is reported as unread, never as clean,
10
+ * and so is a configured name it cannot read.
10
11
  */
11
12
  export type Ecosystem = 'npm' | 'cargo' | 'go' | 'pip' | 'python' | 'nuget' | 'bundler';
12
13
  export interface Dependency {
@@ -34,6 +35,21 @@ export type ManifestRead = {
34
35
  };
35
36
  /** The ecosystem a manifest file name belongs to, or `null`. */
36
37
  export declare function ecosystemOf(path: string): Ecosystem | null;
38
+ export interface ManifestNames {
39
+ /** Whether a path is a manifest by one of the names that could be read, matched at any depth. */
40
+ readonly match: (path: string) => boolean;
41
+ /** The names spec-core's glob refused, malformed or too large to compile, each with its reason. */
42
+ readonly unread: readonly {
43
+ readonly name: string;
44
+ readonly reason: string;
45
+ }[];
46
+ }
47
+ /**
48
+ * The configured manifest names, read. A name that cannot be read names no
49
+ * manifest, and is returned so the audit can say so: every file it was
50
+ * meant to name would otherwise go unmeasured in silence.
51
+ */
52
+ export declare function readManifestNames(patterns: readonly string[]): ManifestNames;
37
53
  /** A predicate over paths for the configured manifest names, matched at any depth. */
38
54
  export declare function manifestMatcher(patterns: readonly string[]): (path: string) => boolean;
39
55
  /** The name a PEP 508 requirement names, normalised as PEP 503 says. */
package/dist/manifests.js CHANGED
@@ -6,7 +6,8 @@
6
6
  * that can be answered exactly, and it matters: a dependency is code nobody in
7
7
  * the review read. Each reader here understands the part of its format that
8
8
  * declares dependencies and nothing more - no resolver, no lockfile, no
9
- * network. A manifest it cannot read is reported as unread, never as clean.
9
+ * network. A manifest it cannot read is reported as unread, never as clean,
10
+ * and so is a configured name it cannot read.
10
11
  */
11
12
  import { parseGlob } from './vendor/spec-core/pattern/index.js';
12
13
  /** The ecosystem a manifest file name belongs to, or `null`. */
@@ -28,15 +29,26 @@ export function ecosystemOf(path) {
28
29
  return 'bundler';
29
30
  return null;
30
31
  }
31
- /** A predicate over paths for the configured manifest names, matched at any depth. */
32
- export function manifestMatcher(patterns) {
32
+ /**
33
+ * The configured manifest names, read. A name that cannot be read names no
34
+ * manifest, and is returned so the audit can say so: every file it was
35
+ * meant to name would otherwise go unmeasured in silence.
36
+ */
37
+ export function readManifestNames(patterns) {
33
38
  const globs = [];
39
+ const unread = [];
34
40
  for (const pattern of patterns) {
35
41
  const parsed = parseGlob(pattern, { dialect: 'ripgrep', caseSensitive: true });
36
42
  if (parsed.ok)
37
43
  globs.push(parsed.glob);
44
+ else
45
+ unread.push({ name: pattern, reason: parsed.error });
38
46
  }
39
- return (path) => globs.some((glob) => glob.match(path));
47
+ return { match: (path) => globs.some((glob) => glob.match(path)), unread };
48
+ }
49
+ /** A predicate over paths for the configured manifest names, matched at any depth. */
50
+ export function manifestMatcher(patterns) {
51
+ return readManifestNames(patterns).match;
40
52
  }
41
53
  /* --------------------------------------------------------------------- npm */
42
54
  const NPM_SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
@@ -1 +1 @@
1
- {"version":3,"file":"manifests.js","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which dependencies a round added, removed or moved, read from the manifests\n * it changed.\n *\n * \"No new dependency without saying so\" is one of the few audit questions\n * that can be answered exactly, and it matters: a dependency is code nobody in\n * the review read. Each reader here understands the part of its format that\n * declares dependencies and nothing more - no resolver, no lockfile, no\n * network. A manifest it cannot read is reported as unread, never as clean.\n */\n\nimport { parseGlob, type Glob } from './vendor/spec-core/pattern/index.js';\n\nexport type Ecosystem = 'npm' | 'cargo' | 'go' | 'pip' | 'python' | 'nuget' | 'bundler';\n\nexport interface Dependency {\n readonly section: string;\n readonly name: string;\n /** The version or requirement as written; `''` when none is. */\n readonly version: string;\n}\n\nexport interface DependencyChange {\n readonly file: string;\n readonly ecosystem: Ecosystem;\n readonly section: string;\n readonly name: string;\n /** `null` when the dependency is new. */\n readonly before: string | null;\n /** `null` when the dependency was removed. */\n readonly after: string | null;\n}\n\nexport type ManifestRead = { readonly ok: true; readonly dependencies: readonly Dependency[] } | { readonly ok: false; readonly error: string };\n\n/** The ecosystem a manifest file name belongs to, or `null`. */\nexport function ecosystemOf(path: string): Ecosystem | null {\n const name = path.slice(path.lastIndexOf('/') + 1);\n if (name === 'package.json') return 'npm';\n if (name === 'Cargo.toml') return 'cargo';\n if (name === 'go.mod') return 'go';\n if (name === 'pyproject.toml') return 'python';\n if (/^requirements.*\\.txt$/.test(name)) return 'pip';\n if (name === 'Directory.Packages.props' || /\\.(?:cs|fs|vb)proj$/.test(name)) return 'nuget';\n if (name === 'Gemfile') return 'bundler';\n return null;\n}\n\n/** A predicate over paths for the configured manifest names, matched at any depth. */\nexport function manifestMatcher(patterns: readonly string[]): (path: string) => boolean {\n const globs: Glob[] = [];\n for (const pattern of patterns) {\n const parsed = parseGlob(pattern, { dialect: 'ripgrep', caseSensitive: true });\n if (parsed.ok) globs.push(parsed.glob);\n }\n return (path) => globs.some((glob) => glob.match(path));\n}\n\n/* --------------------------------------------------------------------- npm */\n\nconst NPM_SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];\n\nfunction readNpm(text: string): ManifestRead {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return { ok: false, error: 'not valid JSON' };\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return { ok: false, error: 'not a JSON object' };\n const out: Dependency[] = [];\n for (const section of NPM_SECTIONS) {\n const table = (value as Record<string, unknown>)[section];\n if (table === undefined) continue;\n if (typeof table !== 'object' || table === null || Array.isArray(table)) return { ok: false, error: `\"${section}\" is not an object` };\n for (const [name, version] of Object.entries(table)) out.push({ section, name, version: String(version) });\n }\n return { ok: true, dependencies: out };\n}\n\n/* -------------------------------------------------------------------- TOML */\n\n/** A TOML value on one line, or the start of a multi-line array. Comments and quotes read the way TOML reads them. */\nfunction stripTomlComment(line: string): string {\n let quote: string | null = null;\n for (let i = 0; i < line.length; i += 1) {\n const ch = line.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n } else if (ch === '#') {\n return line.slice(0, i);\n }\n }\n return line;\n}\n\nfunction unquote(value: string): string {\n const trimmed = value.trim();\n if (trimmed.length >= 2 && (trimmed.startsWith('\"') || trimmed.startsWith(\"'\")) && trimmed.endsWith(trimmed.charAt(0))) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\n/**\n * The strings of a TOML array, `[\"a>=1\", 'b']`, possibly spread over lines.\n * A string inside an inline table is a value of that table, not an element:\n * `{include-group = \"test\"}` in a dependency group names a group, not a package.\n */\nfunction tomlStrings(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const ch = text.charAt(i);\n if (ch === '{') depth += 1;\n else if (ch === '}') depth -= 1;\n if (ch === '\"' || ch === \"'\") {\n let j = i + 1;\n let value = '';\n while (j < text.length && text.charAt(j) !== ch) {\n if (text.charAt(j) === '\\\\' && ch === '\"') {\n value += text.charAt(j + 1);\n j += 2;\n continue;\n }\n value += text.charAt(j);\n j += 1;\n }\n if (depth === 0) out.push(value);\n i = j + 1;\n continue;\n }\n i += 1;\n }\n return out;\n}\n\n/** The name a PEP 508 requirement names, normalised as PEP 503 says. */\nexport function pythonName(requirement: string): string {\n const match = /^\\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(requirement);\n return (match?.[1] ?? requirement.trim()).toLowerCase().replace(/[-_.]+/g, '-');\n}\n\ninterface TomlLine {\n readonly table: string;\n readonly key: string;\n readonly value: string;\n}\n\n/** Top-level `key = value` lines per table header, arrays gathered across lines. Enough for dependency tables. */\nfunction tomlEntries(text: string): TomlLine[] | string {\n const out: TomlLine[] = [];\n let table = '';\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i += 1) {\n const line = stripTomlComment(lines[i] as string).trim();\n if (line === '') continue;\n const header = /^\\[\\[?\\s*([^\\]]+?)\\s*\\]\\]?$/.exec(line);\n if (header !== null) {\n table = (header[1] as string).replace(/\\s*\\.\\s*/g, '.');\n continue;\n }\n const eq = line.indexOf('=');\n if (eq <= 0) continue;\n const key = unquote(line.slice(0, eq));\n let value = line.slice(eq + 1).trim();\n if (value.startsWith('[') && !balanced(value)) {\n while (i + 1 < lines.length && !balanced(value)) {\n i += 1;\n value += ` ${stripTomlComment(lines[i] as string).trim()}`;\n }\n if (!balanced(value)) return `the array for \"${key}\" is never closed`;\n }\n out.push({ table, key, value });\n }\n return out;\n}\n\nfunction balanced(value: string): boolean {\n let depth = 0;\n let quote: string | null = null;\n for (let i = 0; i < value.length; i += 1) {\n const ch = value.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") quote = ch;\n else if (ch === '[') depth += 1;\n else if (ch === ']') depth -= 1;\n }\n return depth <= 0 && quote === null;\n}\n\nconst CARGO_TABLE = /^(?:target\\..+\\.)?((?:workspace\\.)?(?:dev-|build-)?dependencies)(?:\\.(.+))?$/;\n\nfunction readCargo(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out = new Map<string, Dependency>();\n for (const { table, key, value } of entries) {\n const match = CARGO_TABLE.exec(table);\n if (match === null) continue;\n const section = match[1] as string;\n const inline = match[2];\n if (inline !== undefined) {\n // [dependencies.serde] then `version = \"1\"`: the table names the crate.\n const name = unquote(inline);\n const id = `${section}\\u0000${name}`;\n const current = out.get(id) ?? { section, name, version: '' };\n out.set(id, key === 'version' ? { ...current, version: unquote(value) } : current);\n continue;\n }\n const version = value.startsWith('{') ? (/version\\s*=\\s*(\"[^\"]*\"|'[^']*')/.exec(value)?.[1] ?? '') : value;\n out.set(`${section}\\u0000${key}`, { section, name: key, version: unquote(version) });\n }\n return { ok: true, dependencies: [...out.values()] };\n}\n\nfunction readPyproject(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out: Dependency[] = [];\n for (const { table, key, value } of entries) {\n if (table === 'project' && key === 'dependencies') {\n for (const requirement of tomlStrings(value)) out.push({ section: 'project', name: pythonName(requirement), version: requirement });\n } else if (table === 'project.optional-dependencies' || table === 'dependency-groups') {\n for (const requirement of tomlStrings(value)) out.push({ section: `${table}.${key}`, name: pythonName(requirement), version: requirement });\n } else if (/^tool\\.poetry\\.(?:group\\.[^.]+\\.)?(?:dev-)?dependencies$/.test(table) && key.toLowerCase() !== 'python') {\n out.push({ section: table, name: pythonName(key), version: unquote(value) });\n }\n }\n return { ok: true, dependencies: out };\n}\n\n/* ------------------------------------------------------------ line formats */\n\nfunction readGoMod(text: string): ManifestRead {\n const out: Dependency[] = [];\n let block = false;\n for (const raw of text.split(/\\r?\\n/)) {\n const indirect = /\\/\\/\\s*indirect\\b/.test(raw);\n const line = raw.replace(/\\/\\/.*$/, '').trim();\n if (line === '') continue;\n if (block) {\n if (line === ')') {\n block = false;\n continue;\n }\n const [name, version] = line.split(/\\s+/);\n if (name !== undefined) out.push({ section: indirect ? 'require (indirect)' : 'require', name, version: version ?? '' });\n continue;\n }\n if (/^require\\s*\\($/.test(line)) {\n block = true;\n continue;\n }\n const single = /^require\\s+(\\S+)\\s+(\\S+)/.exec(line);\n if (single !== null) out.push({ section: indirect ? 'require (indirect)' : 'require', name: single[1] as string, version: single[2] as string });\n }\n if (block) return { ok: false, error: 'a require block is never closed' };\n return { ok: true, dependencies: out };\n}\n\nfunction readRequirements(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.replace(/(^|\\s)#.*$/, '').trim();\n if (line === '' || line.startsWith('-')) continue;\n out.push({ section: 'requirements', name: pythonName(line), version: line });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readNuget(text: string): ManifestRead {\n const out: Dependency[] = [];\n const element = /<(PackageReference|PackageVersion)\\b([^>]*?)\\/?>/g;\n for (let match = element.exec(text); match !== null; match = element.exec(text)) {\n const attributes = match[2] as string;\n const name = /\\bInclude\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? /\\bUpdate\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1];\n if (name === undefined) continue;\n const version = /\\bVersion\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? '';\n out.push({ section: match[1] as string, name, version });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readGemfile(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const match = /^\\s*gem\\s+([\"'])([^\"']+)\\1\\s*(?:,\\s*([\"'])([^\"']*)\\3)?/.exec(raw);\n if (match !== null) out.push({ section: 'gem', name: match[2] as string, version: match[4] ?? '' });\n }\n return { ok: true, dependencies: out };\n}\n\n/** Reads the dependencies a manifest declares. */\nexport function readManifest(ecosystem: Ecosystem, text: string): ManifestRead {\n switch (ecosystem) {\n case 'npm':\n return readNpm(text);\n case 'cargo':\n return readCargo(text);\n case 'go':\n return readGoMod(text);\n case 'python':\n return readPyproject(text);\n case 'pip':\n return readRequirements(text);\n case 'nuget':\n return readNuget(text);\n case 'bundler':\n return readGemfile(text);\n }\n}\n\n/**\n * What changed between two versions of one manifest. `null` text is a file\n * that does not exist on that side. Returns the error instead when either\n * side cannot be read.\n */\nexport function diffManifest(\n file: string,\n ecosystem: Ecosystem,\n before: string | null,\n after: string | null,\n): { readonly changes: readonly DependencyChange[] } | { readonly error: string } {\n const read = (text: string | null, side: string): readonly Dependency[] | string => {\n if (text === null) return [];\n const result = readManifest(ecosystem, text);\n return result.ok ? result.dependencies : `${file} (${side}): ${result.error}`;\n };\n const old = read(before, 'before');\n if (typeof old === 'string') return { error: old };\n const now = read(after, 'after');\n if (typeof now === 'string') return { error: now };\n const key = (d: Dependency): string => `${d.section}\\u0000${d.name}`;\n const was = new Map(old.map((d) => [key(d), d]));\n const is = new Map(now.map((d) => [key(d), d]));\n const changes: DependencyChange[] = [];\n for (const [id, dependency] of is) {\n const previous = was.get(id);\n if (previous === undefined || previous.version !== dependency.version) {\n changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: previous?.version ?? null, after: dependency.version });\n }\n }\n for (const [id, dependency] of was) {\n if (!is.has(id)) changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: dependency.version, after: null });\n }\n return { changes };\n}\n"]}
1
+ {"version":3,"file":"manifests.js","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which dependencies a round added, removed or moved, read from the manifests\n * it changed.\n *\n * \"No new dependency without saying so\" is one of the few audit questions\n * that can be answered exactly, and it matters: a dependency is code nobody in\n * the review read. Each reader here understands the part of its format that\n * declares dependencies and nothing more - no resolver, no lockfile, no\n * network. A manifest it cannot read is reported as unread, never as clean,\n * and so is a configured name it cannot read.\n */\n\nimport { parseGlob, type Glob } from './vendor/spec-core/pattern/index.js';\n\nexport type Ecosystem = 'npm' | 'cargo' | 'go' | 'pip' | 'python' | 'nuget' | 'bundler';\n\nexport interface Dependency {\n readonly section: string;\n readonly name: string;\n /** The version or requirement as written; `''` when none is. */\n readonly version: string;\n}\n\nexport interface DependencyChange {\n readonly file: string;\n readonly ecosystem: Ecosystem;\n readonly section: string;\n readonly name: string;\n /** `null` when the dependency is new. */\n readonly before: string | null;\n /** `null` when the dependency was removed. */\n readonly after: string | null;\n}\n\nexport type ManifestRead = { readonly ok: true; readonly dependencies: readonly Dependency[] } | { readonly ok: false; readonly error: string };\n\n/** The ecosystem a manifest file name belongs to, or `null`. */\nexport function ecosystemOf(path: string): Ecosystem | null {\n const name = path.slice(path.lastIndexOf('/') + 1);\n if (name === 'package.json') return 'npm';\n if (name === 'Cargo.toml') return 'cargo';\n if (name === 'go.mod') return 'go';\n if (name === 'pyproject.toml') return 'python';\n if (/^requirements.*\\.txt$/.test(name)) return 'pip';\n if (name === 'Directory.Packages.props' || /\\.(?:cs|fs|vb)proj$/.test(name)) return 'nuget';\n if (name === 'Gemfile') return 'bundler';\n return null;\n}\n\nexport interface ManifestNames {\n /** Whether a path is a manifest by one of the names that could be read, matched at any depth. */\n readonly match: (path: string) => boolean;\n /** The names spec-core's glob refused, malformed or too large to compile, each with its reason. */\n readonly unread: readonly { readonly name: string; readonly reason: string }[];\n}\n\n/**\n * The configured manifest names, read. A name that cannot be read names no\n * manifest, and is returned so the audit can say so: every file it was\n * meant to name would otherwise go unmeasured in silence.\n */\nexport function readManifestNames(patterns: readonly string[]): ManifestNames {\n const globs: Glob[] = [];\n const unread: { name: string; reason: string }[] = [];\n for (const pattern of patterns) {\n const parsed = parseGlob(pattern, { dialect: 'ripgrep', caseSensitive: true });\n if (parsed.ok) globs.push(parsed.glob);\n else unread.push({ name: pattern, reason: parsed.error });\n }\n return { match: (path) => globs.some((glob) => glob.match(path)), unread };\n}\n\n/** A predicate over paths for the configured manifest names, matched at any depth. */\nexport function manifestMatcher(patterns: readonly string[]): (path: string) => boolean {\n return readManifestNames(patterns).match;\n}\n\n/* --------------------------------------------------------------------- npm */\n\nconst NPM_SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];\n\nfunction readNpm(text: string): ManifestRead {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return { ok: false, error: 'not valid JSON' };\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return { ok: false, error: 'not a JSON object' };\n const out: Dependency[] = [];\n for (const section of NPM_SECTIONS) {\n const table = (value as Record<string, unknown>)[section];\n if (table === undefined) continue;\n if (typeof table !== 'object' || table === null || Array.isArray(table)) return { ok: false, error: `\"${section}\" is not an object` };\n for (const [name, version] of Object.entries(table)) out.push({ section, name, version: String(version) });\n }\n return { ok: true, dependencies: out };\n}\n\n/* -------------------------------------------------------------------- TOML */\n\n/** A TOML value on one line, or the start of a multi-line array. Comments and quotes read the way TOML reads them. */\nfunction stripTomlComment(line: string): string {\n let quote: string | null = null;\n for (let i = 0; i < line.length; i += 1) {\n const ch = line.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n } else if (ch === '#') {\n return line.slice(0, i);\n }\n }\n return line;\n}\n\nfunction unquote(value: string): string {\n const trimmed = value.trim();\n if (trimmed.length >= 2 && (trimmed.startsWith('\"') || trimmed.startsWith(\"'\")) && trimmed.endsWith(trimmed.charAt(0))) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\n/**\n * The strings of a TOML array, `[\"a>=1\", 'b']`, possibly spread over lines.\n * A string inside an inline table is a value of that table, not an element:\n * `{include-group = \"test\"}` in a dependency group names a group, not a package.\n */\nfunction tomlStrings(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const ch = text.charAt(i);\n if (ch === '{') depth += 1;\n else if (ch === '}') depth -= 1;\n if (ch === '\"' || ch === \"'\") {\n let j = i + 1;\n let value = '';\n while (j < text.length && text.charAt(j) !== ch) {\n if (text.charAt(j) === '\\\\' && ch === '\"') {\n value += text.charAt(j + 1);\n j += 2;\n continue;\n }\n value += text.charAt(j);\n j += 1;\n }\n if (depth === 0) out.push(value);\n i = j + 1;\n continue;\n }\n i += 1;\n }\n return out;\n}\n\n/** The name a PEP 508 requirement names, normalised as PEP 503 says. */\nexport function pythonName(requirement: string): string {\n const match = /^\\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(requirement);\n return (match?.[1] ?? requirement.trim()).toLowerCase().replace(/[-_.]+/g, '-');\n}\n\ninterface TomlLine {\n readonly table: string;\n readonly key: string;\n readonly value: string;\n}\n\n/** Top-level `key = value` lines per table header, arrays gathered across lines. Enough for dependency tables. */\nfunction tomlEntries(text: string): TomlLine[] | string {\n const out: TomlLine[] = [];\n let table = '';\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i += 1) {\n const line = stripTomlComment(lines[i] as string).trim();\n if (line === '') continue;\n const header = /^\\[\\[?\\s*([^\\]]+?)\\s*\\]\\]?$/.exec(line);\n if (header !== null) {\n table = (header[1] as string).replace(/\\s*\\.\\s*/g, '.');\n continue;\n }\n const eq = line.indexOf('=');\n if (eq <= 0) continue;\n const key = unquote(line.slice(0, eq));\n let value = line.slice(eq + 1).trim();\n if (value.startsWith('[') && !balanced(value)) {\n while (i + 1 < lines.length && !balanced(value)) {\n i += 1;\n value += ` ${stripTomlComment(lines[i] as string).trim()}`;\n }\n if (!balanced(value)) return `the array for \"${key}\" is never closed`;\n }\n out.push({ table, key, value });\n }\n return out;\n}\n\nfunction balanced(value: string): boolean {\n let depth = 0;\n let quote: string | null = null;\n for (let i = 0; i < value.length; i += 1) {\n const ch = value.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") quote = ch;\n else if (ch === '[') depth += 1;\n else if (ch === ']') depth -= 1;\n }\n return depth <= 0 && quote === null;\n}\n\nconst CARGO_TABLE = /^(?:target\\..+\\.)?((?:workspace\\.)?(?:dev-|build-)?dependencies)(?:\\.(.+))?$/;\n\nfunction readCargo(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out = new Map<string, Dependency>();\n for (const { table, key, value } of entries) {\n const match = CARGO_TABLE.exec(table);\n if (match === null) continue;\n const section = match[1] as string;\n const inline = match[2];\n if (inline !== undefined) {\n // [dependencies.serde] then `version = \"1\"`: the table names the crate.\n const name = unquote(inline);\n const id = `${section}\\u0000${name}`;\n const current = out.get(id) ?? { section, name, version: '' };\n out.set(id, key === 'version' ? { ...current, version: unquote(value) } : current);\n continue;\n }\n const version = value.startsWith('{') ? (/version\\s*=\\s*(\"[^\"]*\"|'[^']*')/.exec(value)?.[1] ?? '') : value;\n out.set(`${section}\\u0000${key}`, { section, name: key, version: unquote(version) });\n }\n return { ok: true, dependencies: [...out.values()] };\n}\n\nfunction readPyproject(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out: Dependency[] = [];\n for (const { table, key, value } of entries) {\n if (table === 'project' && key === 'dependencies') {\n for (const requirement of tomlStrings(value)) out.push({ section: 'project', name: pythonName(requirement), version: requirement });\n } else if (table === 'project.optional-dependencies' || table === 'dependency-groups') {\n for (const requirement of tomlStrings(value)) out.push({ section: `${table}.${key}`, name: pythonName(requirement), version: requirement });\n } else if (/^tool\\.poetry\\.(?:group\\.[^.]+\\.)?(?:dev-)?dependencies$/.test(table) && key.toLowerCase() !== 'python') {\n out.push({ section: table, name: pythonName(key), version: unquote(value) });\n }\n }\n return { ok: true, dependencies: out };\n}\n\n/* ------------------------------------------------------------ line formats */\n\nfunction readGoMod(text: string): ManifestRead {\n const out: Dependency[] = [];\n let block = false;\n for (const raw of text.split(/\\r?\\n/)) {\n const indirect = /\\/\\/\\s*indirect\\b/.test(raw);\n const line = raw.replace(/\\/\\/.*$/, '').trim();\n if (line === '') continue;\n if (block) {\n if (line === ')') {\n block = false;\n continue;\n }\n const [name, version] = line.split(/\\s+/);\n if (name !== undefined) out.push({ section: indirect ? 'require (indirect)' : 'require', name, version: version ?? '' });\n continue;\n }\n if (/^require\\s*\\($/.test(line)) {\n block = true;\n continue;\n }\n const single = /^require\\s+(\\S+)\\s+(\\S+)/.exec(line);\n if (single !== null) out.push({ section: indirect ? 'require (indirect)' : 'require', name: single[1] as string, version: single[2] as string });\n }\n if (block) return { ok: false, error: 'a require block is never closed' };\n return { ok: true, dependencies: out };\n}\n\nfunction readRequirements(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.replace(/(^|\\s)#.*$/, '').trim();\n if (line === '' || line.startsWith('-')) continue;\n out.push({ section: 'requirements', name: pythonName(line), version: line });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readNuget(text: string): ManifestRead {\n const out: Dependency[] = [];\n const element = /<(PackageReference|PackageVersion)\\b([^>]*?)\\/?>/g;\n for (let match = element.exec(text); match !== null; match = element.exec(text)) {\n const attributes = match[2] as string;\n const name = /\\bInclude\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? /\\bUpdate\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1];\n if (name === undefined) continue;\n const version = /\\bVersion\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? '';\n out.push({ section: match[1] as string, name, version });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readGemfile(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const match = /^\\s*gem\\s+([\"'])([^\"']+)\\1\\s*(?:,\\s*([\"'])([^\"']*)\\3)?/.exec(raw);\n if (match !== null) out.push({ section: 'gem', name: match[2] as string, version: match[4] ?? '' });\n }\n return { ok: true, dependencies: out };\n}\n\n/** Reads the dependencies a manifest declares. */\nexport function readManifest(ecosystem: Ecosystem, text: string): ManifestRead {\n switch (ecosystem) {\n case 'npm':\n return readNpm(text);\n case 'cargo':\n return readCargo(text);\n case 'go':\n return readGoMod(text);\n case 'python':\n return readPyproject(text);\n case 'pip':\n return readRequirements(text);\n case 'nuget':\n return readNuget(text);\n case 'bundler':\n return readGemfile(text);\n }\n}\n\n/**\n * What changed between two versions of one manifest. `null` text is a file\n * that does not exist on that side. Returns the error instead when either\n * side cannot be read.\n */\nexport function diffManifest(\n file: string,\n ecosystem: Ecosystem,\n before: string | null,\n after: string | null,\n): { readonly changes: readonly DependencyChange[] } | { readonly error: string } {\n const read = (text: string | null, side: string): readonly Dependency[] | string => {\n if (text === null) return [];\n const result = readManifest(ecosystem, text);\n return result.ok ? result.dependencies : `${file} (${side}): ${result.error}`;\n };\n const old = read(before, 'before');\n if (typeof old === 'string') return { error: old };\n const now = read(after, 'after');\n if (typeof now === 'string') return { error: now };\n const key = (d: Dependency): string => `${d.section}\\u0000${d.name}`;\n const was = new Map(old.map((d) => [key(d), d]));\n const is = new Map(now.map((d) => [key(d), d]));\n const changes: DependencyChange[] = [];\n for (const [id, dependency] of is) {\n const previous = was.get(id);\n if (previous === undefined || previous.version !== dependency.version) {\n changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: previous?.version ?? null, after: dependency.version });\n }\n }\n for (const [id, dependency] of was) {\n if (!is.has(id)) changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: dependency.version, after: null });\n }\n return { changes };\n}\n"]}
package/dist/round.js CHANGED
@@ -15,7 +15,7 @@ import { readRules, renderContext } from './context.js';
15
15
  import { readJsonObject, readText, repositoryPath, stateDirectory, writeAtomic } from './fs.js';
16
16
  import { blameLine, changes, mergeBase, remoteDefault, revision, show, verifyCommit } from './git.js';
17
17
  import { decide } from './guard.js';
18
- import { diffManifest, ecosystemOf, manifestMatcher } from './manifests.js';
18
+ import { diffManifest, ecosystemOf, readManifestNames } from './manifests.js';
19
19
  import { addRulingRow, nextId, readRulings, renderMemo, renderRow, } from './rulings.js';
20
20
  import { compileGlob } from './vendor/spec-core/pattern/index.js';
21
21
  import { dirname, isRelativeReference, resolveInside, splitReference } from './vendor/spec-core/path/index.js';
@@ -111,11 +111,26 @@ export async function checkPaths(workspace, brief, noBrief, given, cwd, reader,
111
111
  return resolved.map(({ path, given: g }) => decide({ path, given: g, brief, noBrief, rulings, outOfScope: workspace.config.outOfScope }));
112
112
  }
113
113
  /* ----------------------------------------------------------------- context */
114
- function scopeBases(patterns) {
114
+ /**
115
+ * The paths spec-guard is asked about for a scope: where its patterns can
116
+ * match. A name with no glob syntax that exists is asked about as itself,
117
+ * since spec-guard reads from the disk whether a path is a file or a
118
+ * directory. One the round has yet to create may become either, and
119
+ * spec-guard would read it as a file, leaving out the rules over a
120
+ * directory of that name; it is asked about through the directory that will
121
+ * hold it, the root for a name at the top, which covers both.
122
+ */
123
+ function scopeBases(root, patterns) {
124
+ // Read as a directory, a name's base is the name itself; read any other
125
+ // way, it is the directory holding it, so which other reading a missing
126
+ // name gets changes nothing. The glob is compiled for its bases and never
127
+ // matched: a file read as a directory narrows the question and nothing
128
+ // else.
129
+ const literal = (path) => (existsSync(join(root, path)) ? 'directory' : 'either');
115
130
  const bases = new Set();
116
131
  for (const pattern of patterns) {
117
132
  try {
118
- for (const base of compileGlob(pattern, { dialect: 'path', caseSensitive: true }).bases)
133
+ for (const base of compileGlob(pattern, { dialect: 'path', caseSensitive: true, literal }).bases)
119
134
  bases.add(base === '' ? '.' : base);
120
135
  }
121
136
  catch {
@@ -125,7 +140,7 @@ function scopeBases(patterns) {
125
140
  return [...bases].sort();
126
141
  }
127
142
  async function rulesFor(workspace, brief) {
128
- const paths = scopeBases(brief.affectedFiles);
143
+ const paths = scopeBases(workspace.root, brief.affectedFiles);
129
144
  if (paths.length === 0)
130
145
  return [];
131
146
  try {
@@ -199,9 +214,12 @@ export async function buildContext(workspace, brief, briefs, reader, baseFlag) {
199
214
  }
200
215
  /* ------------------------------------------------------------------- audit */
201
216
  async function dependencyChanges(workspace, base) {
217
+ // A name that cannot be read is reported with or without a base: it would
218
+ // leave its manifests out of every audit measured from one.
219
+ const names = readManifestNames(workspace.config.dependencies.manifests);
202
220
  if (base.kind === 'unresolved')
203
- return { changes: [], unread: [] };
204
- const isManifest = manifestMatcher(workspace.config.dependencies.manifests);
221
+ return { changes: [], unread: [], unreadNames: names.unread };
222
+ const isManifest = names.match;
205
223
  const out = [];
206
224
  const unread = [];
207
225
  for (const change of await changes(base.mergeBase, base.head, workspace.root)) {
@@ -220,7 +238,7 @@ async function dependencyChanges(workspace, base) {
220
238
  else
221
239
  out.push(...diff.changes);
222
240
  }
223
- return { changes: out, unread };
241
+ return { changes: out, unread, unreadNames: names.unread };
224
242
  }
225
243
  /** Whether spec-brief loads this package's plugin, as its configuration at the root says. */
226
244
  export async function specBriefPlugin(root) {