dunstan 0.1.15 → 0.1.18

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 (3) hide show
  1. package/README.md +59 -7
  2. package/dist/dunstan.mjs +1676 -430
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,7 +26,7 @@ jobs:
26
26
  handback:
27
27
  runs-on: ubuntu-latest
28
28
  steps:
29
- - uses: BargLabs/dunstan@v0.1.15 # pin the full commit SHA in production
29
+ - uses: BargLabs/dunstan@v0.1.18 # pin the full commit SHA in production
30
30
  with:
31
31
  report-source: pr-body
32
32
  ```
@@ -52,7 +52,7 @@ export exists).
52
52
 
53
53
  ## Use with Claude
54
54
 
55
- Dunstan ships as a Claude Code plugin: two MCP tools and a skill that has Claude declare, check and
55
+ Dunstan ships as a Claude Code plugin: three MCP tools and a skill that has Claude declare, check and
56
56
  report a pull request's handback before it says done ([`plugins/dunstan/`](plugins/dunstan/)).
57
57
 
58
58
  **Install.** In Claude Code:
@@ -62,7 +62,7 @@ report a pull request's handback before it says done ([`plugins/dunstan/`](plugi
62
62
  /plugin install dunstan@barglabs
63
63
  ```
64
64
 
65
- The plugin runs the MCP server with `npx -y dunstan@0.1.15 mcp`, the `dunstan` npm package at that
65
+ The plugin runs the MCP server with `npx -y dunstan@0.1.18 mcp`, the `dunstan` npm package at that
66
66
  exact version: one self-contained file, no dependencies.
67
67
 
68
68
  **The token.** When you enable the plugin, Claude Code asks for an optional GitHub token and keeps it
@@ -108,6 +108,11 @@ pnpm install && pnpm build # writes dist/dunstan.mjs and prints its SHA
108
108
  GITHUB_TOKEN=... dist/dunstan.mjs check --repo <owner/name> --pr <n> --report-pr-body --out record.json
109
109
  # or --report-file <path|->, or --report-comment-author <login> (that author's latest comment)
110
110
 
111
+ # Check a GitLab merge request (DRAFT, see "GitLab (draft)" below). GITLAB_TOKEN is optional.
112
+ dist/dunstan.mjs check --gitlab-project <full path> --mr <iid> --report-mr-description --out record.json
113
+ # or --report-file <path|->; --gitlab-host <host> for a self-managed GitLab (default gitlab.com);
114
+ # --exclude-gitlab-job "$CI_JOB_ID" in a pipeline, so the checker's own job is not counted
115
+
111
116
  dist/dunstan.mjs verify record.json # offline: recompute claims, verdict and digests
112
117
  dist/dunstan.mjs rerun record.json # online: re-read the sources, report evidence_changed
