@withtruing/companion 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +24 -25
  2. package/dist/anthropic-output-schema.js +41 -0
  3. package/dist/api-key-caller.js +9 -2
  4. package/dist/claude-cli-caller.js +107 -8
  5. package/dist/cli.js +836 -206
  6. package/dist/codex-cli-caller.js +85 -14
  7. package/dist/config.js +177 -2
  8. package/dist/git-range.js +88 -46
  9. package/dist/http-client.js +95 -10
  10. package/dist/investigation-reader.js +12 -6
  11. package/dist/managed-repo.js +132 -0
  12. package/dist/observed-sources.js +132 -6
  13. package/dist/reader-result-contract.js +296 -2
  14. package/dist/repository-provider.js +608 -0
  15. package/dist/schema-entities.js +18 -18
  16. package/dist/service.js +7 -2
  17. package/dist/surface-mapper.js +53 -11
  18. package/dist/text-model.js +91 -8
  19. package/dist/whole-read/archive.js +196 -0
  20. package/dist/whole-read/checks.js +297 -0
  21. package/dist/whole-read/claims.js +196 -0
  22. package/dist/whole-read/coverage-ledger.js +51 -0
  23. package/dist/whole-read/decode.js +234 -0
  24. package/dist/whole-read/drafting.js +502 -0
  25. package/dist/whole-read/generated-rules.js +68 -0
  26. package/dist/whole-read/go-proof.js +255 -0
  27. package/dist/whole-read/grouping-prompt.js +19 -0
  28. package/dist/whole-read/grouping.js +247 -0
  29. package/dist/whole-read/identity.js +103 -0
  30. package/dist/whole-read/import-graph.js +293 -0
  31. package/dist/whole-read/inventory-collector.js +55 -0
  32. package/dist/whole-read/inventory-rules.js +141 -0
  33. package/dist/whole-read/lockfiles.js +947 -0
  34. package/dist/whole-read/notes.js +216 -0
  35. package/dist/whole-read/npm-proof.js +272 -0
  36. package/dist/whole-read/owner-decision-detectors.js +270 -0
  37. package/dist/whole-read/packing.js +612 -0
  38. package/dist/whole-read/pages.js +429 -0
  39. package/dist/whole-read/phase-policy.js +54 -0
  40. package/dist/whole-read/prd-build.js +279 -0
  41. package/dist/whole-read/prd-fidelity.js +14 -0
  42. package/dist/whole-read/python-proof.js +489 -0
  43. package/dist/whole-read/read-prompt.js +67 -0
  44. package/dist/whole-read/redaction.js +411 -0
  45. package/dist/whole-read/relationship-mapping.js +98 -0
  46. package/dist/whole-read/sanitize.js +266 -0
  47. package/dist/whole-read/secret-rules.js +26 -0
  48. package/dist/whole-read/structure.js +300 -0
  49. package/dist/whole-read/types.js +2 -0
  50. package/dist/whole-read-claims.js +12 -0
  51. package/dist/whole-read-git.js +340 -0
  52. package/dist/whole-read-pool.js +80 -0
  53. package/dist/whole-read-runner.js +945 -0
  54. package/dist/whole-read-shared-source.js +194 -0
  55. package/dist/whole-read-temp.js +479 -0
  56. package/package.json +8 -2
@@ -1,6 +1,32 @@
1
+ const PRD_FIDELITY_POLICY_VERSION = 'prd-fidelity@1';
2
+ export const isPrdSource = (source) => 'kind' in source && source.kind === 'prd';
3
+ export const prdSourceIdentityKey = (source) => JSON.stringify([
4
+ source.prdVersionId,
5
+ source.textSha256,
6
+ source.parserVersion,
7
+ ]);
1
8
  const contributes = (assessment) => assessment.outcome === 'supported' || assessment.outcome === 'contradicted';
2
9
  const breadth = { element: 0, named_paths: 1, module_surface: 2 };
10
+ const byteRangeKey = (range) => `${range.startByte}:${range.endByte}`;
11
+ const scopedEvidenceKey = (evidence) => JSON.stringify([
12
+ evidence.evidenceId,
13
+ evidence.sourceEntryId,
14
+ evidence.connectionId,
15
+ evidence.blobId,
16
+ evidence.locator,
17
+ byteRangeKey(evidence.byteRangeRequested),
18
+ byteRangeKey(evidence.byteRangeReturned),
19
+ ]);
3
20
  export function scopeCovers(covering, declared) {
21
+ if (covering.document !== undefined || declared.document !== undefined) {
22
+ if (covering.document === undefined || declared.document === undefined)
23
+ return false;
24
+ if (covering.document.sourceEntryId !== declared.document.sourceEntryId
25
+ || covering.document.prdVersionId !== declared.document.prdVersionId
26
+ || JSON.stringify([...covering.document.sectionIds].sort())
27
+ !== JSON.stringify([...declared.document.sectionIds].sort()))
28
+ return false;
29
+ }
4
30
  const coveringHasSurface = covering.surfaceId !== undefined || covering.surfaceHash !== undefined;
5
31
  const declaredHasSurface = declared.surfaceId !== undefined || declared.surfaceHash !== undefined;
6
32
  if (coveringHasSurface || declaredHasSurface) {
@@ -13,6 +39,15 @@ export function scopeCovers(covering, declared) {
13
39
  if ((declared.operations ?? []).some((operation) => !coveredOperations.has(operation))) {
14
40
  return false;
15
41
  }
42
+ if (covering.evidence !== undefined || declared.evidence !== undefined) {
43
+ if (covering.evidence === undefined || declared.evidence === undefined)
44
+ return false;
45
+ const coveringEvidence = new Set(covering.evidence.map(scopedEvidenceKey));
46
+ const declaredEvidence = new Set(declared.evidence.map(scopedEvidenceKey));
47
+ if (coveringEvidence.size !== declaredEvidence.size
48
+ || [...declaredEvidence].some((entry) => !coveringEvidence.has(entry)))
49
+ return false;
50
+ }
16
51
  if (declared.breadth !== 'named_paths' || covering.breadth !== 'named_paths')
17
52
  return true;
18
53
  const covered = new Set(covering.paths || []);
@@ -26,7 +61,7 @@ const inventoryAccess = {
26
61
  };
27
62
  function evidenceRepositoryPath(evidence, sourceEntries) {
28
63
  const source = sourceEntries.find((entry) => entry.sourceEntryId === evidence.sourceEntryId);
29
- if (!source)
64
+ if (!source || isPrdSource(source))
30
65
  return null;
31
66
  const prefix = `repo:${source.connectionId}@${source.commit}:`;
32
67
  return evidence.locator.startsWith(prefix) ? evidence.locator.slice(prefix.length) : null;
@@ -68,11 +103,109 @@ function parseRange(value) {
68
103
  const end = Number(match[2]);
69
104
  return start <= end ? [start, end] : null;
70
105
  }
106
+ function validByteRange(value) {
107
+ return Boolean(value
108
+ && Number.isSafeInteger(value.startByte)
109
+ && Number.isSafeInteger(value.endByte)
110
+ && value.startByte >= 0
111
+ && value.startByte < value.endByte);
112
+ }
113
+ function validDeliveryMap(segments, returned) {
114
+ if (!segments?.length || !segments.every((segment) => 'canonicalStartByte' in segment) || segments[0]?.deliveredStart !== 0
115
+ || segments[0]?.canonicalStartByte !== returned.startByte
116
+ || segments.at(-1)?.canonicalEndByte !== returned.endByte)
117
+ return false;
118
+ let priorDeliveredEnd = 0;
119
+ let priorCanonicalEnd = returned.startByte;
120
+ for (const segment of segments) {
121
+ if (!Number.isSafeInteger(segment.deliveredStart)
122
+ || !Number.isSafeInteger(segment.deliveredEnd)
123
+ || !Number.isSafeInteger(segment.canonicalStartByte)
124
+ || !Number.isSafeInteger(segment.canonicalEndByte)
125
+ || segment.deliveredStart !== priorDeliveredEnd
126
+ || segment.deliveredStart >= segment.deliveredEnd
127
+ || segment.canonicalStartByte !== priorCanonicalEnd
128
+ || segment.canonicalStartByte >= segment.canonicalEndByte
129
+ || segment.canonicalEndByte > returned.endByte)
130
+ return false;
131
+ if (segment.kind === 'source') {
132
+ const offsets = segment.canonicalByteOffsets;
133
+ if (!offsets
134
+ || offsets.length !== segment.deliveredEnd - segment.deliveredStart + 1
135
+ || offsets[0] !== segment.canonicalStartByte
136
+ || offsets.at(-1) !== segment.canonicalEndByte
137
+ || offsets.some((offset, index) => index > 0 && offset <= offsets[index - 1]))
138
+ return false;
139
+ }
140
+ else if (!segment.ruleId)
141
+ return false;
142
+ priorDeliveredEnd = segment.deliveredEnd;
143
+ priorCanonicalEnd = segment.canonicalEndByte;
144
+ }
145
+ return true;
146
+ }
147
+ function validPrdDeliveryMap(segments, returned) {
148
+ if (!segments?.length || !segments.every((segment) => 'sourceStartChar' in segment) || segments[0]?.deliveredStart !== 0
149
+ || segments[0]?.sourceStartChar !== returned.startChar
150
+ || segments.at(-1)?.sourceEndChar !== returned.endChar)
151
+ return false;
152
+ let deliveredEnd = 0;
153
+ let sourceEnd = returned.startChar;
154
+ for (const segment of segments) {
155
+ if (!Number.isSafeInteger(segment.deliveredStart)
156
+ || !Number.isSafeInteger(segment.deliveredEnd)
157
+ || !Number.isSafeInteger(segment.sourceStartChar)
158
+ || !Number.isSafeInteger(segment.sourceEndChar)
159
+ || segment.deliveredStart !== deliveredEnd
160
+ || segment.deliveredStart >= segment.deliveredEnd
161
+ || segment.sourceStartChar !== sourceEnd
162
+ || segment.sourceStartChar >= segment.sourceEndChar
163
+ || segment.sourceEndChar > returned.endChar)
164
+ return false;
165
+ if (segment.kind === 'source') {
166
+ const offsets = segment.sourceCharOffsets;
167
+ if (!offsets
168
+ || offsets.length !== segment.deliveredEnd - segment.deliveredStart + 1
169
+ || offsets[0] !== segment.sourceStartChar
170
+ || offsets.at(-1) !== segment.sourceEndChar
171
+ || offsets.some((offset, index) => index > 0 && offset <= offsets[index - 1]))
172
+ return false;
173
+ }
174
+ else if (!segment.ruleId)
175
+ return false;
176
+ deliveredEnd = segment.deliveredEnd;
177
+ sourceEnd = segment.sourceEndChar;
178
+ }
179
+ return true;
180
+ }
181
+ const validPrdRange = (range) => Boolean(range
182
+ && range.prdVersionId.length > 0
183
+ && range.sectionId.length > 0
184
+ && Number.isSafeInteger(range.startChar)
185
+ && Number.isSafeInteger(range.endChar)
186
+ && range.startChar >= 0
187
+ && range.startChar < range.endChar);
188
+ const samePrdLocation = (left, right) => left.prdVersionId === right.prdVersionId && left.sectionId === right.sectionId;
189
+ const prdRangeWithin = (inner, outer) => samePrdLocation(inner, outer)
190
+ && inner.startChar >= outer.startChar && inner.endChar <= outer.endChar;
191
+ const sameByteRange = (left, right) => validByteRange(left) && validByteRange(right)
192
+ && left.startByte === right.startByte && left.endByte === right.endByte;
71
193
  export const result_binds_request = (result, context) => {
72
194
  if (result.investigationId !== context.request.investigationId)
73
195
  return `result claims ${result.investigationId}; the request is ${context.request.investigationId}`;
74
196
  if (result.attempt !== context.request.attempt)
75
197
  return `result claims attempt ${result.attempt}; the request issued attempt ${context.request.attempt}`;
198
+ if (context.request.policyVersion === 'build-citation-check@1'
199
+ && result.policyVersion !== context.request.policyVersion) {
200
+ return `build result claims policy ${result.policyVersion ?? '<missing>'}; the request issued ${context.request.policyVersion}`;
201
+ }
202
+ if (context.request.policyVersion === PRD_FIDELITY_POLICY_VERSION
203
+ && result.policyVersion !== context.request.policyVersion) {
204
+ return `PRD fidelity result claims policy ${result.policyVersion ?? '<missing>'}; the request issued ${context.request.policyVersion}`;
205
+ }
206
+ if (result.policyVersion !== undefined && result.policyVersion !== context.request.policyVersion) {
207
+ return `result claims policy ${result.policyVersion}; the request issued ${context.request.policyVersion}`;
208
+ }
76
209
  return null;
77
210
  };
78
211
  export const identifiers_unique = (result, context) => {
@@ -111,6 +244,7 @@ export const policy4_surface_complete = (result, context) => {
111
244
  return 'policy-4 result has no execution metadata';
112
245
  const requested = context.sourceEntry(surface.sourceEntryId);
113
246
  if (!requested
247
+ || isPrdSource(requested)
114
248
  || requested.role !== 'current'
115
249
  || requested.commit !== surface.commit) {
116
250
  return 'sealed surface source does not match the requested current source';
@@ -147,6 +281,88 @@ export const policy4_surface_complete = (result, context) => {
147
281
  }
148
282
  return null;
149
283
  };
284
+ export const prd_source_mismatch = (result, context) => {
285
+ for (const actual of result.observedSources) {
286
+ const expected = context.sourceEntry(actual.sourceEntryId);
287
+ if (!expected || (!isPrdSource(expected) && !isPrdSource(actual)))
288
+ continue;
289
+ if (!isPrdSource(expected) || !isPrdSource(actual)) {
290
+ return `${actual.sourceEntryId}: requested and observed source branches differ`;
291
+ }
292
+ if (actual.role !== expected.role
293
+ || prdSourceIdentityKey(actual) !== prdSourceIdentityKey(expected)) {
294
+ return `${actual.sourceEntryId}: observed document identity does not match the request`;
295
+ }
296
+ }
297
+ return null;
298
+ };
299
+ export const prd_evidence_not_code = (result, context) => {
300
+ for (const evidence of result.evidence) {
301
+ if (evidence.kind !== 'prd_excerpt')
302
+ continue;
303
+ const source = context.sourceEntry(evidence.sourceEntryId);
304
+ if (context.request.policyVersion !== PRD_FIDELITY_POLICY_VERSION || !source || !isPrdSource(source)) {
305
+ return `${evidence.evidenceId}: PRD evidence cannot establish repository conformance`;
306
+ }
307
+ }
308
+ return null;
309
+ };
310
+ export const prd_fidelity_scope = (result, context) => {
311
+ if (context.request.policyVersion !== PRD_FIDELITY_POLICY_VERSION)
312
+ return null;
313
+ if (context.request.requestedSources.length === 0
314
+ || context.request.requestedSources.some((source) => !isPrdSource(source))) {
315
+ return 'PRD fidelity must use document sources only';
316
+ }
317
+ for (const obligation of context.request.obligations) {
318
+ const document = obligation.scope.document;
319
+ const source = document ? context.sourceEntry(document.sourceEntryId) : undefined;
320
+ if (obligation.question !== 'describes_current' || !document || !source || !isPrdSource(source)
321
+ || source.prdVersionId !== document.prdVersionId || document.sectionIds.length === 0) {
322
+ return `${obligation.obligationId}: PRD fidelity is not explicitly scoped to document statements`;
323
+ }
324
+ }
325
+ const evidenceById = new Map(result.evidence.map((evidence) => [evidence.evidenceId, evidence]));
326
+ for (const evidence of result.evidence) {
327
+ const source = context.sourceEntry(evidence.sourceEntryId);
328
+ const requested = evidence.prdRangeRequested;
329
+ const returned = evidence.prdRangeReturned;
330
+ if (evidence.kind !== 'prd_excerpt' || !source || !isPrdSource(source)
331
+ || !validPrdRange(requested) || !validPrdRange(returned)
332
+ || requested.prdVersionId !== source.prdVersionId
333
+ || returned.prdVersionId !== source.prdVersionId
334
+ || !prdRangeWithin(requested, returned)
335
+ || evidence.delivery !== 'excerpt'
336
+ || evidence.access !== 'read_ok'
337
+ || evidence.complete !== 1
338
+ || !/^sha256:[0-9a-f]{64}$/.test(evidence.originalSpanSha256 ?? '')
339
+ || !/^sha256:[0-9a-f]{64}$/.test(evidence.contentHash ?? '')
340
+ || evidence.serverReceivedContent !== 1
341
+ || evidence.localRetention !== 'workspace_cache'
342
+ || !validPrdDeliveryMap(evidence.deliveryMap, returned)) {
343
+ return `${evidence.evidenceId}: invalid PRD excerpt delivery metadata`;
344
+ }
345
+ }
346
+ for (const assessment of result.assessments) {
347
+ const obligation = context.obligation(assessment.obligationId);
348
+ if (!obligation || assessment.question !== 'describes_current'
349
+ || !scopeCovers(assessment.scope, obligation.scope)
350
+ || !scopeCovers(obligation.scope, assessment.scope)) {
351
+ return `${assessment.obligationId}: fidelity must answer describes_current at the issued document scope`;
352
+ }
353
+ const sections = new Set(obligation.scope.document?.sectionIds ?? []);
354
+ for (const citation of assessment.citations) {
355
+ const evidence = evidenceById.get(citation.evidenceId);
356
+ const cited = citation.citedPrdRange;
357
+ if (!evidence || !validPrdRange(cited) || !validPrdRange(evidence.prdRangeReturned)
358
+ || !prdRangeWithin(cited, evidence.prdRangeReturned)
359
+ || !sections.has(cited.sectionId)) {
360
+ return `${citation.evidenceId}: invalid PRD citation coordinates`;
361
+ }
362
+ }
363
+ }
364
+ return null;
365
+ };
150
366
  export const evidence_source_entry_requested = (result, context) => {
151
367
  const entries = new Set(context.request.requestedSources.map((source) => source.sourceEntryId));
152
368
  for (const evidence of result.evidence) {
@@ -236,6 +452,13 @@ export const observed_sources_satisfy_obligation = (result, context) => {
236
452
  continue;
237
453
  if (actual.role !== expected.role)
238
454
  return `${entryId}: requested role '${expected.role}', observed '${actual.role}'`;
455
+ if (isPrdSource(expected) || isPrdSource(actual)) {
456
+ if (!isPrdSource(expected) || !isPrdSource(actual)
457
+ || prdSourceIdentityKey(actual) !== prdSourceIdentityKey(expected)) {
458
+ return `${entryId}: observed document identity does not match the request`;
459
+ }
460
+ continue;
461
+ }
239
462
  if (actual.connectionId !== expected.connectionId)
240
463
  return `${entryId}: requested repository '${expected.connectionId}', observed '${actual.connectionId}'`;
241
464
  if (actual.sourceIdentity !== `commit:${expected.commit}`)
@@ -307,7 +530,57 @@ export const changed_since_two_roles = (result, context) => {
307
530
  }
308
531
  return null;
309
532
  };
310
- export const cited_range_within_returned = (result) => {
533
+ export const build_citation_scope_matches = (result, context) => {
534
+ if (context.request.policyVersion !== 'build-citation-check@1')
535
+ return null;
536
+ const evidenceById = new Map(result.evidence.map((evidence) => [evidence.evidenceId, evidence]));
537
+ for (const obligation of context.request.obligations) {
538
+ if (obligation.question !== 'describes_current' || !obligation.scope.evidence?.length) {
539
+ return `${obligation.obligationId} has no named build evidence scope`;
540
+ }
541
+ const allowed = new Set(obligation.requiredSourceEntryIds);
542
+ for (const scoped of obligation.scope.evidence) {
543
+ const source = context.sourceEntry(scoped.sourceEntryId);
544
+ if (!allowed.has(scoped.sourceEntryId) || !source
545
+ || source.connectionId !== scoped.connectionId) {
546
+ return `${obligation.obligationId} scopes ${scoped.evidenceId} to an unbound repository connection`;
547
+ }
548
+ if (!validByteRange(scoped.byteRangeRequested)
549
+ || !validByteRange(scoped.byteRangeReturned)
550
+ || scoped.byteRangeRequested.startByte < scoped.byteRangeReturned.startByte
551
+ || scoped.byteRangeRequested.endByte > scoped.byteRangeReturned.endByte) {
552
+ return `${obligation.obligationId} has an invalid delivered span contract for ${scoped.evidenceId}`;
553
+ }
554
+ }
555
+ }
556
+ for (const assessment of result.assessments) {
557
+ const obligation = context.obligation(assessment.obligationId);
558
+ if (!obligation)
559
+ continue;
560
+ if (!scopeCovers(assessment.scope, obligation.scope)
561
+ || !scopeCovers(obligation.scope, assessment.scope)) {
562
+ return `${assessment.obligationId} does not preserve its named build evidence scope`;
563
+ }
564
+ for (const scoped of obligation.scope.evidence ?? []) {
565
+ const evidence = evidenceById.get(scoped.evidenceId);
566
+ if (!evidence
567
+ || evidence.sourceEntryId !== scoped.sourceEntryId
568
+ || evidence.locator !== scoped.locator
569
+ || evidence.blobId !== scoped.blobId
570
+ || !sameByteRange(evidence.byteRangeRequested, scoped.byteRangeRequested)
571
+ || !sameByteRange(evidence.byteRangeReturned, scoped.byteRangeReturned)
572
+ || !/^sha256:[0-9a-f]{64}$/.test(evidence.originalSpanSha256 ?? '')
573
+ || !/^sha256:[0-9a-f]{64}$/.test(evidence.contentHash ?? '')
574
+ || evidence.delivery !== 'excerpt'
575
+ || evidence.localRetention !== 'none'
576
+ || !validDeliveryMap(evidence.deliveryMap, scoped.byteRangeReturned)) {
577
+ return `${assessment.obligationId} did not deliver the sealed metadata for ${scoped.evidenceId}`;
578
+ }
579
+ }
580
+ }
581
+ return null;
582
+ };
583
+ export const cited_range_within_returned = (result, context) => {
311
584
  const evidenceById = new Map(result.evidence.map((evidence) => [evidence.evidenceId, evidence]));
312
585
  for (const assessment of result.assessments) {
313
586
  if (!contributes(assessment))
@@ -316,6 +589,19 @@ export const cited_range_within_returned = (result) => {
316
589
  const evidence = evidenceById.get(citation.evidenceId);
317
590
  if (!evidence)
318
591
  continue;
592
+ if (context.request.policyVersion === 'build-citation-check@1') {
593
+ if (!validByteRange(evidence.byteRangeReturned)) {
594
+ return `${citation.evidenceId} has no usable canonical byteRangeReturned`;
595
+ }
596
+ if (!validByteRange(citation.citedByteRange)) {
597
+ return `${citation.evidenceId} has no usable citedByteRange`;
598
+ }
599
+ if (citation.citedByteRange.startByte < evidence.byteRangeReturned.startByte
600
+ || citation.citedByteRange.endByte > evidence.byteRangeReturned.endByte) {
601
+ return `cited bytes [${citation.citedByteRange.startByte},${citation.citedByteRange.endByte}) lie outside delivered bytes [${evidence.byteRangeReturned.startByte},${evidence.byteRangeReturned.endByte})`;
602
+ }
603
+ continue;
604
+ }
319
605
  if (evidence.access !== 'truncated') {
320
606
  if (citation.citedRange !== null
321
607
  && citation.citedRange !== undefined
@@ -418,11 +704,15 @@ export const resultCheckNames = [
418
704
  'assessment_question_matches',
419
705
  'assessment_scope_covers',
420
706
  'policy4_surface_complete',
707
+ 'prd_source_mismatch',
708
+ 'prd_evidence_not_code',
709
+ 'prd_fidelity_scope',
421
710
  'observed_sources_satisfy_obligation',
422
711
  'citation_source_allowed_by_obligation',
423
712
  'citation_evidence_delivered',
424
713
  'support_citation_usable',
425
714
  'changed_since_two_roles',
715
+ 'build_citation_scope_matches',
426
716
  'cited_range_within_returned',
427
717
  'automatic_claim_shape_required',
428
718
  'automatic_claim_shape_abstains',
@@ -437,11 +727,15 @@ export const resultChecks = {
437
727
  assessment_question_matches,
438
728
  assessment_scope_covers,
439
729
  policy4_surface_complete,
730
+ prd_source_mismatch,
731
+ prd_evidence_not_code,
732
+ prd_fidelity_scope,
440
733
  observed_sources_satisfy_obligation,
441
734
  citation_source_allowed_by_obligation,
442
735
  citation_evidence_delivered,
443
736
  support_citation_usable,
444
737
  changed_since_two_roles,
738
+ build_citation_scope_matches,
445
739
  cited_range_within_returned,
446
740
  automatic_claim_shape_required,
447
741
  automatic_claim_shape_abstains,