dunstan 0.1.7 → 0.1.9

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 +1 -1
  2. package/dist/dunstan.mjs +293 -114
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -27,7 +27,7 @@ report a pull request's handback before it says done ([`plugins/dunstan/`](plugi
27
27
  /plugin install dunstan@barglabs
28
28
  ```
29
29
 
30
- The plugin runs the MCP server with `npx -y dunstan@0.1.7 mcp`, the `dunstan` npm package at that
30
+ The plugin runs the MCP server with `npx -y dunstan@0.1.9 mcp`, the `dunstan` npm package at that
31
31
  exact version: one self-contained file, no dependencies.
32
32
 
33
33
  **The token.** When you enable the plugin, Claude Code asks for an optional GitHub token and keeps it
package/dist/dunstan.mjs CHANGED
@@ -8030,7 +8030,7 @@ import { readFileSync as readFileSync7 } from "node:fs";
8030
8030
  import { createHash } from "node:crypto";
8031
8031
 
8032
8032
  // src/spec/constants.ts
8033
- var SPEC_VERSION = "0.1.4";
8033
+ var SPEC_VERSION = "0.1.5";
8034
8034
  var BLOCK_VERSION = "0.1";
8035
8035
  var SUPPORTED_BLOCK_VERSIONS = [BLOCK_VERSION];
8036
8036
  var BLOCK_INFO_STRING = "dunstan-handback";
@@ -8468,6 +8468,14 @@ function closedBy(reference, field, evidence, repository, at, observed) {
8468
8468
  "not_closing"
8469
8469
  );
8470
8470
  }
8471
+ function closesBlock(issue2, headCommit) {
8472
+ return {
8473
+ dunstan: BLOCK_VERSION,
8474
+ headCommit,
8475
+ filesChanged: [],
8476
+ references: [{ issue: issue2, relation: "closes" }]
8477
+ };
8478
+ }
8471
8479
  function checkReferences(block, evidence, repository) {
8472
8480
  return (block.references ?? []).map((reference, i) => {
8473
8481
  const field = `/references/${i}`;
@@ -9460,6 +9468,7 @@ async function readCheckRuns(client, repository, commit, excludedIds) {
9460
9468
  const workflow = run.checkSuiteId === void 0 ? void 0 : workflowRuns.get(run.checkSuiteId);
9461
9469
  if (workflow === void 0) continue;
9462
9470
  run.workflowPath = workflow.path;
9471
+ if (workflow.name !== void 0) run.workflowName = workflow.name;
9463
9472
  run.workflowRunId = workflow.id;
9464
9473
  run.runAttempt = workflow.attempt;
9465
9474
  }
@@ -9487,6 +9496,8 @@ async function readWorkflowRunsAt(client, repository, commit) {
9487
9496
  const run = { id: int(r.id), attempt: int(r.run_attempt), path: str(r.path) };
9488
9497
  run.path = run.path.replace(/@.*$/, "");
9489
9498
  if (run.id < 1 || run.attempt < 1 || run.path === "") throw new ShapeError();
9499
+ const name = strOrNull(r.name);
9500
+ if (name !== null && name !== "") run.name = name;
9490
9501
  return { suite, run };
9491
9502
  }
9492
9503
  );
@@ -9741,7 +9752,7 @@ async function reportFromComment(client, repository, number4, login) {
9741
9752
  // src/record/checker.ts
9742
9753
  import { readFileSync } from "node:fs";
9743
9754
  var CHECKER_NAME = "dunstan";
9744
- var CHECKER_VERSION = "0.1.7";
9755
+ var CHECKER_VERSION = "0.1.9";
9745
9756
  function checkerIdentity(artifact) {
9746
9757
  return {
9747
9758
  name: CHECKER_NAME,
@@ -9914,7 +9925,7 @@ var FILES = { "handback-block-0.1.schema.json": `{
9914
9925
  }
9915
9926
  }
9916
9927
  }