113
118
  ```
@@ -202,8 +207,53 @@ Two changes make such claims checkable:
202
207
  (`local_run_not_in_record`), never `pass`.
203
208
 
204
209
  Reading the artifact needs a token: without one a `tests` row is `unverifiable`
205
- (`source_unreadable:artifact`). Each `tests` row the record cannot settle is printed with a
206
- `remedy:` line saying what to do.
210
+ (`source_unreadable:artifact`). A pull request that changes the workflow file its record names
211
+ wrote its own evidence, so its `tests` rows are `unverifiable` (`record_producer_changed_by_pr`,
212
+ spec 0.1.10): land the workflow change in a pull request of its own. Each `tests` row the record
213
+ cannot settle is printed with a `remedy:` line saying what to do.
214
+
215
+ ## GitLab (draft)
216
+
217
+ `dunstan check --gitlab-project <full path> --mr <iid>` checks a GitLab merge request on
218
+ gitlab.com, or on a self-managed instance with `--gitlab-host <host>`. The project is named by its
219
+ full path, `group/subgroup/.../project`. The report is `--report-mr-description` (the merge
220
+ request's description) or `--report-file <path|->`. It reads without a token; set `GITLAB_TOKEN`
221
+ to read what an anonymous reader cannot. The token is sent as a header and is never written into a
222
+ record, a source, a log line or an error. This is the
223
+ [DRAFT GitLab profile 0.1](spec/claim-format.md#draft-gitlab-profile-01), which the operator has
224
+ not yet made normative. Nothing about GitHub changes: every GitHub record is written byte for byte
225
+ as before.
226
+
227
+ Every evidence section has a GitLab source, so every claim a block makes can be settled:
228
+
229
+ - the merge request (its head, state, merge time and merge commit: the squash commit for a squash
230
+ merged by fast-forward), its changed files from the diffs listing, its `closes_issues`, and the
231
+ issues and commits a block names, with each `closes` target's state events (the head, scope,
232
+ `closes`, `cites` and `mergedAt` claims);
233
+ - `checks`: the latest jobs and the bridges of the merge request's head pipeline, which must be for
234
+ `headCommit`, by one job status table. A failed job counts as failed even when it is allowed to
235
+ fail, and a manual job never run as skipped. Run in a pipeline, pass the checker's own job,
236
+ `--exclude-gitlab-job "$CI_JOB_ID"`;
237
+ - a `tests[]` item whose `junit` record names `workflow: .gitlab-ci.yml` (the project's CI
238
+ configuration file), the `job`, `artifact: archive` and the `path` of the JUnit file in that
239
+ job's artifacts archive: Dunstan downloads the archive and counts the file itself, never GitLab's
240
+ parsed test report;
241
+ - `deployedAt`: the project's deployments to the named environment, where only `success` is a
242
+ successful deployment.
243
+
244
+ What has no GitLab equivalent, and so is never recorded: an issue's `exists: false` (GitLab
245
+ answers 404 for a confidential issue, so a `cites` or `closes` of one is
246
+ `source_unreadable:issue`, never `not_found`) and `resolvedAs`, a check run's suite, app and
247
+ workflow, a deployment's status events, a fast-forward merge's merged commit without a squash, the
248
+ `--report-comment-author` report and `--advisory`. A block names an issue of the subject project as
249
+ `#N` at any depth; it cannot name another subgroup project's issue in full, since the block format
250
+ admits two-segment references only.
251
+
252
+ A GitLab record has its own predicate type (`.../record/v0.1-gitlab-draft`) and schema
253
+ (`spec/schema/record-gitlab-0.1-draft.schema.json`), and carries
254
+ `predicate.subject.forge: {"kind": "gitlab", "host": ...}`. `dunstan verify` re-runs it offline,
255
+ and `dunstan rerun` re-reads it from the host it names. The GitHub Action, the MCP tool, the survey
256
+ and the hosted tier read GitHub only.
207
257
 
208
258
  ## The GitHub Action
209
259
 
@@ -288,11 +338,13 @@ baseline, a negated list or a deliberate test failure as claims, and since 0.1.3
288
338
  list or a merge time after a colon ([`docs/advisory.md`](docs/advisory.md), "Attribution" and
289
339
  "Lists, merge times and own references"). Extractor 0.1.4's rules did not generalise to held-out
290
340
  pull requests and were reverted ("Labels, used names and descriptions"). No figure is published
291
- for 0.1.3, so a record it writes says "unmeasured". The comparison that runs is 0.4.0: a file claim
341
+ for 0.1.3, so a record it writes says "unmeasured". The comparison that runs is 0.5.0: a file claim
292
342
  not among the changed files is a possible disagreement only when its path is a file at the head,
293
343
  and is unanswered when nothing or a directory is there ("Paths that are not at the head"), and a
294
344
  `closes` claim is settled by who closed the target, as the gate's is ("Closes read by who closed
295
- the target"). It is unmeasured too: the `differs` figure stays bound to comparison 0.2.0.
345
+ the target"), and a cited commit agrees when it exists, whether or not the head reaches it, as the
346
+ gate's `cites` does since spec 0.1.11 ("Cited commits that the head does not reach"). It is
347
+ unmeasured too: the `differs` figure stays bound to comparison 0.2.0.
296
348
 
297
349
  The base rate is always shown next to the `differs` figure: among the 44 advisories the record
298
350
  could check, none was a false claim (0 of 44), so 0 of 20 means there was little to find, not that