dunstan 0.1.12 → 0.1.14

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 +90 -4
  2. package/dist/dunstan.mjs +1634 -383
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Dunstan
2
2
 
3
+ <p align="left"><img src="brand/dunstan-icon.png" alt="Dunstan logo" width="96"></p>
4
+
5
+ **Verify an AI coding agent's pull request claims, with a verdict anyone can rerun.** The agent
6
+ says which files it changed, which tests ran and which issues it closed; Dunstan checks each claim
7
+ against the pull request, its commits and its CI runs, and reports the verdict as a check run. Every
8
+ verdict comes with a record: `dunstan verify` recomputes it offline and `dunstan rerun` re-reads
9
+ the sources, so a reviewer or customer can check it themselves. Deterministic, no LLM.
10
+
11
+ Add it to a workflow; the agent's report goes in the pull request body
12
+ (`npx -y dunstan template --base main` writes the block for you):
13
+
14
+ ```yaml
15
+ on:
16
+ pull_request:
17
+ types: [opened, edited, synchronize, reopened, ready_for_review]
18
+ permissions:
19
+ contents: read
20
+ pull-requests: read
21
+ issues: read
22
+ deployments: read
23
+ checks: write
24
+ actions: read
25
+ jobs:
26
+ handback:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: BargLabs/dunstan@v0.1.14 # pin the full commit SHA in production
30
+ with:
31
+ report-source: pr-body
32
+ ```
33
+
34
+ More in [`docs/action.md`](docs/action.md).
35
+
3
36
  Dunstan holds a coding agent's completion report to the record.
4
37
 
5
38
  An agent finishes and writes a handback: files changed, tests run, references closed, head commit,
@@ -29,15 +62,18 @@ report a pull request's handback before it says done ([`plugins/dunstan/`](plugi
29
62
  /plugin install dunstan@barglabs
30
63
  ```
31
64
 
32
- The plugin runs the MCP server with `npx -y dunstan@0.1.12 mcp`, the `dunstan` npm package at that
65
+ The plugin runs the MCP server with `npx -y dunstan@0.1.14 mcp`, the `dunstan` npm package at that
33
66
  exact version: one self-contained file, no dependencies.
34
67
 
35
68
  **The token.** When you enable the plugin, Claude Code asks for an optional GitHub token and keeps it
36
69
  in your system's secure credential store. Use a fine-grained personal access token with read-only
37
70
  permissions, limited to the repositories you check, with an expiry; the permissions are in
38
71
  [`docs/mcp.md`](docs/mcp.md#a-read-only-github-token). Leave it empty to read public repositories
39
- anonymously. GitHub's GraphQL API refuses anonymous calls, so without a token a `closes` reference is
40
- `unverifiable`, never `fail`. The plugin never reads a token from your environment.
72
+ anonymously. Two kinds of claim need a token, even on a public repository: GitHub's GraphQL API
73
+ refuses anonymous calls, so a `closes` reference is `unverifiable`, never `fail`; and GitHub answers
74
+ an artifact download only with a token (401 "Requires authentication" anonymously, observed
75
+ 2026-11-02), so a `tests` count is `unverifiable` (`source_unreadable:artifact`). The plugin never
76
+ reads a token from your environment.
41
77
 
42
78
  **What it reads, and from where.** The server runs on your machine and calls GitHub's API directly
43
79
  from it: the pull request, its changed files, commits, check runs, workflow runs and test artifacts,
@@ -96,6 +132,34 @@ handback covers what the issue asked for: an accurate report of the wrong work p
96
132
 
97
133
  What to declare: [`docs/agents.md`](docs/agents.md#the-instruction-block).
98
134
 
135
+ ## Survey: how much of a repository's handbacks can be checked
136
+
137
+ `dunstan survey` (DRAFT) runs `dunstan check` over a repository's recent closed pull requests and
138
+ reports settleability: which claims the record could not settle, why, and what would let it settle
139
+ them. It is read-only and needs no token to start.
140
+
141
+ ```sh
142
+ dist/dunstan.mjs survey --repo <owner/name> --last 30 [--author <agent-login>] --out survey.json --summary survey.md
143
+ dist/dunstan.mjs verify survey.json # offline: every embedded record, the selection, the aggregate and the digest
144
+ ```
145
+
146
+ It reports two rates, each with its numerator, denominator and Wilson 95% interval:
147
+
148
+ - **the share of claims the record could not settle**, (unverifiable + not record-checkable) / all
149
+ claims, led by a table of the reasons, each with its remedy;
150
+ - **the share of settled claims the record contradicts**, contradicted / (supported +
151
+ contradicted).
152
+
153
+ With `--advisory` it also runs the advisory prose reader and counts its notes in a table of their
154
+ own. Advisory notes never enter either rate. A tests claim can be settled only with `GITHUB_TOKEN`
155
+ set and a JUnit record that CI uploads: GitHub serves no artifact to an anonymous reader, and a
156
+ test run CI never recorded, or a local run declared as one, is not record-checkable. The survey
157
+ record says whether it read with a token, and an anonymous survey's summary says its
158
+ cannot-settle share is higher for it. The survey measures what the record can settle. A survey
159
+ record is unsigned: `verify` proves it consistent with itself and every embedded record valid, not
160
+ that its listing is what GitHub returned, so to trust its completeness, run it again online.
161
+ Details: [`docs/survey.md`](docs/survey.md).
162
+
99
163
  ## Declare before done
100
164
 
101
165
  Checking a declared claim is exact. Finding the claims in prose is where readers fail: a hosted
@@ -111,7 +175,8 @@ dist/dunstan.mjs suggest --report-file report.md # prose claims the block does
111
175
  ```
