dunstan 0.1.6 → 0.1.8
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.
- package/README.md +5 -4
- package/dist/dunstan.mjs +404 -628
- package/package.json +1 -1
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.
|
|
8033
|
+
var SPEC_VERSION = "0.1.4";
|
|
8034
8034
|
var BLOCK_VERSION = "0.1";
|
|
8035
8035
|
var SUPPORTED_BLOCK_VERSIONS = [BLOCK_VERSION];
|
|
8036
8036
|
var BLOCK_INFO_STRING = "dunstan-handback";
|
|
@@ -8421,6 +8421,61 @@ function findReference(evidence, kind, ref) {
|
|
|
8421
8421
|
(r) => r.kind === kind && (kind === "issue" ? sameIssue(r.ref, ref) : r.ref === ref)
|
|
8422
8422
|
);
|
|
8423
8423
|
}
|
|
8424
|
+
function closedBy(reference, field, evidence, repository, at, observed) {
|
|
8425
|
+
const pr = evidence.pullRequest;
|
|
8426
|
+
const section = evidence.closeEvents;
|
|
8427
|
+
const target = section?.targets.find((t) => sameIssue(t.ref, at));
|
|
8428
|
+
const side = section?.pullRequest;
|
|
8429
|
+
let unread2;
|
|
8430
|
+
if (section === void 0 || target === void 0) unread2 = absentSection("closeEvents");
|
|
8431
|
+
else if (target.status !== "ok") unread2 = unreadReason(target);
|
|
8432
|
+
const events = target?.status === "ok" ? target.events : [];
|
|
8433
|
+
const sideUnread = () => side === void 0 ? absentSection("closeEvents") : unreadReason(side);
|
|
8434
|
+
const self = `${repository}#${pr.number}`;
|
|
8435
|
+
const byThis = events.find(
|
|
8436
|
+
(e) => e.closer?.kind === "pull_request" && sameIssue(e.closer.ref, self)
|
|
8437
|
+
);
|
|
8438
|
+
if (byThis !== void 0) {
|
|
8439
|
+
return pass("reference", field, reference, { ...observed, closedBy: byThis });
|
|
8440
|
+
}
|
|
8441
|
+
for (const e of events) {
|
|
8442
|
+
if (e.closer?.kind !== "commit") continue;
|
|
8443
|
+
const oid = e.closer.oid;
|
|
8444
|
+
if (oid === pr.mergeSha || side?.status === "ok" && side.commits.includes(oid)) {
|
|
8445
|
+
return pass("reference", field, reference, { ...observed, closedBy: e });
|
|
8446
|
+
}
|
|
8447
|
+
if (side?.status !== "ok") unread2 ??= sideUnread();
|
|
8448
|
+
}
|
|
8449
|
+
if (pr.state === "open" && !pr.merged) {
|
|
8450
|
+
return unverifiable("reference", field, reference, "closing_link_unsettled");
|
|
8451
|
+
}
|
|
8452
|
+
if (pr.merged && target?.status === "ok" && target.state === "open") {
|
|
8453
|
+
if (side?.status !== "ok") unread2 ??= sideUnread();
|
|
8454
|
+
else if (side.baseRef !== side.defaultBranch) {
|
|
8455
|
+
return unverifiable("reference", field, reference, "closing_base_not_default");
|
|
8456
|
+
}
|
|
8457
|
+
}
|
|
8458
|
+
if (unread2 !== void 0) return unverifiable("reference", field, reference, unread2);
|
|
8459
|
+
const settled = target;
|
|
8460
|
+
if (settled.state === "closed" && settled.events.length === 0) {
|
|
8461
|
+
return unverifiable("reference", field, reference, "closing_event_unsettled");
|
|
8462
|
+
}
|
|
8463
|
+
return fail(
|
|
8464
|
+
"reference",
|
|
8465
|
+
field,
|
|
8466
|
+
reference,
|
|
8467
|
+
{ ...observed, state: settled.state, events: settled.events },
|
|
8468
|
+
"not_closing"
|
|
8469
|
+
);
|
|
8470
|
+
}
|
|
8471
|
+
function closesBlock(issue2, headCommit) {
|
|
8472
|
+
return {
|
|
8473
|
+
dunstan: BLOCK_VERSION,
|
|
8474
|
+
headCommit,
|
|
8475
|
+
filesChanged: [],
|
|
8476
|
+
references: [{ issue: issue2, relation: "closes" }]
|
|
8477
|
+
};
|
|
8478
|
+
}
|
|
8424
8479
|
function checkReferences(block, evidence, repository) {
|
|
8425
8480
|
return (block.references ?? []).map((reference, i) => {
|
|
8426
8481
|
const field = `/references/${i}`;
|
|
@@ -8448,15 +8503,11 @@ function checkReferences(block, evidence, repository) {
|
|
|
8448
8503
|
return fail("reference", field, reference, { ...observed2, exists: false }, "not_found");
|
|
8449
8504
|
}
|
|
8450
8505
|
const resolved = entry2.kind === "issue" ? entry2.resolvedAs : void 0;
|
|
8506
|
+
const named = resolved === void 0 ? observed2 : { ...observed2, ref: target, resolvedAs: resolved };
|
|
8451
8507
|
if (resolved !== void 0 && closing.issues.some((issue2) => sameIssue(issue2, resolved))) {
|
|
8452
|
-
return pass("reference", field, reference,
|
|
8453
|
-
...observed2,
|
|
8454
|
-
ref: target,
|
|
8455
|
-
resolvedAs: resolved
|
|
8456
|
-
});
|
|
8508
|
+
return pass("reference", field, reference, named);
|
|
8457
8509
|
}
|
|
8458
|
-
|
|
8459
|
-
return pr.state === "open" && !pr.merged ? unverifiable("reference", field, reference, "closing_link_unsettled") : fail("reference", field, reference, observed2, "not_closing");
|
|
8510
|
+
return closedBy(reference, field, evidence, repository, resolved ?? target, named);
|
|
8460
8511
|
}
|
|
8461
8512
|
if ("issue" in reference) {
|
|
8462
8513
|
const entry2 = findReference(evidence, "issue", qualifyIssue(reference.issue, repository));
|
|
@@ -8525,14 +8576,30 @@ function checkTests(block, evidence) {
|
|
|
8525
8576
|
});
|
|
8526
8577
|
return rows;
|
|
8527
8578
|
}
|
|
8579
|
+
function checkKey(run) {
|
|
8580
|
+
if (run.workflowPath !== void 0)
|
|
8581
|
+
return JSON.stringify(["workflow", run.workflowPath, run.name]);
|
|
8582
|
+
if (run.checkSuiteId !== void 0) return JSON.stringify(["suite", run.checkSuiteId, run.name]);
|
|
8583
|
+
return JSON.stringify(["name", run.name]);
|
|
8584
|
+
}
|
|
8585
|
+
function later(a, b) {
|
|
8586
|
+
if ((a.workflowRunId ?? 0) !== (b.workflowRunId ?? 0)) {
|
|
8587
|
+
return (a.workflowRunId ?? 0) > (b.workflowRunId ?? 0);
|
|
8588
|
+
}
|
|
8589
|
+
if ((a.runAttempt ?? 0) !== (b.runAttempt ?? 0)) return (a.runAttempt ?? 0) > (b.runAttempt ?? 0);
|
|
8590
|
+
return a.id > b.id;
|
|
8591
|
+
}
|
|
8528
8592
|
function countedCheckRuns(runs, excludedIds) {
|
|
8593
|
+
const excluded = new Set(excludedIds);
|
|
8594
|
+
const checker = /* @__PURE__ */ new Map();
|
|
8529
8595
|
const latest = /* @__PURE__ */ new Map();
|
|
8530
8596
|
for (const run of runs) {
|
|
8531
|
-
|
|
8532
|
-
|
|
8597
|
+
if (excluded.has(run.id)) checker.set(run.name, Math.max(checker.get(run.name) ?? 0, run.id));
|
|
8598
|
+
const key = checkKey(run);
|
|
8599
|
+
const seen = latest.get(key);
|
|
8600
|
+
if (seen === void 0 || later(run, seen)) latest.set(key, run);
|
|
8533
8601
|
}
|
|
8534
|
-
|
|
8535
|
-
return [...latest.values()].filter((run) => !excluded.has(run.id)).sort((a, b) => a.id - b.id);
|
|
8602
|
+
return [...latest.values()].filter((run) => !excluded.has(run.id) && run.id > (checker.get(run.name) ?? 0)).sort((a, b) => a.id - b.id);
|
|
8536
8603
|
}
|
|
8537
8604
|
function checkChecks(block, evidence) {
|
|
8538
8605
|
const checks = block.checks;
|
|
@@ -9023,6 +9090,204 @@ async function readClosingReferences(client, repository, number4) {
|
|
|
9023
9090
|
}
|
|
9024
9091
|
return unreadable("closing_references");
|
|
9025
9092
|
}
|
|
9093
|
+
var CLOSE_BASE_QUERY = `query($owner: String!, $name: String!, $number: Int!, $after: String) {
|
|
9094
|
+
repository(owner: $owner, name: $name) {
|
|
9095
|
+
defaultBranchRef { name }
|
|
9096
|
+
pullRequest(number: $number) {
|
|
9097
|
+
baseRefName
|
|
9098
|
+
commits(first: 100, after: $after) {
|
|
9099
|
+
totalCount
|
|
9100
|
+
nodes { commit { oid } }
|
|
9101
|
+
pageInfo { hasNextPage endCursor }
|
|
9102
|
+
}
|
|
9103
|
+
}
|
|
9104
|
+
}
|
|
9105
|
+
}`;
|
|
9106
|
+
var CLOSE_EVENTS_QUERY = `query($owner: String!, $name: String!, $number: Int!, $after: String) {
|
|
9107
|
+
repository(owner: $owner, name: $name) {
|
|
9108
|
+
issueOrPullRequest(number: $number) {
|
|
9109
|
+
__typename
|
|
9110
|
+
... on Issue {
|
|
9111
|
+
state
|
|
9112
|
+
timelineItems(itemTypes: [CLOSED_EVENT], first: 100, after: $after) {
|
|
9113
|
+
nodes {
|
|
9114
|
+
... on ClosedEvent {
|
|
9115
|
+
createdAt
|
|
9116
|
+
closer {
|
|
9117
|
+
__typename
|
|
9118
|
+
... on PullRequest { number repository { nameWithOwner } }
|
|
9119
|
+
... on Commit { oid }
|
|
9120
|
+
}
|
|
9121
|
+
}
|
|
9122
|
+
}
|
|
9123
|
+
pageInfo { hasNextPage endCursor }
|
|
9124
|
+
}
|
|
9125
|
+
}
|
|
9126
|
+
... on PullRequest {
|
|
9127
|
+
state
|
|
9128
|
+
timelineItems(itemTypes: [CLOSED_EVENT], first: 100, after: $after) {
|
|
9129
|
+
nodes {
|
|
9130
|
+
... on ClosedEvent {
|
|
9131
|
+
createdAt
|
|
9132
|
+
closer {
|
|
9133
|
+
__typename
|
|
9134
|
+
... on PullRequest { number repository { nameWithOwner } }
|
|
9135
|
+
... on Commit { oid }
|
|
9136
|
+
}
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
pageInfo { hasNextPage endCursor }
|
|
9140
|
+
}
|
|
9141
|
+
}
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
}`;
|
|
9145
|
+
var CLOSE_EVENT_PAGES = 10;
|
|
9146
|
+
var COMMIT_OID = /^[0-9a-f]{40}$/;
|
|
9147
|
+
function commitOid(value) {
|
|
9148
|
+
const oid = str(value);
|
|
9149
|
+
if (!COMMIT_OID.test(oid)) throw new ShapeError();
|
|
9150
|
+
return oid;
|
|
9151
|
+
}
|
|
9152
|
+
async function readGraphqlPages(client, kind, locator, query, variables, budget, page) {
|
|
9153
|
+
const items = [];
|
|
9154
|
+
let head;
|
|
9155
|
+
let after = null;
|
|
9156
|
+
for (let n = 1; n <= budget; n++) {
|
|
9157
|
+
const at = `${locator}${after === null ? "" : `(after:${after})`}`;
|
|
9158
|
+
const read = await client.read(kind, "/graphql", {
|
|
9159
|
+
method: "POST",
|
|
9160
|
+
locator: at,
|
|
9161
|
+
requestBody: JSON.stringify({ query, variables: { ...variables, after } })
|
|
9162
|
+
});
|
|
9163
|
+
if (!read.ok) return unreadable(kind);
|
|
9164
|
+
try {
|
|
9165
|
+
const body = obj(read.json);
|
|
9166
|
+
if (body.errors !== void 0 && body.errors !== null) throw new ShapeError();
|
|
9167
|
+
const answer = page(obj(body.data));
|
|
9168
|
+
if ("unpopulated" in answer) return { status: "unpopulated", field: answer.unpopulated };
|
|
9169
|
+
head ??= { value: answer.head };
|
|
9170
|
+
items.push(...answer.items);
|
|
9171
|
+
const info = obj(answer.pageInfo);
|
|
9172
|
+
if (info.hasNextPage !== true) return { status: "ok", head: head.value, items };
|
|
9173
|
+
after = str(info.endCursor);
|
|
9174
|
+
} catch (e) {
|
|
9175
|
+
if (!(e instanceof ShapeError)) throw e;
|
|
9176
|
+
client.markParseError(at);
|
|
9177
|
+
return unreadable(kind);
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
9180
|
+
return unreadable(kind);
|
|
9181
|
+
}
|
|
9182
|
+
async function readCloseBase(client, repository, number4) {
|
|
9183
|
+
const { owner, name } = splitRepo(repository);
|
|
9184
|
+
const read = await readGraphqlPages(
|
|
9185
|
+
client,
|
|
9186
|
+
"pull_request_commits",
|
|
9187
|
+
`POST /graphql repository(${repository}).pullRequest(${number4}).commits`,
|
|
9188
|
+
CLOSE_BASE_QUERY,
|
|
9189
|
+
{ owner, name, number: number4 },
|
|
9190
|
+
MAX_PAGES,
|
|
9191
|
+
(data) => {
|
|
9192
|
+
const repo = obj(data.repository);
|
|
9193
|
+
const pr = obj(repo.pullRequest);
|
|
9194
|
+
const commits2 = obj(pr.commits);
|
|
9195
|
+
const branch = repo.defaultBranchRef;
|
|
9196
|
+
return {
|
|
9197
|
+
head: {
|
|
9198
|
+
baseRef: str(pr.baseRefName),
|
|
9199
|
+
defaultBranch: branch === null ? null : str(obj(branch).name),
|
|
9200
|
+
total: int(commits2.totalCount)
|
|
9201
|
+
},
|
|
9202
|
+
items: arr(commits2.nodes).map((node2) => commitOid(obj(obj(node2).commit).oid)),
|
|
9203
|
+
pageInfo: commits2.pageInfo
|
|
9204
|
+
};
|
|
9205
|
+
}
|
|
9206
|
+
);
|
|
9207
|
+
if (read.status !== "ok") return read;
|
|
9208
|
+
const commits = [...new Set(read.items)].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
9209
|
+
if (commits.length < read.head.total) return unreadable("pull_request_commits");
|
|
9210
|
+
return {
|
|
9211
|
+
status: "ok",
|
|
9212
|
+
baseRef: read.head.baseRef,
|
|
9213
|
+
defaultBranch: read.head.defaultBranch,
|
|
9214
|
+
commits
|
|
9215
|
+
};
|
|
9216
|
+
}
|
|
9217
|
+
var TARGET_TYPES = {
|
|
9218
|
+
Issue: "issue",
|
|
9219
|
+
PullRequest: "pull_request"
|
|
9220
|
+
};
|
|
9221
|
+
var TARGET_STATES = {
|
|
9222
|
+
OPEN: "open",
|
|
9223
|
+
CLOSED: "closed",
|
|
9224
|
+
MERGED: "merged"
|
|
9225
|
+
};
|
|
9226
|
+
function closeEvent(node2) {
|
|
9227
|
+
const n = obj(node2);
|
|
9228
|
+
const at = timestamp(n.createdAt);
|
|
9229
|
+
if (n.closer === null) return { at, closer: null };
|
|
9230
|
+
const closer = obj(n.closer);
|
|
9231
|
+
const type = str(closer.__typename);
|
|
9232
|
+
if (type === "PullRequest") {
|
|
9233
|
+
const ref = `${str(obj(closer.repository).nameWithOwner)}#${int(closer.number)}`;
|
|
9234
|
+
if (!ISSUE_REF.test(ref)) throw new ShapeError();
|
|
9235
|
+
return { at, closer: { kind: "pull_request", ref } };
|
|
9236
|
+
}
|
|
9237
|
+
if (type === "Commit") return { at, closer: { kind: "commit", oid: commitOid(closer.oid) } };
|
|
9238
|
+
return { at, closer: { kind: "other" } };
|
|
9239
|
+
}
|
|
9240
|
+
async function readCloseTarget(client, ref) {
|
|
9241
|
+
const [repo, num] = ref.split("#");
|
|
9242
|
+
const { owner, name } = splitRepo(repo);
|
|
9243
|
+
const read = await readGraphqlPages(
|
|
9244
|
+
client,
|
|
9245
|
+
"issue",
|
|
9246
|
+
`POST /graphql repository(${repo}).issueOrPullRequest(${num}).timelineItems(CLOSED_EVENT)`,
|
|
9247
|
+
CLOSE_EVENTS_QUERY,
|
|
9248
|
+
{ owner, name, number: Number(num) },
|
|
9249
|
+
CLOSE_EVENT_PAGES,
|
|
9250
|
+
(data) => {
|
|
9251
|
+
const item = obj(data.repository).issueOrPullRequest;
|
|
9252
|
+
if (item === null || item === void 0) return { unpopulated: "issueOrPullRequest" };
|
|
9253
|
+
const target = obj(item);
|
|
9254
|
+
const type = TARGET_TYPES[str(target.__typename)];
|
|
9255
|
+
const state = TARGET_STATES[str(target.state)];
|
|
9256
|
+
if (type === void 0 || state === void 0) throw new ShapeError();
|
|
9257
|
+
if (type === "issue" && state === "merged") throw new ShapeError();
|
|
9258
|
+
const timeline = obj(target.timelineItems);
|
|
9259
|
+
return {
|
|
9260
|
+
head: { type, state },
|
|
9261
|
+
items: arr(timeline.nodes).map(closeEvent),
|
|
9262
|
+
pageInfo: timeline.pageInfo
|
|
9263
|
+
};
|
|
9264
|
+
}
|
|
9265
|
+
);
|
|
9266
|
+
if (read.status !== "ok") return { ref, ...read };
|
|
9267
|
+
return { ref, status: "ok", ...read.head, events: read.items };
|
|
9268
|
+
}
|
|
9269
|
+
function closeTargets(block, repository, closing, references) {
|
|
9270
|
+
if (closing?.status !== "ok") return [];
|
|
9271
|
+
const listed2 = (ref) => closing.issues.some((issue2) => sameIssue(issue2, ref));
|
|
9272
|
+
const out = [];
|
|
9273
|
+
for (const reference of block.references ?? []) {
|
|
9274
|
+
if (reference.relation !== "closes" || !("issue" in reference)) continue;
|
|
9275
|
+
const target = qualifyIssue(reference.issue, repository);
|
|
9276
|
+
if (listed2(target)) continue;
|
|
9277
|
+
const entry = references?.find((r) => r.kind === "issue" && sameIssue(r.ref, target));
|
|
9278
|
+
if (entry?.kind !== "issue" || entry.status !== "ok" || !entry.exists) continue;
|
|
9279
|
+
const at = entry.resolvedAs ?? target;
|
|
9280
|
+
if (listed2(at) || out.some((o) => sameIssue(o, at))) continue;
|
|
9281
|
+
out.push(at);
|
|
9282
|
+
}
|
|
9283
|
+
return out;
|
|
9284
|
+
}
|
|
9285
|
+
async function readCloseEvents(client, repository, number4, targets) {
|
|
9286
|
+
const pullRequest = await readCloseBase(client, repository, number4);
|
|
9287
|
+
const read = [];
|
|
9288
|
+
for (const target of targets) read.push(await readCloseTarget(client, target));
|
|
9289
|
+
return { pullRequest, targets: read };
|
|
9290
|
+
}
|
|
9026
9291
|
var PATH_QUERY = `query($owner: String!, $name: String!, $expression: String!) {
|
|
9027
9292
|
repository(owner: $owner, name: $name) {
|
|
9028
9293
|
object(expression: $expression) { __typename }
|
|
@@ -9179,15 +9444,34 @@ async function readCheckRuns(client, repository, commit, excludedIds) {
|
|
|
9179
9444
|
(json2) => arr(obj(json2).check_runs),
|
|
9180
9445
|
(item) => {
|
|
9181
9446
|
const r = obj(item);
|
|
9182
|
-
|
|
9447
|
+
const run = {
|
|
9183
9448
|
id: int(r.id),
|
|
9184
9449
|
name: str(r.name),
|
|
9185
9450
|
status: str(r.status),
|
|
9186
9451
|
conclusion: strOrNull(r.conclusion)
|
|
9187
9452
|
};
|
|
9453
|
+
if (r.check_suite !== null && r.check_suite !== void 0) {
|
|
9454
|
+
const suite = int(obj(r.check_suite).id);
|
|
9455
|
+
if (suite < 1) throw new ShapeError();
|
|
9456
|
+
run.checkSuiteId = suite;
|
|
9457
|
+
}
|
|
9458
|
+
const slug = r.app?.slug;
|
|
9459
|
+
if (typeof slug === "string" && slug !== "") run.appSlug = slug;
|
|
9460
|
+
return run;
|
|
9188
9461
|
}
|
|
9189
9462
|
);
|
|
9190
9463
|
if (runs === null) return unreadable("check_runs");
|
|
9464
|
+
if (runs.some((r) => r.checkSuiteId !== void 0)) {
|
|
9465
|
+
const workflowRuns = await readWorkflowRunsAt(client, repository, commit);
|
|
9466
|
+
if (workflowRuns === null) return unreadable("workflow_runs");
|
|
9467
|
+
for (const run of runs) {
|
|
9468
|
+
const workflow = run.checkSuiteId === void 0 ? void 0 : workflowRuns.get(run.checkSuiteId);
|
|
9469
|
+
if (workflow === void 0) continue;
|
|
9470
|
+
run.workflowPath = workflow.path;
|
|
9471
|
+
run.workflowRunId = workflow.id;
|
|
9472
|
+
run.runAttempt = workflow.attempt;
|
|
9473
|
+
}
|
|
9474
|
+
}
|
|
9191
9475
|
return {
|
|
9192
9476
|
status: "ok",
|
|
9193
9477
|
commit,
|
|
@@ -9195,6 +9479,35 @@ async function readCheckRuns(client, repository, commit, excludedIds) {
|
|
|
9195
9479
|
runs: [...new Map(runs.map((r) => [r.id, r])).values()].sort((a, b) => a.id - b.id)
|
|
9196
9480
|
};
|
|
9197
9481
|
}
|
|
9482
|
+
async function readWorkflowRunsAt(client, repository, commit) {
|
|
9483
|
+
const listed2 = await readAllPages(
|
|
9484
|
+
client,
|
|
9485
|
+
"workflow_runs",
|
|
9486
|
+
(page) => `/repos/${repository}/actions/runs?head_sha=${commit}&per_page=${PER_PAGE}&page=${page}`,
|
|
9487
|
+
(json2) => arr(obj(json2).workflow_runs),
|
|
9488
|
+
(item) => {
|
|
9489
|
+
const r = obj(item);
|
|
9490
|
+
let suite = null;
|
|
9491
|
+
if (r.check_suite_id !== null && r.check_suite_id !== void 0) {
|
|
9492
|
+
suite = int(r.check_suite_id);
|
|
9493
|
+
if (suite < 1) throw new ShapeError();
|
|
9494
|
+
}
|
|
9495
|
+
const run = { id: int(r.id), attempt: int(r.run_attempt), path: str(r.path) };
|
|
9496
|
+
run.path = run.path.replace(/@.*$/, "");
|
|
9497
|
+
if (run.id < 1 || run.attempt < 1 || run.path === "") throw new ShapeError();
|
|
9498
|
+
return { suite, run };
|
|
9499
|
+
}
|
|
9500
|
+
);
|
|
9501
|
+
if (listed2 === null) return null;
|
|
9502
|
+
const bySuite = /* @__PURE__ */ new Map();
|
|
9503
|
+
for (const { suite, run } of listed2) {
|
|
9504
|
+
if (suite === null) continue;
|
|
9505
|
+
const seen = bySuite.get(suite);
|
|
9506
|
+
if (seen !== void 0 && seen.id !== run.id) return null;
|
|
9507
|
+
bySuite.set(suite, run);
|
|
9508
|
+
}
|
|
9509
|
+
return bySuite;
|
|
9510
|
+
}
|
|
9198
9511
|
async function readTestRecord(client, repository, record2, headCommit) {
|
|
9199
9512
|
const workflowFile = record2.workflow.split("/").at(-1);
|
|
9200
9513
|
const runsPath = (page) => `/repos/${repository}/actions/workflows/${encodeURIComponent(workflowFile)}/runs?head_sha=${headCommit}&per_page=${PER_PAGE}&page=${page}`;
|
|
@@ -9344,6 +9657,10 @@ async function readEvidence(client, input2) {
|
|
|
9344
9657
|
evidence.closingReferences
|
|
9345
9658
|
);
|
|
9346
9659
|
if (references !== void 0) evidence.references = references;
|
|
9660
|
+
const closeTargetRefs = closeTargets(block, repository, evidence.closingReferences, references);
|
|
9661
|
+
if (closeTargetRefs.length > 0) {
|
|
9662
|
+
evidence.closeEvents = await readCloseEvents(client, repository, pr.number, closeTargetRefs);
|
|
9663
|
+
}
|
|
9347
9664
|
if (block.checks !== void 0) {
|
|
9348
9665
|
evidence.checkRuns = await readCheckRuns(
|
|
9349
9666
|
client,
|
|
@@ -9432,7 +9749,7 @@ async function reportFromComment(client, repository, number4, login) {
|
|
|
9432
9749
|
// src/record/checker.ts
|
|
9433
9750
|
import { readFileSync } from "node:fs";
|
|
9434
9751
|
var CHECKER_NAME = "dunstan";
|
|
9435
|
-
var CHECKER_VERSION = "0.1.
|
|
9752
|
+
var CHECKER_VERSION = "0.1.8";
|
|
9436
9753
|
function checkerIdentity(artifact) {
|
|
9437
9754
|
return {
|
|
9438
9755
|
name: CHECKER_NAME,
|
|
@@ -9605,601 +9922,7 @@ var FILES = { "handback-block-0.1.schema.json": `{
|
|
|
9605
9922
|
}
|
|
9606
9923
|
}
|
|
9607
9924
|
}
|
|
9608
|
-
`, "record-0.1.schema.json": `{
|
|
9609
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
9610
|
-
"$id": "https://barglabs.ai/dunstan/spec/schema/record-0.1.schema.json",
|
|
9611
|
-
"title": "Dunstan record, v0.1",
|
|
9612
|
-
"description": "An in-toto Statement v1 whose predicate records one check of a handback block against the record. See spec/claim-format.md.",
|
|
9613
|
-
"type": "object",
|
|
9614
|
-
"required": ["_type", "subject", "predicateType", "predicate"],
|
|
9615
|
-
"additionalProperties": false,
|
|
9616
|
-
"properties": {
|
|
9617
|
-
"_type": { "const": "https://in-toto.io/Statement/v1" },
|
|
9618
|
-
"subject": {
|
|
9619
|
-
"type": "array",
|
|
9620
|
-
"minItems": 1,
|
|
9621
|
-
"maxItems": 2,
|
|
9622
|
-
"prefixItems": [{ "$ref": "#/$defs/commitSubject" }, { "$ref": "#/$defs/blockSubject" }],
|
|
9623
|
-
"items": false
|
|
9624
|
-
},
|
|
9625
|
-
"predicateType": { "const": "https://barglabs.ai/dunstan/record/v0.1" },
|
|
9626
|
-
"predicate": { "$ref": "#/$defs/predicate" }
|
|
9627
|
-
},
|
|
9628
|
-
"allOf": [
|
|
9629
|
-
{
|
|
9630
|
-
"if": {
|
|
9631
|
-
"properties": {
|
|
9632
|
-
"predicate": {
|
|
9633
|
-
"properties": { "block": { "properties": { "status": { "const": "found" } } } }
|
|
9634
|
-
}
|
|
9635
|
-
}
|
|
9636
|
-
},
|
|
9637
|
-
"then": { "properties": { "subject": { "minItems": 2 } } },
|
|
9638
|
-
"else": { "properties": { "subject": { "maxItems": 1 } } }
|
|
9639
|
-
}
|
|
9640
|
-
],
|
|
9641
|
-
"$defs": {
|
|
9642
|
-
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
9643
|
-
"commitSha": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
|
|
9644
|
-
"repository": {
|
|
9645
|
-
"description": "owner/repo",
|
|
9646
|
-
"type": "string",
|
|
9647
|
-
"pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}$"
|
|
9648
|
-
},
|
|
9649
|
-
"issueRef": {
|
|
9650
|
-
"description": "owner/repo#N, always fully qualified in evidence.",
|
|
9651
|
-
"type": "string",
|
|
9652
|
-
"pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})/[A-Za-z0-9._-]{1,100}#[1-9][0-9]{0,9}$"
|
|
9653
|
-
},
|
|
9654
|
-
"timestamp": {
|
|
9655
|
-
"type": "string",
|
|
9656
|
-
"format": "date-time",
|
|
9657
|
-
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\\\\.[0-9]+)?Z$"
|
|
9658
|
-
},
|
|
9659
|
-
"sourceKind": {
|
|
9660
|
-
"enum": [
|
|
9661
|
-
"pull_request",
|
|
9662
|
-
"pull_request_files",
|
|
9663
|
-
"closing_references",
|
|
9664
|
-
"repository",
|
|
9665
|
-
"issue",
|
|
9666
|
-
"commit",
|
|
9667
|
-
"compare",
|
|
9668
|
-
"check_runs",
|
|
9669
|
-
"workflow_runs",
|
|
9670
|
-
"artifact",
|
|
9671
|
-
"deployments"
|
|
9672
|
-
]
|
|
9673
|
-
},
|
|
9674
|
-
"commitSubject": {
|
|
9675
|
-
"type": "object",
|
|
9676
|
-
"required": ["name", "digest"],
|
|
9677
|
-
"additionalProperties": false,
|
|
9678
|
-
"properties": {
|
|
9679
|
-
"name": {
|
|
9680
|
-
"type": "string",
|
|
9681
|
-
"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}$"
|
|
9682
|
-
},
|
|
9683
|
-
"digest": {
|
|
9684
|
-
"type": "object",
|
|
9685
|
-
"required": ["gitCommit"],
|
|
9686
|
-
"additionalProperties": false,
|
|
9687
|
-
"properties": { "gitCommit": { "$ref": "#/$defs/commitSha" } }
|
|
9688
|
-
}
|
|
9689
|
-
}
|
|
9690
|
-
},
|
|
9691
|
-
"blockSubject": {
|
|
9692
|
-
"type": "object",
|
|
9693
|
-
"required": ["name", "digest"],
|
|
9694
|
-
"additionalProperties": false,
|
|
9695
|
-
"properties": {
|
|
9696
|
-
"name": { "const": "handback-block" },
|
|
9697
|
-
"digest": {
|
|
9698
|
-
"type": "object",
|
|
9699
|
-
"required": ["sha256"],
|
|
9700
|
-
"additionalProperties": false,
|
|
9701
|
-
"properties": { "sha256": { "$ref": "#/$defs/sha256" } }
|
|
9702
|
-
}
|
|
9703
|
-
}
|
|
9704
|
-
},
|
|
9705
|
-
"predicate": {
|
|
9706
|
-
"type": "object",
|
|
9707
|
-
"required": [
|
|
9708
|
-
"spec",
|
|
9709
|
-
"checker",
|
|
9710
|
-
"report",
|
|
9711
|
-
"block",
|
|
9712
|
-
"subject",
|
|
9713
|
-
"evidence",
|
|
9714
|
-
"claims",
|
|
9715
|
-
"verdict",
|
|
9716
|
-
"digests",
|
|
9717
|
-
"rerun",
|
|
9718
|
-
"assurance"
|
|
9719
|
-
],
|
|
9720
|
-
"additionalProperties": false,
|
|
9721
|
-
"properties": {
|
|
9722
|
-
"spec": {
|
|
9723
|
-
"description": "The spec version the checker implements, semver.",
|
|
9724
|
-
"type": "string",
|
|
9725
|
-
"pattern": "^0\\\\.1\\\\.(?:0|[1-9][0-9]*)$"
|
|
9726
|
-
},
|
|
9727
|
-
"checker": {
|
|
9728
|
-
"type": "object",
|
|
9729
|
-
"required": ["name", "version", "digest"],
|
|
9730
|
-
"additionalProperties": false,
|
|
9731
|
-
"properties": {
|
|
9732
|
-
"name": { "type": "string", "minLength": 1, "maxLength": 214 },
|
|
9733
|
-
"version": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
9734
|
-
"digest": {
|
|
9735
|
-
"type": "object",
|
|
9736
|
-
"required": ["sha256"],
|
|
9737
|
-
"additionalProperties": false,
|
|
9738
|
-
"properties": { "sha256": { "$ref": "#/$defs/sha256" } }
|
|
9739
|
-
}
|
|
9740
|
-
}
|
|
9741
|
-
},
|
|
9742
|
-
"report": {
|
|
9743
|
-
"description": "The report's digest and where it was read. Never its text.",
|
|
9744
|
-
"type": "object",
|
|
9745
|
-
"required": ["sha256", "source"],
|
|
9746
|
-
"additionalProperties": false,
|
|
9747
|
-
"properties": {
|
|
9748
|
-
"sha256": { "$ref": "#/$defs/sha256" },
|
|
9749
|
-
"source": {
|
|
9750
|
-
"type": "object",
|
|
9751
|
-
"required": ["kind", "locator"],
|
|
9752
|
-
"additionalProperties": false,
|
|
9753
|
-
"properties": {
|
|
9754
|
-
"kind": { "enum": ["pr-body", "pr-comment", "file", "stdin", "api"] },
|
|
9755
|
-
"locator": { "type": "string", "minLength": 1, "maxLength": 2000 }
|
|
9756
|
-
}
|
|
9757
|
-
}
|
|
9758
|
-
}
|
|
9759
|
-
},
|
|
9760
|
-
"block": { "$ref": "#/$defs/block" },
|
|
9761
|
-
"subject": {
|
|
9762
|
-
"type": "object",
|
|
9763
|
-
"required": ["repository", "pullRequest", "headSha", "mergeSha"],
|
|
9764
|
-
"additionalProperties": false,
|
|
9765
|
-
"properties": {
|
|
9766
|
-
"repository": { "$ref": "#/$defs/repository" },
|
|
9767
|
-
"pullRequest": { "type": "integer", "minimum": 1 },
|
|
9768
|
-
"headSha": { "$ref": "#/$defs/commitSha" },
|
|
9769
|
-
"mergeSha": {
|
|
9770
|
-
"oneOf": [{ "$ref": "#/$defs/commitSha" }, { "type": "null" }]
|
|
9771
|
-
}
|
|
9772
|
-
}
|
|
9773
|
-
},
|
|
9774
|
-
"evidence": { "$ref": "#/$defs/evidence" },
|
|
9775
|
-
"claims": {
|
|
9776
|
-
"type": "array",
|
|
9777
|
-
"items": { "$ref": "#/$defs/claim" }
|
|
9778
|
-
},
|
|
9779
|
-
"verdict": { "$ref": "#/$defs/verdict" },
|
|
9780
|
-
"digests": {
|
|
9781
|
-
"type": "object",
|
|
9782
|
-
"required": ["claims", "evidence"],
|
|
9783
|
-
"additionalProperties": false,
|
|
9784
|
-
"properties": {
|
|
9785
|
-
"claims": { "$ref": "#/$defs/sha256" },
|
|
9786
|
-
"evidence": { "$ref": "#/$defs/sha256" }
|
|
9787
|
-
}
|
|
9788
|
-
},
|
|
9789
|
-
"rerun": {
|
|
9790
|
-
"type": "object",
|
|
9791
|
-
"required": ["offline", "online"],
|
|
9792
|
-
"additionalProperties": false,
|
|
9793
|
-
"properties": {
|
|
9794
|
-
"offline": { "type": "string", "minLength": 1, "maxLength": 2000 },
|
|
9795
|
-
"online": { "type": "string", "minLength": 1, "maxLength": 2000 }
|
|
9796
|
-
}
|
|
9797
|
-
},
|
|
9798
|
-
"assurance": {
|
|
9799
|
-
"oneOf": [
|
|
9800
|
-
{
|
|
9801
|
-
"type": "object",
|
|
9802
|
-
"required": ["status", "issuer"],
|
|
9803
|
-
"additionalProperties": false,
|
|
9804
|
-
"properties": {
|
|
9805
|
-
"status": { "const": "unsigned" },
|
|
9806
|
-
"issuer": { "const": "self-generated" }
|
|
9807
|
-
}
|
|
9808
|
-
},
|
|
9809
|
-
{
|
|
9810
|
-
"type": "object",
|
|
9811
|
-
"required": ["status", "issuer", "keyFingerprint"],
|
|
9812
|
-
"additionalProperties": false,
|
|
9813
|
-
"properties": {
|
|
9814
|
-
"status": { "const": "signed" },
|
|
9815
|
-
"issuer": { "type": "string", "minLength": 1, "maxLength": 320 },
|
|
9816
|
-
"keyFingerprint": { "type": "string", "pattern": "^SHA256:[A-Za-z0-9+/]{43}$" }
|
|
9817
|
-
}
|
|
9818
|
-
}
|
|
9819
|
-
]
|
|
9820
|
-
}
|
|
9821
|
-
}
|
|
9822
|
-
},
|
|
9823
|
-
"verdict": { "enum": ["pass", "fail", "unverifiable"] },
|
|
9824
|
-
"blockError": {
|
|
9825
|
-
"type": "object",
|
|
9826
|
-
"required": ["code"],
|
|
9827
|
-
"additionalProperties": false,
|
|
9828
|
-
"properties": {
|
|
9829
|
-
"code": {
|
|
9830
|
-
"enum": [
|
|
9831
|
-
"unterminated_fence",
|
|
9832
|
-
"invalid_json",
|
|
9833
|
-
"duplicate_member",
|
|
9834
|
-
"unsupported_version",
|
|
9835
|
-
"schema_violation"
|
|
9836
|
-
]
|
|
9837
|
-
},
|
|
9838
|
-
"pointer": { "type": "string", "pattern": "^(?:/.*)?$" },
|
|
9839
|
-
"keyword": { "type": "string", "minLength": 1 }
|
|
9840
|
-
}
|
|
9841
|
-
},
|
|
9842
|
-
"block": {
|
|
9843
|
-
"oneOf": [
|
|
9844
|
-
{
|
|
9845
|
-
"type": "object",
|
|
9846
|
-
"required": ["status", "sha256", "value"],
|
|
9847
|
-
"additionalProperties": false,
|
|
9848
|
-
"properties": {
|
|
9849
|
-
"status": { "const": "found" },
|
|
9850
|
-
"sha256": { "$ref": "#/$defs/sha256" },
|
|
9851
|
-
"value": { "$ref": "handback-block-0.1.schema.json" }
|
|
9852
|
-
}
|
|
9853
|
-
},
|
|
9854
|
-
{
|
|
9855
|
-
"type": "object",
|
|
9856
|
-
"required": ["status", "reason", "sha256", "value"],
|
|
9857
|
-
"additionalProperties": false,
|
|
9858
|
-
"properties": {
|
|
9859
|
-
"status": { "const": "missing" },
|
|
9860
|
-
"reason": { "const": "block_missing" },
|
|
9861
|
-
"sha256": { "type": "null" },
|
|
9862
|
-
"value": { "type": "null" }
|
|
9863
|
-
}
|
|
9864
|
-
},
|
|
9865
|
-
{
|
|
9866
|
-
"type": "object",
|
|
9867
|
-
"required": ["status", "reason", "count", "sha256", "value"],
|
|
9868
|
-
"additionalProperties": false,
|
|
9869
|
-
"properties": {
|
|
9870
|
-
"status": { "const": "ambiguous" },
|
|
9871
|
-
"reason": { "const": "block_ambiguous" },
|
|
9872
|
-
"count": { "type": "integer", "minimum": 2 },
|
|
9873
|
-
"sha256": { "type": "null" },
|
|
9874
|
-
"value": { "type": "null" }
|
|
9875
|
-
}
|
|
9876
|
-
},
|
|
9877
|
-
{
|
|
9878
|
-
"type": "object",
|
|
9879
|
-
"required": ["status", "reason", "errors", "sha256", "value"],
|
|
9880
|
-
"additionalProperties": false,
|
|
9881
|
-
"properties": {
|
|
9882
|
-
"status": { "const": "invalid" },
|
|
9883
|
-
"reason": { "const": "block_invalid" },
|
|
9884
|
-
"errors": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/blockError" } },
|
|
9885
|
-
"sha256": { "type": "null" },
|
|
9886
|
-
"value": { "type": "null" }
|
|
9887
|
-
}
|
|
9888
|
-
}
|
|
9889
|
-
]
|
|
9890
|
-
},
|
|
9891
|
-
"claim": {
|
|
9892
|
-
"type": "object",
|
|
9893
|
-
"required": ["id", "check", "field", "declared", "observed", "verdict"],
|
|
9894
|
-
"additionalProperties": false,
|
|
9895
|
-
"properties": {
|
|
9896
|
-
"id": { "type": "string", "minLength": 1 },
|
|
9897
|
-
"check": { "enum": ["head", "scope", "reference", "count", "time"] },
|
|
9898
|
-
"field": {
|
|
9899
|
-
"description": "A JSON Pointer (RFC 6901) into the block.",
|
|
9900
|
-
"type": "string",
|
|
9901
|
-
"pattern": "^/.+$"
|
|
9902
|
-
},
|
|
9903
|
-
"declared": true,
|
|
9904
|
-
"observed": true,
|
|
9905
|
-
"verdict": { "$ref": "#/$defs/verdict" },
|
|
9906
|
-
"reason": { "$ref": "#/$defs/claimReason" }
|
|
9907
|
-
},
|
|
9908
|
-
"if": { "properties": { "verdict": { "const": "pass" } } },
|
|
9909
|
-
"then": { "not": { "required": ["reason"] } },
|
|
9910
|
-
"else": { "required": ["reason"] }
|
|
9911
|
-
},
|
|
9912
|
-
"claimReason": {
|
|
9913
|
-
"type": "string",
|
|
9914
|
-
"pattern": "^(?:head_mismatch|declared_not_changed|undeclared_file|file_list_truncated|not_closing|closing_link_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|closing_references|repository|issue|commit|compare|check_runs|workflow_runs|artifact|deployments))$"
|
|
9915
|
-
},
|
|
9916
|
-
"unread": {
|
|
9917
|
-
"description": "A section the checker could not use: its source errored, or the source answered without the field.",
|
|
9918
|
-
"oneOf": [
|
|
9919
|
-
{
|
|
9920
|
-
"type": "object",
|
|
9921
|
-
"required": ["status", "source"],
|
|
9922
|
-
"properties": {
|
|
9923
|
-
"status": { "const": "unreadable" },
|
|
9924
|
-
"source": { "$ref": "#/$defs/sourceKind" }
|
|
9925
|
-
}
|
|
9926
|
-
},
|
|
9927
|
-
{
|
|
9928
|
-
"type": "object",
|
|
9929
|
-
"required": ["status", "field"],
|
|
9930
|
-
"properties": {
|
|
9931
|
-
"status": { "const": "unpopulated" },
|
|
9932
|
-
"field": { "type": "string", "pattern": "^[A-Za-z0-9_.]+$" }
|
|
9933
|
-
}
|
|
9934
|
-
}
|
|
9935
|
-
]
|
|
9936
|
-
},
|
|
9937
|
-
"evidence": {
|
|
9938
|
-
"type": "object",
|
|
9939
|
-
"required": ["pullRequest", "sources"],
|
|
9940
|
-
"additionalProperties": false,
|
|
9941
|
-
"properties": {
|
|
9942
|
-
"pullRequest": {
|
|
9943
|
-
"description": "Always read: a checker that cannot read the pull request writes no record.",
|
|
9944
|
-
"type": "object",
|
|
9945
|
-
"required": [
|
|
9946
|
-
"status",
|
|
9947
|
-
"number",
|
|
9948
|
-
"state",
|
|
9949
|
-
"merged",
|
|
9950
|
-
"mergedAt",
|
|
9951
|
-
"headSha",
|
|
9952
|
-
"mergeSha",
|
|
9953
|
-
"changedFiles"
|
|
9954
|
-
],
|
|
9955
|
-
"additionalProperties": false,
|
|
9956
|
-
"properties": {
|
|
9957
|
-
"status": { "const": "ok" },
|
|
9958
|
-
"number": { "type": "integer", "minimum": 1 },
|
|
9959
|
-
"state": { "enum": ["open", "closed"] },
|
|
9960
|
-
"merged": { "type": "boolean" },
|
|
9961
|
-
"mergedAt": { "oneOf": [{ "$ref": "#/$defs/timestamp" }, { "type": "null" }] },
|
|
9962
|
-
"headSha": { "$ref": "#/$defs/commitSha" },
|
|
9963
|
-
"mergeSha": { "oneOf": [{ "$ref": "#/$defs/commitSha" }, { "type": "null" }] },
|
|
9964
|
-
"changedFiles": { "type": "integer", "minimum": 0 }
|
|
9965
|
-
}
|
|
9966
|
-
},
|
|
9967
|
-
"files": {
|
|
9968
|
-
"oneOf": [
|
|
9969
|
-
{
|
|
9970
|
-
"type": "object",
|
|
9971
|
-
"required": ["status", "complete", "entries"],
|
|
9972
|
-
"additionalProperties": false,
|
|
9973
|
-
"properties": {
|
|
9974
|
-
"status": { "const": "ok" },
|
|
9975
|
-
"complete": { "type": "boolean" },
|
|
9976
|
-
"entries": {
|
|
9977
|
-
"type": "array",
|
|
9978
|
-
"items": {
|
|
9979
|
-
"type": "object",
|
|
9980
|
-
"required": ["path", "status"],
|
|
9981
|
-
"additionalProperties": false,
|
|
9982
|
-
"properties": {
|
|
9983
|
-
"path": { "type": "string", "minLength": 1 },
|
|
9984
|
-
"status": {
|
|
9985
|
-
"enum": [
|
|
9986
|
-
"added",
|
|
9987
|
-
"removed",
|
|
9988
|
-
"modified",
|
|
9989
|
-
"renamed",
|
|
9990
|
-
"copied",
|
|
9991
|
-
"changed",
|
|
9992
|
-
"unchanged"
|
|
9993
|
-
]
|
|
9994
|
-
},
|
|
9995
|
-
"previousPath": { "type": "string", "minLength": 1 }
|
|
9996
|
-
}
|
|
9997
|
-
}
|
|
9998
|
-
}
|
|
9999
|
-
}
|
|
10000
|
-
},
|
|
10001
|
-
{ "$ref": "#/$defs/unreadOnly" }
|
|
10002
|
-
]
|
|
10003
|
-
},
|
|
10004
|
-
"closingReferences": {
|
|
10005
|
-
"oneOf": [
|
|
10006
|
-
{
|
|
10007
|
-
"type": "object",
|
|
10008
|
-
"required": ["status", "issues"],
|
|
10009
|
-
"additionalProperties": false,
|
|
10010
|
-
"properties": {
|
|
10011
|
-
"status": { "const": "ok" },
|
|
10012
|
-
"issues": { "type": "array", "items": { "$ref": "#/$defs/issueRef" } }
|
|
10013
|
-
}
|
|
10014
|
-
},
|
|
10015
|
-
{ "$ref": "#/$defs/unreadOnly" }
|
|
10016
|
-
]
|
|
10017
|
-
},
|
|
10018
|
-
"references": {
|
|
10019
|
-
"type": "array",
|
|
10020
|
-
"items": {
|
|
10021
|
-
"oneOf": [
|
|
10022
|
-
{
|
|
10023
|
-
"type": "object",
|
|
10024
|
-
"required": ["kind", "ref", "status", "exists"],
|
|
10025
|
-
"additionalProperties": false,
|
|
10026
|
-
"properties": {
|
|
10027
|
-
"kind": { "const": "issue" },
|
|
10028
|
-
"ref": { "$ref": "#/$defs/issueRef" },
|
|
10029
|
-
"status": { "const": "ok" },
|
|
10030
|
-
"exists": { "type": "boolean" },
|
|
10031
|
-
"resolvedAs": {
|
|
10032
|
-
"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).",
|
|
10033
|
-
"$ref": "#/$defs/issueRef"
|
|
10034
|
-
}
|
|
10035
|
-
},
|
|
10036
|
-
"if": { "required": ["resolvedAs"] },
|
|
10037
|
-
"then": { "properties": { "exists": { "const": true } } }
|
|
10038
|
-
},
|
|
10039
|
-
{
|
|
10040
|
-
"type": "object",
|
|
10041
|
-
"required": ["kind", "ref", "status", "exists", "reachableFromHead"],
|
|
10042
|
-
"additionalProperties": false,
|
|
10043
|
-
"properties": {
|
|
10044
|
-
"kind": { "const": "commit" },
|
|
10045
|
-
"ref": { "$ref": "#/$defs/commitSha" },
|
|
10046
|
-
"status": { "const": "ok" },
|
|
10047
|
-
"exists": { "type": "boolean" },
|
|
10048
|
-
"reachableFromHead": { "type": "boolean" }
|
|
10049
|
-
}
|
|
10050
|
-
},
|
|
10051
|
-
{
|
|
10052
|
-
"type": "object",
|
|
10053
|
-
"required": ["kind", "ref"],
|
|
10054
|
-
"properties": {
|
|
10055
|
-
"kind": { "enum": ["issue", "commit"] },
|
|
10056
|
-
"ref": { "type": "string", "minLength": 1 }
|
|
10057
|
-
},
|
|
10058
|
-
"allOf": [{ "$ref": "#/$defs/unread" }]
|
|
10059
|
-
}
|
|
10060
|
-
]
|
|
10061
|
-
}
|
|
10062
|
-
},
|
|
10063
|
-
"checkRuns": {
|
|
10064
|
-
"oneOf": [
|
|
10065
|
-
{
|
|
10066
|
-
"type": "object",
|
|
10067
|
-
"required": ["status", "commit", "excludedIds", "runs"],
|
|
10068
|
-
"additionalProperties": false,
|
|
10069
|
-
"properties": {
|
|
10070
|
-
"status": { "const": "ok" },
|
|
10071
|
-
"commit": { "$ref": "#/$defs/commitSha" },
|
|
10072
|
-
"excludedIds": {
|
|
10073
|
-
"description": "Check runs left out of the count: the run the checker itself executes in.",
|
|
10074
|
-
"type": "array",
|
|
10075
|
-
"items": { "type": "integer", "minimum": 1 }
|
|
10076
|
-
},
|
|
10077
|
-
"runs": {
|
|
10078
|
-
"type": "array",
|
|
10079
|
-
"items": {
|
|
10080
|
-
"type": "object",
|
|
10081
|
-
"required": ["id", "name", "status", "conclusion"],
|
|
10082
|
-
"additionalProperties": false,
|
|
10083
|
-
"properties": {
|
|
10084
|
-
"id": { "type": "integer", "minimum": 1 },
|
|
10085
|
-
"name": { "type": "string" },
|
|
10086
|
-
"status": { "type": "string", "minLength": 1 },
|
|
10087
|
-
"conclusion": { "type": ["string", "null"] }
|
|
10088
|
-
}
|
|
10089
|
-
}
|
|
10090
|
-
}
|
|
10091
|
-
}
|
|
10092
|
-
},
|
|
10093
|
-
{ "$ref": "#/$defs/unreadOnly" }
|
|
10094
|
-
]
|
|
10095
|
-
},
|
|
10096
|
-
"testRecords": {
|
|
10097
|
-
"type": "array",
|
|
10098
|
-
"items": {
|
|
10099
|
-
"oneOf": [
|
|
10100
|
-
{
|
|
10101
|
-
"type": "object",
|
|
10102
|
-
"required": ["record", "status", "runs"],
|
|
10103
|
-
"additionalProperties": false,
|
|
10104
|
-
"properties": {
|
|
10105
|
-
"record": { "$ref": "handback-block-0.1.schema.json#/$defs/junitRecord" },
|
|
10106
|
-
"status": { "const": "ok" },
|
|
10107
|
-
"runs": {
|
|
10108
|
-
"type": "array",
|
|
10109
|
-
"items": {
|
|
10110
|
-
"type": "object",
|
|
10111
|
-
"required": ["runId", "runAttempt", "executed", "failed"],
|
|
10112
|
-
"additionalProperties": false,
|
|
10113
|
-
"properties": {
|
|
10114
|
-
"runId": { "type": "integer", "minimum": 1 },
|
|
10115
|
-
"runAttempt": { "type": "integer", "minimum": 1 },
|
|
10116
|
-
"executed": { "type": "integer", "minimum": 0 },
|
|
10117
|
-
"failed": { "type": "integer", "minimum": 0 }
|
|
10118
|
-
}
|
|
10119
|
-
}
|
|
10120
|
-
}
|
|
10121
|
-
}
|
|
10122
|
-
},
|
|
10123
|
-
{
|
|
10124
|
-
"type": "object",
|
|
10125
|
-
"required": ["record"],
|
|
10126
|
-
"properties": {
|
|
10127
|
-
"record": { "$ref": "handback-block-0.1.schema.json#/$defs/junitRecord" }
|
|
10128
|
-
},
|
|
10129
|
-
"allOf": [{ "$ref": "#/$defs/unread" }]
|
|
10130
|
-
}
|
|
10131
|
-
]
|
|
10132
|
-
}
|
|
10133
|
-
},
|
|
10134
|
-
"deployments": {
|
|
10135
|
-
"type": "array",
|
|
10136
|
-
"items": {
|
|
10137
|
-
"oneOf": [
|
|
10138
|
-
{
|
|
10139
|
-
"type": "object",
|
|
10140
|
-
"required": ["environment", "status", "deployments"],
|
|
10141
|
-
"additionalProperties": false,
|
|
10142
|
-
"properties": {
|
|
10143
|
-
"environment": { "type": "string", "minLength": 1 },
|
|
10144
|
-
"status": { "const": "ok" },
|
|
10145
|
-
"deployments": {
|
|
10146
|
-
"type": "array",
|
|
10147
|
-
"items": {
|
|
10148
|
-
"type": "object",
|
|
10149
|
-
"required": ["id", "sha", "relation", "successAt"],
|
|
10150
|
-
"additionalProperties": false,
|
|
10151
|
-
"properties": {
|
|
10152
|
-
"id": { "type": "integer", "minimum": 1 },
|
|
10153
|
-
"sha": { "$ref": "#/$defs/commitSha" },
|
|
10154
|
-
"relation": {
|
|
10155
|
-
"enum": ["head", "merge", "descendant", "unrelated", "unknown"]
|
|
10156
|
-
},
|
|
10157
|
-
"successAt": {
|
|
10158
|
-
"oneOf": [{ "$ref": "#/$defs/timestamp" }, { "type": "null" }]
|
|
10159
|
-
}
|
|
10160
|
-
}
|
|
10161
|
-
}
|
|
10162
|
-
}
|
|
10163
|
-
}
|
|
10164
|
-
},
|
|
10165
|
-
{
|
|
10166
|
-
"type": "object",
|
|
10167
|
-
"required": ["environment"],
|
|
10168
|
-
"properties": { "environment": { "type": "string", "minLength": 1 } },
|
|
10169
|
-
"allOf": [{ "$ref": "#/$defs/unread" }]
|
|
10170
|
-
}
|
|
10171
|
-
]
|
|
10172
|
-
}
|
|
10173
|
-
},
|
|
10174
|
-
"sources": {
|
|
10175
|
-
"type": "array",
|
|
10176
|
-
"items": {
|
|
10177
|
-
"type": "object",
|
|
10178
|
-
"required": ["kind", "locator", "sha256", "readAt"],
|
|
10179
|
-
"additionalProperties": false,
|
|
10180
|
-
"properties": {
|
|
10181
|
-
"kind": { "$ref": "#/$defs/sourceKind" },
|
|
10182
|
-
"locator": { "type": "string", "minLength": 1, "maxLength": 2000 },
|
|
10183
|
-
"sha256": { "$ref": "#/$defs/sha256" },
|
|
10184
|
-
"etag": { "type": "string", "minLength": 1 },
|
|
10185
|
-
"readAt": { "$ref": "#/$defs/timestamp" },
|
|
10186
|
-
"error": {
|
|
10187
|
-
"description": "Present when the read failed: http_<status>, network, or parse.",
|
|
10188
|
-
"type": "string",
|
|
10189
|
-
"pattern": "^(?:http_[1-5][0-9]{2}|network|parse)$"
|
|
10190
|
-
}
|
|
10191
|
-
}
|
|
10192
|
-
}
|
|
10193
|
-
}
|
|
10194
|
-
}
|
|
10195
|
-
},
|
|
10196
|
-
"unreadOnly": {
|
|
10197
|
-
"allOf": [{ "$ref": "#/$defs/unread" }],
|
|
10198
|
-
"unevaluatedProperties": false
|
|
10199
|
-
}
|
|
10200
|
-
}
|
|
10201
|
-
}
|
|
10202
|
-
`, "record-0.2-draft.schema.json": `{
|
|
9925
|
+
`, "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": `{
|
|
10203
9926
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
10204
9927
|
"$id": "https://barglabs.ai/dunstan/spec/schema/record-0.2-draft.schema.json",
|
|
10205
9928
|
"title": "Dunstan record, DRAFT 0.2.0",
|
|
@@ -10282,6 +10005,9 @@ var FILES = { "handback-block-0.1.schema.json": `{
|
|
|
10282
10005
|
"$ref": "record-0.1.schema.json#/$defs/evidence/properties/closingReferences"
|
|
10283
10006
|
},
|
|
10284
10007
|
"references": { "$ref": "record-0.1.schema.json#/$defs/evidence/properties/references" },
|
|
10008
|
+
"closeEvents": {
|
|
10009
|
+
"$ref": "record-0.1.schema.json#/$defs/evidence/properties/closeEvents"
|
|
10010
|
+
},
|
|
10285
10011
|
"checkRuns": { "$ref": "record-0.1.schema.json#/$defs/evidence/properties/checkRuns" },
|
|
10286
10012
|
"testRecords": {
|
|
10287
10013
|
"$ref": "record-0.1.schema.json#/$defs/evidence/properties/testRecords"
|
|
@@ -10527,7 +10253,7 @@ var FILES = { "handback-block-0.1.schema.json": `{
|
|
|
10527
10253
|
"additionalProperties": false,
|
|
10528
10254
|
"properties": {
|
|
10529
10255
|
"comparison": {
|
|
10530
|
-
"description": "The version of the rules that turn a proposed claim and the evidence into its note, separate from the extractor's. 0.1.0 compared a file claim by exact path only; 0.2.0 also matches it by name; 0.3.0 also asks whether a path not among the changed files exists at the head (D.11.4).",
|
|
10256
|
+
"description": "The version of the rules that turn a proposed claim and the evidence into its note, separate from the extractor's. 0.1.0 compared a file claim by exact path only; 0.2.0 also matches it by name; 0.3.0 also asks whether a path not among the changed files exists at the head (D.11.4); 0.4.0 compares a closes claim by who closed the target (spec 0.1.4).",
|
|
10531
10257
|
"type": "object",
|
|
10532
10258
|
"required": ["version"],
|
|
10533
10259
|
"additionalProperties": false,
|
|
@@ -10678,11 +10404,11 @@ var FILES = { "handback-block-0.1.schema.json": `{
|
|
|
10678
10404
|
"advisoryNote": {
|
|
10679
10405
|
"description": "agrees when the gate check would pass; differs:<reason> when it would fail; unanswered:<reason> when it would be unverifiable. The reasons are the 0.1 claim reasons. A file claim matched by name only is agrees_by_name, or unanswered:ambiguous_path when more than one changed path has that name. A file claim not among the changed files is unanswered:no_such_path when nothing is at its path at the head, unanswered:directory when a directory is, and unanswered:source_unreadable:path when the answer was not read (D.11.4). Never a verdict.",
|
|
10680
10406
|
"type": "string",
|
|
10681
|
-
"pattern": "^(?:agrees|agrees_by_name|unanswered:(?:ambiguous_path|no_such_path|directory|source_unreadable:path)|(?:differs|unanswered):(?:head_mismatch|declared_not_changed|file_list_truncated|not_closing|closing_link_unsettled|not_found|not_reachable|count_mismatch|all_succeeded_mismatch|checks_incomplete|no_check_runs|no_comparable_record_field|not_merged|premature|evidence_field_unpopulated:[A-Za-z0-9_.]+|source_unreadable:(?:pull_request|pull_request_files|closing_references|repository|issue|commit|compare|check_runs)))$"
|
|
10407
|
+
"pattern": "^(?:agrees|agrees_by_name|unanswered:(?:ambiguous_path|no_such_path|directory|source_unreadable:path)|(?:differs|unanswered):(?:head_mismatch|declared_not_changed|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|not_merged|premature|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)))$"
|
|
10682
10408
|
},
|
|
10683
10409
|
"readerReason": {
|
|
10684
10410
|
"type": "string",
|
|
10685
|
-
"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))$"
|
|
10411
|
+
"pattern": "^(?:no_matching_record_item|declared_item_not_among_candidates|candidate_not_in_evidence|ambiguous_candidates|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))$"
|
|
10686
10412
|
}
|
|
10687
10413
|
}
|
|
10688
10414
|
}
|
|
@@ -11682,7 +11408,7 @@ function precisionText(precision) {
|
|
|
11682
11408
|
}
|
|
11683
11409
|
|
|
11684
11410
|
// src/advisory/advise.ts
|
|
11685
|
-
var COMPARISON_VERSION = "0.
|
|
11411
|
+
var COMPARISON_VERSION = "0.4.0";
|
|
11686
11412
|
var COMPARISON = { version: COMPARISON_VERSION };
|
|
11687
11413
|
var HEX = /^[0-9a-f]{7,40}$/;
|
|
11688
11414
|
function blockFor(kind, value, headSha) {
|
|
@@ -11691,7 +11417,7 @@ function blockFor(kind, value, headSha) {
|
|
|
11691
11417
|
case "file_changed":
|
|
11692
11418
|
return typeof value === "string" ? { ...base, filesChanged: [value] } : void 0;
|
|
11693
11419
|
case "reference_closes":
|
|
11694
|
-
return typeof value === "string" ?
|
|
11420
|
+
return typeof value === "string" ? closesBlock(value, headSha) : void 0;
|
|
11695
11421
|
case "commit":
|
|
11696
11422
|
return typeof value === "string" && value.length === 40 ? { ...base, references: [{ commit: value, relation: "cites" }] } : void 0;
|
|
11697
11423
|
case "head_commit":
|
|
@@ -12753,6 +12479,7 @@ var SHA2 = /(?<![0-9A-Za-z])[0-9a-fA-F]{7,40}(?![0-9A-Za-z])/g;
|
|
|
12753
12479
|
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;
|
|
12754
12480
|
var WORD = /[A-Za-z0-9_]/;
|
|
12755
12481
|
var PATH_CHAR = /[A-Za-z0-9._/-]/;
|
|
12482
|
+
var JOB_NAME_CHAR = /[A-Za-z0-9_-]/;
|
|
12756
12483
|
function stringLeaves(value, out) {
|
|
12757
12484
|
if (typeof value === "string") out.push(value);
|
|
12758
12485
|
else if (Array.isArray(value)) for (const v of value) stringLeaves(v, out);
|
|
@@ -12763,17 +12490,31 @@ function stringLeaves(value, out) {
|
|
|
12763
12490
|
function claimTexts(claim2) {
|
|
12764
12491
|
return [claim2.text, ...stringLeaves(claim2.declaredValue, [])];
|
|
12765
12492
|
}
|
|
12766
|
-
function
|
|
12767
|
-
|
|
12493
|
+
function boundedAt(haystack, needle, inside) {
|
|
12494
|
+
const out = [];
|
|
12495
|
+
if (needle.length === 0) return out;
|
|
12768
12496
|
for (let at = haystack.indexOf(needle); at !== -1; at = haystack.indexOf(needle, at + 1)) {
|
|
12769
12497
|
const before = haystack[at - 1];
|
|
12770
12498
|
const after = haystack[at + needle.length];
|
|
12771
12499
|
const next = haystack[at + needle.length + 1];
|
|
12772
12500
|
const openBefore = before === void 0 || !inside.test(before);
|
|
12773
12501
|
const openAfter = after === void 0 || !inside.test(after) || after === "." && (next === void 0 || !/[A-Za-z0-9]/.test(next));
|
|
12774
|
-
if (openBefore && openAfter)
|
|
12502
|
+
if (openBefore && openAfter) out.push(at);
|
|
12775
12503
|
}
|
|
12776
|
-
return
|
|
12504
|
+
return out;
|
|
12505
|
+
}
|
|
12506
|
+
function occursBounded(haystack, needle, inside) {
|
|
12507
|
+
return boundedAt(haystack, needle, inside).length > 0;
|
|
12508
|
+
}
|
|
12509
|
+
function jobNamed(text, name, longer) {
|
|
12510
|
+
if (text === name) return true;
|
|
12511
|
+
return boundedAt(text, name, JOB_NAME_CHAR).some(
|
|
12512
|
+
(at) => !text.startsWith(" (", at + name.length) && !longer.some(
|
|
12513
|
+
(l) => boundedAt(text, l, JOB_NAME_CHAR).some(
|
|
12514
|
+
(from) => from <= at && at + name.length <= from + l.length
|
|
12515
|
+
)
|
|
12516
|
+
)
|
|
12517
|
+
);
|
|
12777
12518
|
}
|
|
12778
12519
|
function shaTokens(texts) {
|
|
12779
12520
|
const out = [];
|
|
@@ -12797,6 +12538,11 @@ function identifierMatches(claim2, items, repository) {
|
|
|
12797
12538
|
const shas = shaTokens(texts);
|
|
12798
12539
|
const issues = issueRefs(texts, repository);
|
|
12799
12540
|
const named = (value, inside) => texts.some((t) => t === value || occursBounded(t, value, inside));
|
|
12541
|
+
const qualified = (item) => item.type === "test" && item.id !== item.fields[0]?.[1] && named(item.id, WORD);
|
|
12542
|
+
const qualifiedNames = new Set(
|
|
12543
|
+
items.filter(qualified).map((item) => item.fields[0]?.[1])
|
|
12544
|
+
);
|
|
12545
|
+
const jobNames = items.filter((item) => item.type === "check_run").map((item) => item.fields[0]?.[1]);
|
|
12800
12546
|
const matches = /* @__PURE__ */ new Map();
|
|
12801
12547
|
items.forEach((item, i) => {
|
|
12802
12548
|
for (const [field, value] of item.fields) {
|
|
@@ -12812,9 +12558,13 @@ function identifierMatches(claim2, items, repository) {
|
|
|
12812
12558
|
case "file.previousPath":
|
|
12813
12559
|
hit = named(value, PATH_CHAR);
|
|
12814
12560
|
break;
|
|
12815
|
-
case "check_run.name":
|
|
12561
|
+
case "check_run.name": {
|
|
12562
|
+
const longer = jobNames.filter((n) => n.length > value.length && n.includes(value));
|
|
12563
|
+
hit = value.length >= MIN_NAME_LENGTH && texts.some((t) => jobNamed(t, value, longer));
|
|
12564
|
+
break;
|
|
12565
|
+
}
|
|
12816
12566
|
case "test.name":
|
|
12817
|
-
hit = value.length >= MIN_NAME_LENGTH && named(value, WORD);
|
|
12567
|
+
hit = value.length >= MIN_NAME_LENGTH && named(value, WORD) && (!qualifiedNames.has(value) || qualified(item));
|
|
12818
12568
|
break;
|
|
12819
12569
|
}
|
|
12820
12570
|
if (hit) {
|
|
@@ -12822,9 +12572,7 @@ function identifierMatches(claim2, items, repository) {
|
|
|
12822
12572
|
return;
|
|
12823
12573
|
}
|
|
12824
12574
|
}
|
|
12825
|
-
if (item
|
|
12826
|
-
matches.set(i, "id");
|
|
12827
|
-
}
|
|
12575
|
+
if (qualified(item)) matches.set(i, "id");
|
|
12828
12576
|
});
|
|
12829
12577
|
return matches;
|
|
12830
12578
|
}
|
|
@@ -12834,7 +12582,7 @@ var FILL_TO_K = "to-k";
|
|
|
12834
12582
|
var ARM_A_FILL = "A-fill";
|
|
12835
12583
|
var BM25_FLOOR = 1;
|
|
12836
12584
|
var SCORE_DECIMALS = 6;
|
|
12837
|
-
function
|
|
12585
|
+
function roundScore(score) {
|
|
12838
12586
|
const scale = 10 ** SCORE_DECIMALS;
|
|
12839
12587
|
return Math.round(score * scale) / scale;
|
|
12840
12588
|
}
|
|
@@ -12882,7 +12630,7 @@ function retrieve(claim2, items, opts) {
|
|
|
12882
12630
|
});
|
|
12883
12631
|
return;
|
|
12884
12632
|
}
|
|
12885
|
-
const score =
|
|
12633
|
+
const score = roundScore(scores[order] ?? 0);
|
|
12886
12634
|
if (!fill && score < BM25_FLOOR) return;
|
|
12887
12635
|
ranked.push({
|
|
12888
12636
|
order,
|
|
@@ -12905,6 +12653,7 @@ var READER_CLAIM_KINDS = /* @__PURE__ */ new Map([
|
|
|
12905
12653
|
["file_changed", "file"],
|
|
12906
12654
|
["commit_present", "commit"],
|
|
12907
12655
|
["reference_in_timeline", "timeline_event"],
|
|
12656
|
+
["reference_closes", "timeline_event"],
|
|
12908
12657
|
["check_succeeded", "check_run"],
|
|
12909
12658
|
["test_passed", "test"]
|
|
12910
12659
|
]);
|
|
@@ -12954,6 +12703,23 @@ function lookup(type, evidence) {
|
|
|
12954
12703
|
return entries;
|
|
12955
12704
|
}
|
|
12956
12705
|
var ISSUE_REF2 = /^(?:[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100})?#[1-9][0-9]{0,9}$/;
|
|
12706
|
+
function combinedCandidates(typed, floor) {
|
|
12707
|
+
const exact = typed.filter((c) => c.score === null);
|
|
12708
|
+
return exact.length > 0 ? exact : typed.filter((c) => c.score !== null && c.score >= floor);
|
|
12709
|
+
}
|
|
12710
|
+
function outcomeOf(type, entry) {
|
|
12711
|
+
if (type === "test") return entry.outcome;
|
|
12712
|
+
const run = entry;
|
|
12713
|
+
if (run.status !== "completed") return "not_completed";
|
|
12714
|
+
return run.conclusion === "success" ? "succeeded" : "failed";
|
|
12715
|
+
}
|
|
12716
|
+
function ambiguousCandidates(combined, outcome) {
|
|
12717
|
+
const scores = combined.map((c) => c.score === null ? null : roundScore(c.score));
|
|
12718
|
+
if (scores.length < 2 || scores.includes(null)) return false;
|
|
12719
|
+
const top = Math.max(...scores);
|
|
12720
|
+
const tied = combined.filter((_, i) => scores[i] === top);
|
|
12721
|
+
return tied.length >= 2 && new Set(tied.map(outcome)).size > 1;
|
|
12722
|
+
}
|
|
12957
12723
|
function checkReaderClaim(claim2, evidence, repository) {
|
|
12958
12724
|
const type = READER_CLAIM_KINDS.get(claim2.kind);
|
|
12959
12725
|
if (type === void 0) return unverifiable2("no_comparable_record_field");
|
|
@@ -12962,6 +12728,13 @@ function checkReaderClaim(claim2, evidence, repository) {
|
|
|
12962
12728
|
if (wantsBoolean ? typeof declared !== "boolean" : typeof declared !== "string") {
|
|
12963
12729
|
return unverifiable2("no_comparable_record_field");
|
|
12964
12730
|
}
|
|
12731
|
+
if (claim2.kind === "reference_closes") {
|
|
12732
|
+
if (!ISSUE_REF2.test(declared)) return unverifiable2("no_comparable_record_field");
|
|
12733
|
+
const head = evidence.pullRequest.headSha;
|
|
12734
|
+
const row = checkReferences(closesBlock(declared, head), evidence, repository)[0];
|
|
12735
|
+
if (row === void 0) return unverifiable2("no_comparable_record_field");
|
|
12736
|
+
return verdictOf(row.verdict, row.observed ?? null, row.reason);
|
|
12737
|
+
}
|
|
12965
12738
|
const missing = sectionReason(type, evidence);
|
|
12966
12739
|
if (missing !== void 0) return unverifiable2(missing);
|
|
12967
12740
|
const entries = lookup(type, evidence);
|
|
@@ -12969,13 +12742,15 @@ function checkReaderClaim(claim2, evidence, repository) {
|
|
|
12969
12742
|
if (typed.some((c) => entries.get(c.id) === void 0)) {
|
|
12970
12743
|
return unverifiable2("candidate_not_in_evidence");
|
|
12971
12744
|
}
|
|
12972
|
-
const
|
|
12973
|
-
const relevant = typed.filter((c) => c.score === null || c.score >= floor);
|
|
12745
|
+
const relevant = combinedCandidates(typed, claim2.retrieval.floor);
|
|
12974
12746
|
const observed = relevant.map((c) => entries.get(c.id));
|
|
12975
12747
|
const truncated = type === "file" && evidence.files?.status === "ok" && !evidence.files.complete;
|
|
12976
12748
|
if (relevant.length === 0) {
|
|
12977
12749
|
return unverifiable2(truncated ? "file_list_truncated" : "no_matching_record_item");
|
|
12978
12750
|
}
|
|
12751
|
+
if (wantsBoolean && ambiguousCandidates(relevant, (c) => outcomeOf(type, entries.get(c.id)))) {
|
|
12752
|
+
return unverifiable2("ambiguous_candidates");
|
|
12753
|
+
}
|
|
12979
12754
|
switch (type) {
|
|
12980
12755
|
case "file": {
|
|
12981
12756
|
const files = observed;
|
|
@@ -13237,7 +13012,8 @@ function advisoryLine(section) {
|
|
|
13237
13012
|
}
|
|
13238
13013
|
var RECORD_SHOWS = {
|
|
13239
13014
|
declared_not_changed: "not among the changed files",
|
|
13240
|
-
|
|
13015
|
+
// Spec 0.1.4: the closing references, and every ClosedEvent of the target, were read.
|
|
13016
|
+
not_closing: "not closed by this pull request or its commits",
|
|
13241
13017
|
not_found: "not found in the record",
|
|
13242
13018
|
not_reachable: "not reachable from the head",
|
|
13243
13019
|
head_mismatch: "the head is another commit",
|