9917
- `, "record-0.1.schema.json": '{\n "$schema": "https://json-schema.org/draft/2020-12/schema",\n "$id": "https://barglabs.ai/dunstan/spec/schema/record-0.1.schema.json",\n "title": "Dunstan record, v0.1",\n "description": "An in-toto Statement v1 whose predicate records one check of a handback block against the record. See spec/claim-format.md.",\n "type": "object",\n "required": ["_type", "subject", "predicateType", "predicate"],\n "additionalProperties": false,\n "properties": {\n "_type": { "const": "https://in-toto.io/Statement/v1" },\n "subject": {\n "type": "array",\n "minItems": 1,\n "maxItems": 2,\n "prefixItems": [{ "$ref": "#/$defs/commitSubject" }, { "$ref": "#/$defs/blockSubject" }],\n "items": false\n },\n "predicateType": { "const": "https://barglabs.ai/dunstan/record/v0.1" },\n "predicate": { "$ref": "#/$defs/predicate" }\n },\n "allOf": [\n {\n "if": {\n "properties": {\n "predicate": {\n "properties": { "block": { "properties": { "status": { "const": "found" } } } }\n }\n }\n },\n "then": { "properties": { "subject": { "minItems": 2 } } },\n "else": { "properties": { "subject": { "maxItems": 1 } } }\n }\n ],\n "$defs": {\n "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },\n "commitSha": { "type": "string", "pattern": "^[0-9a-f]{40}$" },\n "repository": {\n "description": "owner/repo",\n "type": "string",\n "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}$"\n },\n "issueRef": {\n "description": "owner/repo#N, always fully qualified in evidence.",\n "type": "string",\n "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}#[1-9][0-9]{0,9}$"\n },\n "timestamp": {\n "type": "string",\n "format": "date-time",\n "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\\\\.[0-9]+)?Z$"\n },\n "sourceKind": {\n "enum": [\n "pull_request",\n "pull_request_files",\n "pull_request_commits",\n "closing_references",\n "repository",\n "issue",\n "commit",\n "compare",\n "check_runs",\n "workflow_runs",\n "artifact",\n "deployments"\n ]\n },\n "commitSubject": {\n "type": "object",\n "required": ["name", "digest"],\n "additionalProperties": false,\n "properties": {\n "name": {\n "type": "string",\n "pattern": "^git\\\\+https://github\\\\.com/[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}@[0-9a-f]{40}$"\n },\n "digest": {\n "type": "object",\n "required": ["gitCommit"],\n "additionalProperties": false,\n "properties": { "gitCommit": { "$ref": "#/$defs/commitSha" } }\n }\n }\n },\n "blockSubject": {\n "type": "object",\n "required": ["name", "digest"],\n "additionalProperties": false,\n "properties": {\n "name": { "const": "handback-block" },\n "digest": {\n "type": "object",\n "required": ["sha256"],\n "additionalProperties": false,\n "properties": { "sha256": { "$ref": "#/$defs/sha256" } }\n }\n }\n },\n "predicate": {\n "type": "object",\n "required": [\n "spec",\n "checker",\n "report",\n "block",\n "subject",\n "evidence",\n "claims",\n "verdict",\n "digests",\n "rerun",\n "assurance"\n ],\n "additionalProperties": false,\n "properties": {\n "spec": {\n "description": "The spec version the checker implements, semver.",\n "type": "string",\n "pattern": "^0\\\\.1\\\\.(?:0|[1-9][0-9]*)$"\n },\n "checker": {\n "type": "object",\n "required": ["name", "version", "digest"],\n "additionalProperties": false,\n "properties": {\n "name": { "type": "string", "minLength": 1, "maxLength": 214 },\n "version": { "type": "string", "minLength": 1, "maxLength": 100 },\n "digest": {\n "type": "object",\n "required": ["sha256"],\n "additionalProperties": false,\n "properties": { "sha256": { "$ref": "#/$defs/sha256" } }\n }\n }\n },\n "report": {\n "description": "The report\'s digest and where it was read. Never its text.",\n "type": "object",\n "required": ["sha256", "source"],\n "additionalProperties": false,\n "properties": {\n "sha256": { "$ref": "#/$defs/sha256" },\n "source": {\n "type": "object",\n "required": ["kind", "locator"],\n "additionalProperties": false,\n "properties": {\n "kind": { "enum": ["pr-body", "pr-comment", "file", "stdin", "api"] },\n "locator": { "type": "string", "minLength": 1, "maxLength": 2000 }\n }\n }\n }\n },\n "block": { "$ref": "#/$defs/block" },\n "subject": {\n "type": "object",\n "required": ["repository", "pullRequest", "headSha", "mergeSha"],\n "additionalProperties": false,\n "properties": {\n "repository": { "$ref": "#/$defs/repository" },\n "pullRequest": { "type": "integer", "minimum": 1 },\n "headSha": { "$ref": "#/$defs/commitSha" },\n "mergeSha": {\n "oneOf": [{ "$ref": "#/$defs/commitSha" }, { "type": "null" }]\n }\n }\n },\n "evidence": { "$ref": "#/$defs/evidence" },\n "claims": {\n "type": "array",\n "items": { "$ref": "#/$defs/claim" }\n },\n "verdict": { "$ref": "#/$defs/verdict" },\n "digests": {\n "type": "object",\n "required": ["claims", "evidence"],\n "additionalProperties": false,\n "properties": {\n "claims": { "$ref": "#/$defs/sha256" },\n "evidence": { "$ref": "#/$defs/sha256" }\n }\n },\n "rerun": {\n "type": "object",\n "required": ["offline", "online"],\n "additionalProperties": false,\n "properties": {\n "offline": { "type": "string", "minLength": 1, "maxLength": 2000 },\n "online": { "type": "string", "minLength": 1, "maxLength": 2000 }\n }\n },\n "assurance": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "issuer"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "unsigned" },\n "issuer": { "const": "self-generated" }\n }\n },\n {\n "type": "object",\n "required": ["status", "issuer", "keyFingerprint"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "signed" },\n "issuer": { "type": "string", "minLength": 1, "maxLength": 320 },\n "keyFingerprint": { "type": "string", "pattern": "^SHA256:[A-Za-z0-9+/]{43}$" }\n }\n }\n ]\n }\n }\n },\n "verdict": { "enum": ["pass", "fail", "unverifiable"] },\n "blockError": {\n "type": "object",\n "required": ["code"],\n "additionalProperties": false,\n "properties": {\n "code": {\n "enum": [\n "unterminated_fence",\n "invalid_json",\n "duplicate_member",\n "unsupported_version",\n "schema_violation"\n ]\n },\n "pointer": { "type": "string", "pattern": "^(?:/.*)?$" },\n "keyword": { "type": "string", "minLength": 1 }\n }\n },\n "block": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "found" },\n "sha256": { "$ref": "#/$defs/sha256" },\n "value": { "$ref": "handback-block-0.1.schema.json" }\n }\n },\n {\n "type": "object",\n "required": ["status", "reason", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "missing" },\n "reason": { "const": "block_missing" },\n "sha256": { "type": "null" },\n "value": { "type": "null" }\n }\n },\n {\n "type": "object",\n "required": ["status", "reason", "count", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ambiguous" },\n "reason": { "const": "block_ambiguous" },\n "count": { "type": "integer", "minimum": 2 },\n "sha256": { "type": "null" },\n "value": { "type": "null" }\n }\n },\n {\n "type": "object",\n "required": ["status", "reason", "errors", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "invalid" },\n "reason": { "const": "block_invalid" },\n "errors": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/blockError" } },\n "sha256": { "type": "null" },\n "value": { "type": "null" }\n }\n }\n ]\n },\n "claim": {\n "type": "object",\n "required": ["id", "check", "field", "declared", "observed", "verdict"],\n "additionalProperties": false,\n "properties": {\n "id": { "type": "string", "minLength": 1 },\n "check": { "enum": ["head", "scope", "reference", "count", "time"] },\n "field": {\n "description": "A JSON Pointer (RFC 6901) into the block.",\n "type": "string",\n "pattern": "^/.+$"\n },\n "declared": true,\n "observed": true,\n "verdict": { "$ref": "#/$defs/verdict" },\n "reason": { "$ref": "#/$defs/claimReason" }\n },\n "if": { "properties": { "verdict": { "const": "pass" } } },\n "then": { "not": { "required": ["reason"] } },\n "else": { "required": ["reason"] }\n },\n "claimReason": {\n "type": "string",\n "pattern": "^(?:head_mismatch|declared_not_changed|undeclared_file|file_list_truncated|not_closing|closing_link_unsettled|closing_base_not_default|closing_event_unsettled|not_found|not_reachable|count_mismatch|all_succeeded_mismatch|checks_incomplete|no_check_runs|no_comparable_record_field|record_not_found|record_ambiguous|not_merged|premature|no_deployment|evidence_field_unpopulated:[A-Za-z0-9_.]+|source_unreadable:(?:pull_request|pull_request_files|pull_request_commits|closing_references|repository|issue|commit|compare|check_runs|workflow_runs|artifact|deployments))$"\n },\n "unread": {\n "description": "A section the checker could not use: its source errored, or the source answered without the field.",\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "source"],\n "properties": {\n "status": { "const": "unreadable" },\n "source": { "$ref": "#/$defs/sourceKind" }\n }\n },\n {\n "type": "object",\n "required": ["status", "field"],\n "properties": {\n "status": { "const": "unpopulated" },\n "field": { "type": "string", "pattern": "^[A-Za-z0-9_.]+$" }\n }\n }\n ]\n },\n "evidence": {\n "type": "object",\n "required": ["pullRequest", "sources"],\n "additionalProperties": false,\n "properties": {\n "pullRequest": {\n "description": "Always read: a checker that cannot read the pull request writes no record.",\n "type": "object",\n "required": [\n "status",\n "number",\n "state",\n "merged",\n "mergedAt",\n "headSha",\n "mergeSha",\n "changedFiles"\n ],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "number": { "type": "integer", "minimum": 1 },\n "state": { "enum": ["open", "closed"] },\n "merged": { "type": "boolean" },\n "mergedAt": { "oneOf": [{ "$ref": "#/$defs/timestamp" }, { "type": "null" }] },\n "headSha": { "$ref": "#/$defs/commitSha" },\n "mergeSha": { "oneOf": [{ "$ref": "#/$defs/commitSha" }, { "type": "null" }] },\n "changedFiles": { "type": "integer", "minimum": 0 }\n }\n },\n "files": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "complete", "entries"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "complete": { "type": "boolean" },\n "entries": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["path", "status"],\n "additionalProperties": false,\n "properties": {\n "path": { "type": "string", "minLength": 1 },\n "status": {\n "enum": [\n "added",\n "removed",\n "modified",\n "renamed",\n "copied",\n "changed",\n "unchanged"\n ]\n },\n "previousPath": { "type": "string", "minLength": 1 }\n }\n }\n }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "closingReferences": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "issues"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "issues": { "type": "array", "items": { "$ref": "#/$defs/issueRef" } }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "references": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["kind", "ref", "status", "exists"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "issue" },\n "ref": { "$ref": "#/$defs/issueRef" },\n "status": { "const": "ok" },\n "exists": { "type": "boolean" },\n "resolvedAs": {\n "description": "Spec 0.1.3: the owner/repo#N GitHub answered the read of ref with, when it is another issue (an issue transferred to another repository).",\n "$ref": "#/$defs/issueRef"\n }\n },\n "if": { "required": ["resolvedAs"] },\n "then": { "properties": { "exists": { "const": true } } }\n },\n {\n "type": "object",\n "required": ["kind", "ref", "status", "exists", "reachableFromHead"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "commit" },\n "ref": { "$ref": "#/$defs/commitSha" },\n "status": { "const": "ok" },\n "exists": { "type": "boolean" },\n "reachableFromHead": { "type": "boolean" }\n }\n },\n {\n "type": "object",\n "required": ["kind", "ref"],\n "properties": {\n "kind": { "enum": ["issue", "commit"] },\n "ref": { "type": "string", "minLength": 1 }\n },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n },\n "closeEvents": {\n "description": "Spec 0.1.4: who closed each closes target that exists and is not among the closing references, read under the identity it resolves to; and the pull request\'s base, its repository\'s default branch and its commit ids.",\n "type": "object",\n "required": ["pullRequest", "targets"],\n "additionalProperties": false,\n "properties": {\n "pullRequest": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "baseRef", "defaultBranch", "commits"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "baseRef": { "type": "string", "minLength": 1 },\n "defaultBranch": {\n "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }]\n },\n "commits": {\n "type": "array",\n "uniqueItems": true,\n "items": { "$ref": "#/$defs/commitSha" }\n }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "targets": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["ref", "status", "type", "state", "events"],\n "additionalProperties": false,\n "properties": {\n "ref": { "$ref": "#/$defs/issueRef" },\n "status": { "const": "ok" },\n "type": { "enum": ["issue", "pull_request"] },\n "state": { "enum": ["open", "closed", "merged"] },\n "events": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["at", "closer"],\n "additionalProperties": false,\n "properties": {\n "at": { "$ref": "#/$defs/timestamp" },\n "closer": {\n "oneOf": [\n { "type": "null" },\n {\n "type": "object",\n "required": ["kind", "ref"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "pull_request" },\n "ref": { "$ref": "#/$defs/issueRef" }\n }\n },\n {\n "type": "object",\n "required": ["kind", "oid"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "commit" },\n "oid": { "$ref": "#/$defs/commitSha" }\n }\n },\n {\n "type": "object",\n "required": ["kind"],\n "additionalProperties": false,\n "properties": { "kind": { "const": "other" } }\n }\n ]\n }\n }\n }\n }\n },\n "if": { "properties": { "type": { "const": "issue" } } },\n "then": { "properties": { "state": { "enum": ["open", "closed"] } } }\n },\n {\n "type": "object",\n "required": ["ref"],\n "properties": { "ref": { "$ref": "#/$defs/issueRef" } },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n }\n }\n },\n "checkRuns": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "commit", "excludedIds", "runs"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "commit": { "$ref": "#/$defs/commitSha" },\n "excludedIds": {\n "description": "Check runs left out of the count: the run the checker itself executes in.",\n "type": "array",\n "items": { "type": "integer", "minimum": 1 }\n },\n "runs": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["id", "name", "status", "conclusion"],\n "additionalProperties": false,\n "dependentRequired": {\n "workflowPath": ["checkSuiteId", "workflowRunId", "runAttempt"],\n "workflowRunId": ["workflowPath"],\n "runAttempt": ["workflowPath"]\n },\n "properties": {\n "id": { "type": "integer", "minimum": 1 },\n "name": { "type": "string" },\n "status": { "type": "string", "minLength": 1 },\n "conclusion": { "type": ["string", "null"] },\n "checkSuiteId": {\n "description": "The run\'s check suite (`check_suite.id`). A run without `workflowPath` is counted as the latest per check suite and name; a run without either is keyed by its name alone. Added in spec 0.1.4, amended 2026-10-12.",\n "type": "integer",\n "minimum": 1\n },\n "appSlug": {\n "description": "The slug of the app that created the run (`app.slug`). Recorded, not read by any check. Added in spec 0.1.4, amended 2026-10-12.",\n "type": "string",\n "minLength": 1\n },\n "workflowPath": {\n "description": "The `path` of the workflow run whose `check_suite_id` is the run\'s check suite, less any `@<ref>`, from the workflow runs at the head. Runs that have it are counted as the latest per workflow path and name. Added in spec 0.1.4, amended 2026-10-13.",\n "type": "string",\n "minLength": 1\n },\n "workflowRunId": {\n "description": "The `id` of that workflow run. Added in spec 0.1.4, amended 2026-10-13.",\n "type": "integer",\n "minimum": 1\n },\n "runAttempt": {\n "description": "That workflow run\'s `run_attempt` as the listing gives it, its latest attempt at the read. Added in spec 0.1.4, amended 2026-10-13.",\n "type": "integer",\n "minimum": 1\n }\n }\n }\n }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "testRecords": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["record", "status", "runs"],\n "additionalProperties": false,\n "properties": {\n "record": { "$ref": "handback-block-0.1.schema.json#/$defs/junitRecord" },\n "status": { "const": "ok" },\n "runs": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["runId", "runAttempt", "executed", "failed"],\n "additionalProperties": false,\n "properties": {\n "runId": { "type": "integer", "minimum": 1 },\n "runAttempt": { "type": "integer", "minimum": 1 },\n "executed": { "type": "integer", "minimum": 0 },\n "failed": { "type": "integer", "minimum": 0 }\n }\n }\n }\n }\n },\n {\n "type": "object",\n "required": ["record"],\n "properties": {\n "record": { "$ref": "handback-block-0.1.schema.json#/$defs/junitRecord" }\n },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n },\n "deployments": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["environment", "status", "deployments"],\n "additionalProperties": false,\n "properties": {\n "environment": { "type": "string", "minLength": 1 },\n "status": { "const": "ok" },\n "deployments": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["id", "sha", "relation", "successAt"],\n "additionalProperties": false,\n "properties": {\n "id": { "type": "integer", "minimum": 1 },\n "sha": { "$ref": "#/$defs/commitSha" },\n "relation": {\n "enum": ["head", "merge", "descendant", "unrelated", "unknown"]\n },\n "successAt": {\n "oneOf": [{ "$ref": "#/$defs/timestamp" }, { "type": "null" }]\n }\n }\n }\n }\n }\n },\n {\n "type": "object",\n "required": ["environment"],\n "properties": { "environment": { "type": "string", "minLength": 1 } },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n },\n "sources": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["kind", "locator", "sha256", "readAt"],\n "additionalProperties": false,\n "properties": {\n "kind": { "$ref": "#/$defs/sourceKind" },\n "locator": { "type": "string", "minLength": 1, "maxLength": 2000 },\n "sha256": { "$ref": "#/$defs/sha256" },\n "etag": { "type": "string", "minLength": 1 },\n "readAt": { "$ref": "#/$defs/timestamp" },\n "error": {\n "description": "Present when the read failed: http_<status>, network, or parse.",\n "type": "string",\n "pattern": "^(?:http_[1-5][0-9]{2}|network|parse)$"\n }\n }\n }\n }\n }\n },\n "unreadOnly": {\n "allOf": [{ "$ref": "#/$defs/unread" }],\n "unevaluatedProperties": false\n }\n }\n}\n', "record-0.2-draft.schema.json": `{
9928
+ `, "record-0.1.schema.json": '{\n "$schema": "https://json-schema.org/draft/2020-12/schema",\n "$id": "https://barglabs.ai/dunstan/spec/schema/record-0.1.schema.json",\n "title": "Dunstan record, v0.1",\n "description": "An in-toto Statement v1 whose predicate records one check of a handback block against the record. See spec/claim-format.md.",\n "type": "object",\n "required": ["_type", "subject", "predicateType", "predicate"],\n "additionalProperties": false,\n "properties": {\n "_type": { "const": "https://in-toto.io/Statement/v1" },\n "subject": {\n "type": "array",\n "minItems": 1,\n "maxItems": 2,\n "prefixItems": [{ "$ref": "#/$defs/commitSubject" }, { "$ref": "#/$defs/blockSubject" }],\n "items": false\n },\n "predicateType": { "const": "https://barglabs.ai/dunstan/record/v0.1" },\n "predicate": { "$ref": "#/$defs/predicate" }\n },\n "allOf": [\n {\n "if": {\n "properties": {\n "predicate": {\n "properties": { "block": { "properties": { "status": { "const": "found" } } } }\n }\n }\n },\n "then": { "properties": { "subject": { "minItems": 2 } } },\n "else": { "properties": { "subject": { "maxItems": 1 } } }\n }\n ],\n "$defs": {\n "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },\n "commitSha": { "type": "string", "pattern": "^[0-9a-f]{40}$" },\n "repository": {\n "description": "owner/repo",\n "type": "string",\n "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}$"\n },\n "issueRef": {\n "description": "owner/repo#N, always fully qualified in evidence.",\n "type": "string",\n "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}#[1-9][0-9]{0,9}$"\n },\n "timestamp": {\n "type": "string",\n "format": "date-time",\n "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\\\\.[0-9]+)?Z$"\n },\n "sourceKind": {\n "enum": [\n "pull_request",\n "pull_request_files",\n "pull_request_commits",\n "closing_references",\n "repository",\n "issue",\n "commit",\n "compare",\n "check_runs",\n "workflow_runs",\n "artifact",\n "deployments"\n ]\n },\n "commitSubject": {\n "type": "object",\n "required": ["name", "digest"],\n "additionalProperties": false,\n "properties": {\n "name": {\n "type": "string",\n "pattern": "^git\\\\+https://github\\\\.com/[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}@[0-9a-f]{40}$"\n },\n "digest": {\n "type": "object",\n "required": ["gitCommit"],\n "additionalProperties": false,\n "properties": { "gitCommit": { "$ref": "#/$defs/commitSha" } }\n }\n }\n },\n "blockSubject": {\n "type": "object",\n "required": ["name", "digest"],\n "additionalProperties": false,\n "properties": {\n "name": { "const": "handback-block" },\n "digest": {\n "type": "object",\n "required": ["sha256"],\n "additionalProperties": false,\n "properties": { "sha256": { "$ref": "#/$defs/sha256" } }\n }\n }\n },\n "predicate": {\n "type": "object",\n "required": [\n "spec",\n "checker",\n "report",\n "block",\n "subject",\n "evidence",\n "claims",\n "verdict",\n "digests",\n "rerun",\n "assurance"\n ],\n "additionalProperties": false,\n "properties": {\n "spec": {\n "description": "The spec version the checker implements, semver.",\n "type": "string",\n "pattern": "^0\\\\.1\\\\.(?:0|[1-9][0-9]*)$"\n },\n "checker": {\n "type": "object",\n "required": ["name", "version", "digest"],\n "additionalProperties": false,\n "properties": {\n "name": { "type": "string", "minLength": 1, "maxLength": 214 },\n "version": { "type": "string", "minLength": 1, "maxLength": 100 },\n "digest": {\n "type": "object",\n "required": ["sha256"],\n "additionalProperties": false,\n "properties": { "sha256": { "$ref": "#/$defs/sha256" } }\n }\n }\n },\n "report": {\n "description": "The report\'s digest and where it was read. Never its text.",\n "type": "object",\n "required": ["sha256", "source"],\n "additionalProperties": false,\n "properties": {\n "sha256": { "$ref": "#/$defs/sha256" },\n "source": {\n "type": "object",\n "required": ["kind", "locator"],\n "additionalProperties": false,\n "properties": {\n "kind": { "enum": ["pr-body", "pr-comment", "file", "stdin", "api"] },\n "locator": { "type": "string", "minLength": 1, "maxLength": 2000 }\n }\n }\n }\n },\n "block": { "$ref": "#/$defs/block" },\n "subject": {\n "type": "object",\n "required": ["repository", "pullRequest", "headSha", "mergeSha"],\n "additionalProperties": false,\n "properties": {\n "repository": { "$ref": "#/$defs/repository" },\n "pullRequest": { "type": "integer", "minimum": 1 },\n "headSha": { "$ref": "#/$defs/commitSha" },\n "mergeSha": {\n "oneOf": [{ "$ref": "#/$defs/commitSha" }, { "type": "null" }]\n }\n }\n },\n "evidence": { "$ref": "#/$defs/evidence" },\n "claims": {\n "type": "array",\n "items": { "$ref": "#/$defs/claim" }\n },\n "verdict": { "$ref": "#/$defs/verdict" },\n "digests": {\n "type": "object",\n "required": ["claims", "evidence"],\n "additionalProperties": false,\n "properties": {\n "claims": { "$ref": "#/$defs/sha256" },\n "evidence": { "$ref": "#/$defs/sha256" }\n }\n },\n "rerun": {\n "type": "object",\n "required": ["offline", "online"],\n "additionalProperties": false,\n "properties": {\n "offline": { "type": "string", "minLength": 1, "maxLength": 2000 },\n "online": { "type": "string", "minLength": 1, "maxLength": 2000 }\n }\n },\n "assurance": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "issuer"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "unsigned" },\n "issuer": { "const": "self-generated" }\n }\n },\n {\n "type": "object",\n "required": ["status", "issuer", "keyFingerprint"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "signed" },\n "issuer": { "type": "string", "minLength": 1, "maxLength": 320 },\n "keyFingerprint": { "type": "string", "pattern": "^SHA256:[A-Za-z0-9+/]{43}$" }\n }\n }\n ]\n }\n }\n },\n "verdict": { "enum": ["pass", "fail", "unverifiable"] },\n "blockError": {\n "type": "object",\n "required": ["code"],\n "additionalProperties": false,\n "properties": {\n "code": {\n "enum": [\n "unterminated_fence",\n "invalid_json",\n "duplicate_member",\n "unsupported_version",\n "schema_violation"\n ]\n },\n "pointer": { "type": "string", "pattern": "^(?:/.*)?$" },\n "keyword": { "type": "string", "minLength": 1 }\n }\n },\n "block": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "found" },\n "sha256": { "$ref": "#/$defs/sha256" },\n "value": { "$ref": "handback-block-0.1.schema.json" }\n }\n },\n {\n "type": "object",\n "required": ["status", "reason", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "missing" },\n "reason": { "const": "block_missing" },\n "sha256": { "type": "null" },\n "value": { "type": "null" }\n }\n },\n {\n "type": "object",\n "required": ["status", "reason", "count", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ambiguous" },\n "reason": { "const": "block_ambiguous" },\n "count": { "type": "integer", "minimum": 2 },\n "sha256": { "type": "null" },\n "value": { "type": "null" }\n }\n },\n {\n "type": "object",\n "required": ["status", "reason", "errors", "sha256", "value"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "invalid" },\n "reason": { "const": "block_invalid" },\n "errors": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/blockError" } },\n "sha256": { "type": "null" },\n "value": { "type": "null" }\n }\n }\n ]\n },\n "claim": {\n "type": "object",\n "required": ["id", "check", "field", "declared", "observed", "verdict"],\n "additionalProperties": false,\n "properties": {\n "id": { "type": "string", "minLength": 1 },\n "check": { "enum": ["head", "scope", "reference", "count", "time"] },\n "field": {\n "description": "A JSON Pointer (RFC 6901) into the block.",\n "type": "string",\n "pattern": "^/.+$"\n },\n "declared": true,\n "observed": true,\n "verdict": { "$ref": "#/$defs/verdict" },\n "reason": { "$ref": "#/$defs/claimReason" }\n },\n "if": { "properties": { "verdict": { "const": "pass" } } },\n "then": { "not": { "required": ["reason"] } },\n "else": { "required": ["reason"] }\n },\n "claimReason": {\n "type": "string",\n "pattern": "^(?:head_mismatch|declared_not_changed|undeclared_file|file_list_truncated|not_closing|closing_link_unsettled|closing_base_not_default|closing_event_unsettled|not_found|not_reachable|count_mismatch|all_succeeded_mismatch|checks_incomplete|no_check_runs|no_comparable_record_field|record_not_found|record_ambiguous|not_merged|premature|no_deployment|evidence_field_unpopulated:[A-Za-z0-9_.]+|source_unreadable:(?:pull_request|pull_request_files|pull_request_commits|closing_references|repository|issue|commit|compare|check_runs|workflow_runs|artifact|deployments))$"\n },\n "unread": {\n "description": "A section the checker could not use: its source errored, or the source answered without the field.",\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "source"],\n "properties": {\n "status": { "const": "unreadable" },\n "source": { "$ref": "#/$defs/sourceKind" }\n }\n },\n {\n "type": "object",\n "required": ["status", "field"],\n "properties": {\n "status": { "const": "unpopulated" },\n "field": { "type": "string", "pattern": "^[A-Za-z0-9_.]+$" }\n }\n }\n ]\n },\n "evidence": {\n "type": "object",\n "required": ["pullRequest", "sources"],\n "additionalProperties": false,\n "properties": {\n "pullRequest": {\n "description": "Always read: a checker that cannot read the pull request writes no record.",\n "type": "object",\n "required": [\n "status",\n "number",\n "state",\n "merged",\n "mergedAt",\n "headSha",\n "mergeSha",\n "changedFiles"\n ],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "number": { "type": "integer", "minimum": 1 },\n "state": { "enum": ["open", "closed"] },\n "merged": { "type": "boolean" },\n "mergedAt": { "oneOf": [{ "$ref": "#/$defs/timestamp" }, { "type": "null" }] },\n "headSha": { "$ref": "#/$defs/commitSha" },\n "mergeSha": { "oneOf": [{ "$ref": "#/$defs/commitSha" }, { "type": "null" }] },\n "changedFiles": { "type": "integer", "minimum": 0 }\n }\n },\n "files": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "complete", "entries"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "complete": { "type": "boolean" },\n "entries": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["path", "status"],\n "additionalProperties": false,\n "properties": {\n "path": { "type": "string", "minLength": 1 },\n "status": {\n "enum": [\n "added",\n "removed",\n "modified",\n "renamed",\n "copied",\n "changed",\n "unchanged"\n ]\n },\n "previousPath": { "type": "string", "minLength": 1 }\n }\n }\n }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "closingReferences": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "issues"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "issues": { "type": "array", "items": { "$ref": "#/$defs/issueRef" } }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "references": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["kind", "ref", "status", "exists"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "issue" },\n "ref": { "$ref": "#/$defs/issueRef" },\n "status": { "const": "ok" },\n "exists": { "type": "boolean" },\n "resolvedAs": {\n "description": "Spec 0.1.3: the owner/repo#N GitHub answered the read of ref with, when it is another issue (an issue transferred to another repository).",\n "$ref": "#/$defs/issueRef"\n }\n },\n "if": { "required": ["resolvedAs"] },\n "then": { "properties": { "exists": { "const": true } } }\n },\n {\n "type": "object",\n "required": ["kind", "ref", "status", "exists", "reachableFromHead"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "commit" },\n "ref": { "$ref": "#/$defs/commitSha" },\n "status": { "const": "ok" },\n "exists": { "type": "boolean" },\n "reachableFromHead": { "type": "boolean" }\n }\n },\n {\n "type": "object",\n "required": ["kind", "ref"],\n "properties": {\n "kind": { "enum": ["issue", "commit"] },\n "ref": { "type": "string", "minLength": 1 }\n },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n },\n "closeEvents": {\n "description": "Spec 0.1.4: who closed each closes target that exists and is not among the closing references, read under the identity it resolves to; and the pull request\'s base, its repository\'s default branch and its commit ids.",\n "type": "object",\n "required": ["pullRequest", "targets"],\n "additionalProperties": false,\n "properties": {\n "pullRequest": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "baseRef", "defaultBranch", "commits"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "baseRef": { "type": "string", "minLength": 1 },\n "defaultBranch": {\n "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }]\n },\n "commits": {\n "type": "array",\n "uniqueItems": true,\n "items": { "$ref": "#/$defs/commitSha" }\n }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "targets": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["ref", "status", "type", "state", "events"],\n "additionalProperties": false,\n "properties": {\n "ref": { "$ref": "#/$defs/issueRef" },\n "status": { "const": "ok" },\n "type": { "enum": ["issue", "pull_request"] },\n "state": { "enum": ["open", "closed", "merged"] },\n "events": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["at", "closer"],\n "additionalProperties": false,\n "properties": {\n "at": { "$ref": "#/$defs/timestamp" },\n "closer": {\n "oneOf": [\n { "type": "null" },\n {\n "type": "object",\n "required": ["kind", "ref"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "pull_request" },\n "ref": { "$ref": "#/$defs/issueRef" }\n }\n },\n {\n "type": "object",\n "required": ["kind", "oid"],\n "additionalProperties": false,\n "properties": {\n "kind": { "const": "commit" },\n "oid": { "$ref": "#/$defs/commitSha" }\n }\n },\n {\n "type": "object",\n "required": ["kind"],\n "additionalProperties": false,\n "properties": { "kind": { "const": "other" } }\n }\n ]\n }\n }\n }\n }\n },\n "if": { "properties": { "type": { "const": "issue" } } },\n "then": { "properties": { "state": { "enum": ["open", "closed"] } } }\n },\n {\n "type": "object",\n "required": ["ref"],\n "properties": { "ref": { "$ref": "#/$defs/issueRef" } },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n }\n }\n },\n "checkRuns": {\n "oneOf": [\n {\n "type": "object",\n "required": ["status", "commit", "excludedIds", "runs"],\n "additionalProperties": false,\n "properties": {\n "status": { "const": "ok" },\n "commit": { "$ref": "#/$defs/commitSha" },\n "excludedIds": {\n "description": "Check runs left out of the count: the run the checker itself executes in.",\n "type": "array",\n "items": { "type": "integer", "minimum": 1 }\n },\n "runs": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["id", "name", "status", "conclusion"],\n "additionalProperties": false,\n "dependentRequired": {\n "workflowPath": ["checkSuiteId", "workflowRunId", "runAttempt"],\n "workflowName": ["workflowPath"],\n "workflowRunId": ["workflowPath"],\n "runAttempt": ["workflowPath"]\n },\n "properties": {\n "id": { "type": "integer", "minimum": 1 },\n "name": { "type": "string" },\n "status": { "type": "string", "minLength": 1 },\n "conclusion": { "type": ["string", "null"] },\n "checkSuiteId": {\n "description": "The run\'s check suite (`check_suite.id`). A run without `workflowPath` is counted as the latest per check suite and name; a run without either is keyed by its name alone. Added in spec 0.1.4, amended 2026-10-12.",\n "type": "integer",\n "minimum": 1\n },\n "appSlug": {\n "description": "The slug of the app that created the run (`app.slug`). Recorded, not read by any check. Added in spec 0.1.4, amended 2026-10-12.",\n "type": "string",\n "minLength": 1\n },\n "workflowPath": {\n "description": "The `path` of the workflow run whose `check_suite_id` is the run\'s check suite, less any `@<ref>`, from the workflow runs at the head. Runs that have it are counted as the latest per workflow path and name. Added in spec 0.1.4, amended 2026-10-13.",\n "type": "string",\n "minLength": 1\n },\n "workflowName": {\n "description": "The `name` of that workflow run, as the listing gives it; absent where the listing gives none, null or the empty string. No 0.1 check reads it. Added in spec 0.1.5.",\n "type": "string",\n "minLength": 1\n },\n "workflowRunId": {\n "description": "The `id` of that workflow run. Added in spec 0.1.4, amended 2026-10-13.",\n "type": "integer",\n "minimum": 1\n },\n "runAttempt": {\n "description": "That workflow run\'s `run_attempt` as the listing gives it, its latest attempt at the read. Added in spec 0.1.4, amended 2026-10-13.",\n "type": "integer",\n "minimum": 1\n }\n }\n }\n }\n }\n },\n { "$ref": "#/$defs/unreadOnly" }\n ]\n },\n "testRecords": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["record", "status", "runs"],\n "additionalProperties": false,\n "properties": {\n "record": { "$ref": "handback-block-0.1.schema.json#/$defs/junitRecord" },\n "status": { "const": "ok" },\n "runs": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["runId", "runAttempt", "executed", "failed"],\n "additionalProperties": false,\n "properties": {\n "runId": { "type": "integer", "minimum": 1 },\n "runAttempt": { "type": "integer", "minimum": 1 },\n "executed": { "type": "integer", "minimum": 0 },\n "failed": { "type": "integer", "minimum": 0 }\n }\n }\n }\n }\n },\n {\n "type": "object",\n "required": ["record"],\n "properties": {\n "record": { "$ref": "handback-block-0.1.schema.json#/$defs/junitRecord" }\n },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n },\n "deployments": {\n "type": "array",\n "items": {\n "oneOf": [\n {\n "type": "object",\n "required": ["environment", "status", "deployments"],\n "additionalProperties": false,\n "properties": {\n "environment": { "type": "string", "minLength": 1 },\n "status": { "const": "ok" },\n "deployments": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["id", "sha", "relation", "successAt"],\n "additionalProperties": false,\n "properties": {\n "id": { "type": "integer", "minimum": 1 },\n "sha": { "$ref": "#/$defs/commitSha" },\n "relation": {\n "enum": ["head", "merge", "descendant", "unrelated", "unknown"]\n },\n "successAt": {\n "oneOf": [{ "$ref": "#/$defs/timestamp" }, { "type": "null" }]\n }\n }\n }\n }\n }\n },\n {\n "type": "object",\n "required": ["environment"],\n "properties": { "environment": { "type": "string", "minLength": 1 } },\n "allOf": [{ "$ref": "#/$defs/unread" }]\n }\n ]\n }\n },\n "sources": {\n "type": "array",\n "items": {\n "type": "object",\n "required": ["kind", "locator", "sha256", "readAt"],\n "additionalProperties": false,\n "properties": {\n "kind": { "$ref": "#/$defs/sourceKind" },\n "locator": { "type": "string", "minLength": 1, "maxLength": 2000 },\n "sha256": { "$ref": "#/$defs/sha256" },\n "etag": { "type": "string", "minLength": 1 },\n "readAt": { "$ref": "#/$defs/timestamp" },\n "error": {\n "description": "Present when the read failed: http_<status>, network, or parse.",\n "type": "string",\n "pattern": "^(?:http_[1-5][0-9]{2}|network|parse)$"\n }\n }\n }\n }\n }\n },\n "unreadOnly": {\n "allOf": [{ "$ref": "#/$defs/unread" }],\n "unevaluatedProperties": false\n }\n }\n}\n', "record-0.2-draft.schema.json": `{
9918
9929
  "$schema": "https://json-schema.org/draft/2020-12/schema",
9919
9930
  "$id": "https://barglabs.ai/dunstan/spec/schema/record-0.2-draft.schema.json",
9920
9931
  "title": "Dunstan record, DRAFT 0.2.0",
@@ -10400,7 +10411,7 @@ var FILES = { "handback-block-0.1.schema.json": `{
10400
10411
  },
10401
10412
  "readerReason": {
10402
10413
  "type": "string",
10403
- "pattern": "^(?:no_matching_record_item|declared_item_not_among_candidates|candidate_not_in_evidence|no_comparable_record_field|file_list_truncated|checks_incomplete|all_succeeded_mismatch|test_not_executed|test_outcome_mismatch|evidence_field_unpopulated:[A-Za-z0-9_.]+|source_unreadable:(?:pull_request_files|check_runs|pull_request_commits|timeline|artifact))$"
10414
+ "pattern": "^(?:no_matching_record_item|declared_item_not_among_candidates|candidate_not_in_evidence|ambiguous_candidates|workflow_not_in_record|workflow_name_ambiguous|no_comparable_record_field|file_list_truncated|checks_incomplete|all_succeeded_mismatch|test_not_executed|test_outcome_mismatch|not_closing|not_found|closing_link_unsettled|closing_base_not_default|closing_event_unsettled|evidence_field_unpopulated:[A-Za-z0-9_.]+|source_unreadable:(?:pull_request_files|check_runs|pull_request_commits|closing_references|repository|issue|timeline|artifact))$"
10404
10415
  }
10405
10416
  }
10406
10417
  }
@@ -11409,7 +11420,7 @@ function blockFor(kind, value, headSha) {
11409
11420
  case "file_changed":
11410
11421
  return typeof value === "string" ? { ...base, filesChanged: [value] } : void 0;
11411
11422
  case "reference_closes":
11412
- return typeof value === "string" ? { ...base, references: [{ issue: value, relation: "closes" }] } : void 0;
11423
+ return typeof value === "string" ? closesBlock(value, headSha) : void 0;
11413
11424
  case "commit":
11414
11425
  return typeof value === "string" && value.length === 40 ? { ...base, references: [{ commit: value, relation: "cites" }] } : void 0;
11415
11426
  case "head_commit":
@@ -12324,6 +12335,196 @@ function extractClaims(report) {
12324
12335
  return out;
12325
12336
  }
12326
12337
 
12338
+ // src/retrieval/identifiers.ts
12339
+ var MIN_NAME_LENGTH = 4;
12340
+ var SHA2 = /(?<![0-9A-Za-z])[0-9a-fA-F]{7,40}(?![0-9A-Za-z])/g;
12341
+ var ISSUE2 = /(?<![\w/.-])((?:[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100})?#[1-9][0-9]{0,9})(?![0-9])/g;
12342
+ var WORD = /[A-Za-z0-9_]/;
12343
+ var PATH_CHAR = /[A-Za-z0-9._/-]/;
12344
+ var JOB_NAME_CHAR = /[A-Za-z0-9_-]/;
12345
+ function stringLeaves(value, out) {
12346
+ if (typeof value === "string") out.push(value);
12347
+ else if (Array.isArray(value)) for (const v of value) stringLeaves(v, out);
12348
+ else if (value !== null && typeof value === "object")
12349
+ for (const v of Object.values(value)) stringLeaves(v, out);
12350
+ return out;
12351
+ }
12352
+ function claimTexts(claim2) {
12353
+ return [claim2.text, ...stringLeaves(claim2.declaredValue, [])];
12354
+ }
12355
+ function boundedAt(haystack, needle, inside) {
12356
+ const out = [];
12357
+ if (needle.length === 0) return out;
12358
+ for (let at = haystack.indexOf(needle); at !== -1; at = haystack.indexOf(needle, at + 1)) {
12359
+ const before = haystack[at - 1];
12360
+ const after = haystack[at + needle.length];
12361
+ const next = haystack[at + needle.length + 1];
12362
+ const openBefore = before === void 0 || !inside.test(before);
12363
+ const openAfter = after === void 0 || !inside.test(after) || after === "." && (next === void 0 || !/[A-Za-z0-9]/.test(next));
12364
+ if (openBefore && openAfter) out.push(at);
12365
+ }
12366
+ return out;
12367
+ }
12368
+ function occursBounded(haystack, needle, inside) {
12369
+ return boundedAt(haystack, needle, inside).length > 0;
12370
+ }
12371
+ function classnameSpans(text, classname) {
12372
+ const forms = /* @__PURE__ */ new Set([classname, classname.replaceAll(".", " ")]);
12373
+ return [...forms].flatMap(
12374
+ (form) => boundedAt(text, form, WORD).map((at) => [at, at + form.length])
12375
+ );
12376
+ }
12377
+ function classnameNamed(text, classname, classnames) {
12378
+ const longer = classnames.filter((c) => c.length > classname.length).flatMap((c) => classnameSpans(text, c));
12379
+ return classnameSpans(text, classname).some(
12380
+ ([from, to]) => !longer.some(([l, r]) => l <= from && to <= r)
12381
+ );
12382
+ }
12383
+ var CHECK_RUN_EVENTS = ["push", "pull_request", "pull_request_target", "merge_group"];
12384
+ var EVENT_SUFFIX = new RegExp(`^ \\((?:${CHECK_RUN_EVENTS.join("|")})\\)`);
12385
+ var WORKFLOW_JOB = /(\S+) \/ /g;
12386
+ function afterWorkflows(text) {
12387
+ return [...text.matchAll(WORKFLOW_JOB)].map((m) => m.index + m[0].length);
12388
+ }
12389
+ function jobNamed(text, name, longer) {
12390
+ if (text === name) return true;
12391
+ const workflowed = afterWorkflows(text);
12392
+ return boundedAt(text, name, JOB_NAME_CHAR).some(
12393
+ (at) => jobNamedAt(text, name, at, longer, workflowed.includes(at))
12394
+ );
12395
+ }
12396
+ function jobNamedAt(text, name, at, longer, withWorkflow) {
12397
+ const end = at + name.length;
12398
+ return (!text.startsWith(" (", end) || withWorkflow && EVENT_SUFFIX.test(text.slice(end))) && !longer.some(
12399
+ (l) => boundedAt(text, l, JOB_NAME_CHAR).some((from) => from <= at && end <= from + l.length)
12400
+ );
12401
+ }
12402
+ var longest = (names) => [...names].sort((a, b) => b.length - a.length)[0];
12403
+ function jobAt(text, at, jobNames) {
12404
+ return longest(
12405
+ jobNames.filter((name) => {
12406
+ const longer = jobNames.filter((n) => n.length > name.length && n.includes(name));
12407
+ return boundedAt(text, name, JOB_NAME_CHAR).includes(at) && jobNamedAt(text, name, at, longer, true);
12408
+ })
12409
+ );
12410
+ }
12411
+ function workflowBefore(text, slash, token, workflows) {
12412
+ const whole = workflows.filter((w) => {
12413
+ const from = slash - w.length;
12414
+ return w.length > 0 && from >= 0 && text.startsWith(w, from) && !PATH_CHAR.test(text[from - 1] ?? " ");
12415
+ });
12416
+ return longest(whole) ?? token;
12417
+ }
12418
+ function workflowJobs(text, jobNames, workflows = []) {
12419
+ const out = [];
12420
+ for (const m of text.matchAll(WORKFLOW_JOB)) {
12421
+ const at = m.index + m[0].length;
12422
+ const job = jobAt(text, at, jobNames);
12423
+ if (job === void 0) continue;
12424
+ out.push({ workflow: workflowBefore(text, at - 3, m[1], workflows), job });
12425
+ }
12426
+ return out;
12427
+ }
12428
+ var PARENTHESIS = /^ \([^()]*\)/;
12429
+ function unheldSuffixJobs(text, jobNames) {
12430
+ const out = [];
12431
+ for (const at of afterWorkflows(text)) {
12432
+ if (jobAt(text, at, jobNames) !== void 0) continue;
12433
+ const job = longest(
12434
+ jobNames.filter((name) => {
12435
+ const rest = text.slice(at + name.length);
12436
+ return boundedAt(text, name, JOB_NAME_CHAR).includes(at) && PARENTHESIS.test(rest) && !EVENT_SUFFIX.test(rest);
12437
+ })
12438
+ );
12439
+ if (job !== void 0) out.push(job);
12440
+ }
12441
+ return out;
12442
+ }
12443
+ function shaTokens(texts) {
12444
+ const out = [];
12445
+ for (const text of texts) {
12446
+ for (const m of text.matchAll(SHA2)) {
12447
+ const token = m[0].toLowerCase();
12448
+ if (token.length === 40 || /[0-9]/.test(token)) out.push(token);
12449
+ }
12450
+ }
12451
+ return out;
12452
+ }
12453
+ function issueRefs(texts, repository) {
12454
+ const out = [];
12455
+ for (const text of texts) {
12456
+ for (const m of text.matchAll(ISSUE2)) out.push(qualifyIssue(m[1], repository));
12457
+ }
12458
+ return out;
12459
+ }
12460
+ function identifierMatches(claim2, items, repository) {
12461
+ const texts = claimTexts(claim2);
12462
+ const shas = shaTokens(texts);
12463
+ const issues = issueRefs(texts, repository);
12464
+ const named = (value, inside) => texts.some((t) => t === value || occursBounded(t, value, inside));
12465
+ const qualified = (item) => item.type === "test" && item.id !== item.fields[0]?.[1] && named(item.id, WORD);
12466
+ const qualifiedNames = new Set(
12467
+ items.filter(qualified).map((item) => item.fields[0]?.[1])
12468
+ );
12469
+ const classnameOf = (item) => item.fields.find(([f]) => f === "classname")?.[1];
12470
+ const classnames = [
12471
+ ...new Set(items.filter((item) => item.type === "test").map(classnameOf))
12472
+ ].filter((c) => c !== void 0);
12473
+ const bareNamed = /* @__PURE__ */ new Map();
12474
+ for (const item of items) {
12475
+ const name = item.fields[0]?.[1];
12476
+ if (item.type !== "test" || name.length < MIN_NAME_LENGTH || qualifiedNames.has(name) || !named(name, WORD))
12477
+ continue;
12478
+ bareNamed.set(name, [...bareNamed.get(name) ?? [], item]);
12479
+ }
12480
+ const namedClassnames = /* @__PURE__ */ new Map();
12481
+ for (const [name, cases] of bareNamed) {
12482
+ if (cases.length < 2) continue;
12483
+ const said = cases.map(classnameOf).filter(
12484
+ (c) => c !== void 0 && c.length >= MIN_NAME_LENGTH && texts.some((t) => classnameNamed(t, c, classnames))
12485
+ );
12486
+ if (said.length > 0) namedClassnames.set(name, new Set(said));
12487
+ }
12488
+ const underNamedClassname = (item, name) => {
12489
+ const said = namedClassnames.get(name);
12490
+ const classname = classnameOf(item);
12491
+ return said === void 0 || classname !== void 0 && said.has(classname);
12492
+ };
12493
+ const jobNames = items.filter((item) => item.type === "check_run").map((item) => item.fields[0]?.[1]);
12494
+ const matches = /* @__PURE__ */ new Map();
12495
+ items.forEach((item, i) => {
12496
+ for (const [field, value] of item.fields) {
12497
+ let hit = false;
12498
+ switch (`${item.type}.${field}`) {
12499
+ case "commit.sha":
12500
+ hit = shas.some((s) => value.startsWith(s));
12501
+ break;
12502
+ case "timeline_event.ref":
12503
+ hit = /^[0-9a-f]{40}$/.test(value) ? shas.some((s) => value.startsWith(s)) : issues.some((ref) => sameIssue(ref, value));
12504
+ break;
12505
+ case "file.path":
12506
+ case "file.previousPath":
12507
+ hit = named(value, PATH_CHAR);
12508
+ break;
12509
+ case "check_run.name": {
12510
+ const longer = jobNames.filter((n) => n.length > value.length && n.includes(value));
12511
+ hit = value.length >= MIN_NAME_LENGTH && texts.some((t) => jobNamed(t, value, longer));
12512
+ break;
12513
+ }
12514
+ case "test.name":
12515
+ hit = value.length >= MIN_NAME_LENGTH && named(value, WORD) && (!qualifiedNames.has(value) || qualified(item)) && underNamedClassname(item, value);
12516
+ break;
12517
+ }
12518
+ if (hit) {
12519
+ matches.set(i, field);
12520
+ return;
12521
+ }
12522
+ }
12523
+ if (qualified(item)) matches.set(i, "id");
12524
+ });
12525
+ return matches;
12526
+ }
12527
+
12327
12528
  // src/retrieval/items.ts
12328
12529
  function testId(test) {
12329
12530
  return test.classname === void 0 ? test.name : `${test.classname}.${test.name}`;
@@ -12465,116 +12666,12 @@ var Bm25Index = class {
12465
12666
  }
12466
12667
  };
12467
12668
 
12468
- // src/retrieval/identifiers.ts
12469
- var MIN_NAME_LENGTH = 4;
12470
- var SHA2 = /(?<![0-9A-Za-z])[0-9a-fA-F]{7,40}(?![0-9A-Za-z])/g;
12471
- var ISSUE2 = /(?<![\w/.-])((?:[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100})?#[1-9][0-9]{0,9})(?![0-9])/g;
12472
- var WORD = /[A-Za-z0-9_]/;
12473
- var PATH_CHAR = /[A-Za-z0-9._/-]/;
12474
- var JOB_NAME_CHAR = /[A-Za-z0-9_-]/;
12475
- function stringLeaves(value, out) {
12476
- if (typeof value === "string") out.push(value);
12477
- else if (Array.isArray(value)) for (const v of value) stringLeaves(v, out);
12478
- else if (value !== null && typeof value === "object")
12479
- for (const v of Object.values(value)) stringLeaves(v, out);
12480
- return out;
12481
- }
12482
- function claimTexts(claim2) {
12483
- return [claim2.text, ...stringLeaves(claim2.declaredValue, [])];
12484
- }
12485
- function boundedAt(haystack, needle, inside) {
12486
- const out = [];
12487
- if (needle.length === 0) return out;
12488
- for (let at = haystack.indexOf(needle); at !== -1; at = haystack.indexOf(needle, at + 1)) {
12489
- const before = haystack[at - 1];
12490
- const after = haystack[at + needle.length];
12491
- const next = haystack[at + needle.length + 1];
12492
- const openBefore = before === void 0 || !inside.test(before);
12493
- const openAfter = after === void 0 || !inside.test(after) || after === "." && (next === void 0 || !/[A-Za-z0-9]/.test(next));
12494
- if (openBefore && openAfter) out.push(at);
12495
- }
12496
- return out;
12497
- }
12498
- function occursBounded(haystack, needle, inside) {
12499
- return boundedAt(haystack, needle, inside).length > 0;
12500
- }
12501
- function jobNamed(text, name, longer) {
12502
- if (text === name) return true;
12503
- return boundedAt(text, name, JOB_NAME_CHAR).some(
12504
- (at) => !text.startsWith(" (", at + name.length) && !longer.some(
12505
- (l) => boundedAt(text, l, JOB_NAME_CHAR).some(
12506
- (from) => from <= at && at + name.length <= from + l.length
12507
- )
12508
- )
12509
- );
12510
- }
12511
- function shaTokens(texts) {
12512
- const out = [];
12513
- for (const text of texts) {
12514
- for (const m of text.matchAll(SHA2)) {
12515
- const token = m[0].toLowerCase();
12516
- if (token.length === 40 || /[0-9]/.test(token)) out.push(token);
12517
- }
12518
- }
12519
- return out;
12520
- }
12521
- function issueRefs(texts, repository) {
12522
- const out = [];
12523
- for (const text of texts) {
12524
- for (const m of text.matchAll(ISSUE2)) out.push(qualifyIssue(m[1], repository));
12525
- }
12526
- return out;
12527
- }
12528
- function identifierMatches(claim2, items, repository) {
12529
- const texts = claimTexts(claim2);
12530
- const shas = shaTokens(texts);
12531
- const issues = issueRefs(texts, repository);
12532
- const named = (value, inside) => texts.some((t) => t === value || occursBounded(t, value, inside));
12533
- const qualified = (item) => item.type === "test" && item.id !== item.fields[0]?.[1] && named(item.id, WORD);
12534
- const qualifiedNames = new Set(
12535
- items.filter(qualified).map((item) => item.fields[0]?.[1])
12536
- );
12537
- const jobNames = items.filter((item) => item.type === "check_run").map((item) => item.fields[0]?.[1]);
12538
- const matches = /* @__PURE__ */ new Map();
12539
- items.forEach((item, i) => {
12540
- for (const [field, value] of item.fields) {
12541
- let hit = false;
12542
- switch (`${item.type}.${field}`) {
12543
- case "commit.sha":
12544
- hit = shas.some((s) => value.startsWith(s));
12545
- break;
12546
- case "timeline_event.ref":
12547
- hit = /^[0-9a-f]{40}$/.test(value) ? shas.some((s) => value.startsWith(s)) : issues.some((ref) => sameIssue(ref, value));
12548
- break;
12549
- case "file.path":
12550
- case "file.previousPath":
12551
- hit = named(value, PATH_CHAR);
12552
- break;
12553
- case "check_run.name": {
12554
- const longer = jobNames.filter((n) => n.length > value.length && n.includes(value));
12555
- hit = value.length >= MIN_NAME_LENGTH && texts.some((t) => jobNamed(t, value, longer));
12556
- break;
12557
- }
12558
- case "test.name":
12559
- hit = value.length >= MIN_NAME_LENGTH && named(value, WORD) && (!qualifiedNames.has(value) || qualified(item));
12560
- break;
12561
- }
12562
- if (hit) {
12563
- matches.set(i, field);
12564
- return;
12565
- }
12566
- }
12567
- if (qualified(item)) matches.set(i, "id");
12568
- });
12569
- return matches;
12570
- }
12571
-
12572
12669
  // src/retrieval/retrieve.ts
12573
12670
  var FILL_TO_K = "to-k";
12574
12671
  var ARM_A_FILL = "A-fill";
12575
12672
  var BM25_FLOOR = 1;
12576
12673
  var SCORE_DECIMALS = 6;
12577
- function round(score) {
12674
+ function roundScore(score) {
12578
12675
  const scale = 10 ** SCORE_DECIMALS;
12579
12676
  return Math.round(score * scale) / scale;
12580
12677
  }
@@ -12622,7 +12719,7 @@ function retrieve(claim2, items, opts) {
12622
12719
  });
12623
12720
  return;
12624
12721
  }
12625
- const score = round(scores[order] ?? 0);
12722
+ const score = roundScore(scores[order] ?? 0);
12626
12723
  if (!fill && score < BM25_FLOOR) return;
12627
12724
  ranked.push({
12628
12725
  order,
@@ -12645,6 +12742,7 @@ var READER_CLAIM_KINDS = /* @__PURE__ */ new Map([
12645
12742
  ["file_changed", "file"],
12646
12743
  ["commit_present", "commit"],
12647
12744
  ["reference_in_timeline", "timeline_event"],
12745
+ ["reference_closes", "timeline_event"],
12648
12746
  ["check_succeeded", "check_run"],
12649
12747
  ["test_passed", "test"]
12650
12748
  ]);
@@ -12698,6 +12796,66 @@ function combinedCandidates(typed, floor) {
12698
12796
  const exact = typed.filter((c) => c.score === null);
12699
12797
  return exact.length > 0 ? exact : typed.filter((c) => c.score !== null && c.score >= floor);
12700
12798
  }
12799
+ function outcomeOf(type, entry) {
12800
+ if (type === "test") return entry.outcome;
12801
+ const run = entry;
12802
+ if (run.status !== "completed") return "not_completed";
12803
+ return run.conclusion === "success" ? "succeeded" : "failed";
12804
+ }
12805
+ function ambiguousCandidates(combined, outcome) {
12806
+ const scores = combined.map((c) => c.score === null ? null : roundScore(c.score));
12807
+ if (scores.length < 2 || scores.includes(null)) return false;
12808
+ const top = Math.max(...scores);
12809
+ const tied = combined.filter((_, i) => scores[i] === top);
12810
+ return tied.length >= 2 && new Set(tied.map(outcome)).size > 1;
12811
+ }
12812
+ function namedWorkflowRuns(text, combined, evidence) {
12813
+ const section = evidence.checkRuns;
12814
+ if (section?.status !== "ok") return [...combined];
12815
+ const jobNames = [...new Set(section.runs.map((r) => r.name))];
12816
+ if (unheldSuffixJobs(text, jobNames).length > 0) return { reason: "no_matching_record_item" };
12817
+ const recordedWorkflows = [
12818
+ ...new Set(
12819
+ section.runs.flatMap((r) => [r.workflowPath, r.workflowName]).filter((w) => w !== void 0)
12820
+ )
12821
+ ];
12822
+ const named = workflowJobs(text, jobNames, recordedWorkflows);
12823
+ if (named.length === 0) return [...combined];
12824
+ const counted = new Map(
12825
+ countedCheckRuns(section.runs, section.excludedIds).map((r) => [String(r.id), r])
12826
+ );
12827
+ const runOf = (c) => counted.get(c.id);
12828
+ const isOf = (r, workflow) => r.workflowPath === workflow || r.workflowName === workflow;
12829
+ let kept = [...combined];
12830
+ for (const job of new Set(named.map((n) => n.job))) {
12831
+ const runs = section.runs.filter((r) => r.name === job);
12832
+ if (runs.some((r) => r.workflowPath === void 0)) {
12833
+ if (runs.length >= 2) return { reason: "workflow_not_in_record" };
12834
+ continue;
12835
+ }
12836
+ const workflows = new Set(named.filter((n) => n.job === job).map((n) => n.workflow));
12837
+ for (const workflow of workflows) {
12838
+ const byPath = runs.some((r) => r.workflowPath === workflow);
12839
+ if (!byPath && runs.some((r) => r.workflowName === void 0)) {
12840
+ return { reason: "workflow_not_in_record" };
12841
+ }
12842
+ const paths = new Set(runs.filter((r) => isOf(r, workflow)).map((r) => r.workflowPath));
12843
+ if (paths.size >= 2) return { reason: "workflow_name_ambiguous" };
12844
+ }
12845
+ kept = kept.filter((c) => {
12846
+ const run = runOf(c);
12847
+ return run?.name !== job || [...workflows].some((w) => isOf(run, w));
12848
+ });
12849
+ for (const workflow of workflows) {
12850
+ const of = (r) => r?.name === job && isOf(r, workflow);
12851
+ if (kept.some((c) => of(runOf(c)))) continue;
12852
+ return {
12853
+ reason: [...counted.values()].some(of) ? "declared_item_not_among_candidates" : "workflow_not_in_record"
12854
+ };
12855
+ }
12856
+ }
12857
+ return kept;
12858
+ }
12701
12859
  function checkReaderClaim(claim2, evidence, repository) {
12702
12860
  const type = READER_CLAIM_KINDS.get(claim2.kind);
12703
12861
  if (type === void 0) return unverifiable2("no_comparable_record_field");
@@ -12706,6 +12864,13 @@ function checkReaderClaim(claim2, evidence, repository) {
12706
12864
  if (wantsBoolean ? typeof declared !== "boolean" : typeof declared !== "string") {
12707
12865
  return unverifiable2("no_comparable_record_field");
12708
12866
  }
12867
+ if (claim2.kind === "reference_closes") {
12868
+ if (!ISSUE_REF2.test(declared)) return unverifiable2("no_comparable_record_field");
12869
+ const head = evidence.pullRequest.headSha;
12870
+ const row = checkReferences(closesBlock(declared, head), evidence, repository)[0];
12871
+ if (row === void 0) return unverifiable2("no_comparable_record_field");
12872
+ return verdictOf(row.verdict, row.observed ?? null, row.reason);
12873
+ }
12709
12874
  const missing = sectionReason(type, evidence);
12710
12875
  if (missing !== void 0) return unverifiable2(missing);
12711
12876
  const entries = lookup(type, evidence);
@@ -12713,12 +12878,20 @@ function checkReaderClaim(claim2, evidence, repository) {
12713
12878
  if (typed.some((c) => entries.get(c.id) === void 0)) {
12714
12879
  return unverifiable2("candidate_not_in_evidence");
12715
12880
  }
12716
- const relevant = combinedCandidates(typed, claim2.retrieval.floor);
12717
- const observed = relevant.map((c) => entries.get(c.id));
12881
+ let relevant = combinedCandidates(typed, claim2.retrieval.floor);
12718
12882
  const truncated = type === "file" && evidence.files?.status === "ok" && !evidence.files.complete;
12719
12883
  if (relevant.length === 0) {
12720
12884
  return unverifiable2(truncated ? "file_list_truncated" : "no_matching_record_item");
12721
12885
  }
12886
+ if (type === "check_run") {
12887
+ const scoped = namedWorkflowRuns(claim2.text, relevant, evidence);
12888
+ if (!Array.isArray(scoped)) return unverifiable2(scoped.reason);
12889
+ relevant = scoped;
12890
+ }
12891
+ const observed = relevant.map((c) => entries.get(c.id));
12892
+ if (wantsBoolean && ambiguousCandidates(relevant, (c) => outcomeOf(type, entries.get(c.id)))) {
12893
+ return unverifiable2("ambiguous_candidates");
12894
+ }
12722
12895
  switch (type) {
12723
12896
  case "file": {
12724
12897
  const files = observed;
@@ -12772,7 +12945,13 @@ function readReaderClaim(input2, evidence, repository) {
12772
12945
  retrieval,
12773
12946
  candidates,
12774
12947
  ...checkReaderClaim(
12775
- { kind: input2.kind, declaredValue: input2.declaredValue, candidates, retrieval },
12948
+ {
12949
+ text: input2.text,
12950
+ kind: input2.kind,
12951
+ declaredValue: input2.declaredValue,
12952
+ candidates,
12953
+ retrieval
12954
+ },
12776
12955
  evidence,
12777
12956
  repository
12778
12957
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dunstan",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "mcpName": "io.github.BargLabs/dunstan",
5
5
  "description": "Holds a coding agent's completion report to the record: deterministic checks of a declared handback block against the repository's own metadata.",
6
6
  "license": "AGPL-3.0-only",