112
176
 
113
177
  `template` fills `headCommit` and `filesChanged` (HEAD against its merge base with the remote's
114
- default branch, or `--base`), and `tests` only from a JUnit file and the CI record named with it. It
178
+ default branch, or `--base`). With `--tests-ci` it fills `tests` from the JUnit record CI published
179
+ at HEAD, read as the gate reads it; with `--tests-local` it declares a local run as one (below). It
115
180
  never fills `references` or `mergedAt`, and when git cannot answer it prints no block. `suggest`
116
181
  runs the advisory extractor over the prose outside the fence and lists each claim it proposes that
117
182
  the block does not declare, to declare or reword. It is never a verdict, writes nothing to a record
@@ -119,6 +184,27 @@ and always exits 0; the MCP server offers it as `dunstan_suggest_declarations`.
119
184
  instruction block to copy into `AGENTS.md` or `CLAUDE.md`, and example output:
120
185
  [`docs/agents.md`](docs/agents.md).
121
186
 
187
+ ## Make test claims checkable
188
+
189
+ Most claims about tests in a completion report ("all tests pass", "142 passed") cannot be checked
190
+ from a repository's records today: the tests ran on the agent's machine, or CI ran them and
191
+ published no counts. Dunstan settles a `tests` claim only against a JUnit file a CI run uploaded.
192
+ Two changes make such claims checkable:
193
+
194
+ 1. **CI publishes the record.** One upload step after the test command: an artifact named
195
+ `dunstan-junit` holding `junit.xml`, uploaded even when the tests fail. Recipes for pytest,
196
+ jest, vitest, Maven, Gradle and Go, with `dunstan junit-merge` for runners that write one file
197
+ per test class: [`docs/ci-test-records.md`](docs/ci-test-records.md).
198
+ 2. **The agent declares CI's counts.** After CI has finished on its last push, `dunstan template
199
+ --tests-ci --tests-command <cmd> --tests-workflow <path> --tests-job <name>` reads the counts
200
+ from that record, as the gate will, and declares them. A run made only locally is declared with
201
+ `--tests-local <file>` as `{"kind": "local"}`, which the gate reports `unverifiable`
202
+ (`local_run_not_in_record`), never `pass`.
203
+
204
+ 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.
207
+
122
208
  ## The GitHub Action
123
209
 
124
210
  One step in a workflow, one required check in branch protection: