@withtruing/companion 0.13.2 → 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.
- package/README.md +25 -26
- package/dist/anthropic-output-schema.js +41 -0
- package/dist/api-key-caller.js +9 -2
- package/dist/claude-cli-caller.js +107 -8
- package/dist/cli.js +837 -207
- package/dist/codex-binary.js +96 -0
- package/dist/codex-cli-caller.js +90 -16
- package/dist/config.js +177 -2
- package/dist/git-range.js +88 -46
- package/dist/http-client.js +95 -10
- package/dist/investigation-reader.js +12 -6
- package/dist/managed-repo.js +132 -0
- package/dist/observed-sources.js +132 -6
- package/dist/reader-result-contract.js +296 -2
- package/dist/repository-provider.js +608 -0
- package/dist/schema-entities.js +18 -18
- package/dist/service.js +7 -2
- package/dist/surface-mapper.js +53 -11
- package/dist/text-model.js +91 -8
- package/dist/whole-read/archive.js +196 -0
- package/dist/whole-read/checks.js +297 -0
- package/dist/whole-read/claims.js +196 -0
- package/dist/whole-read/coverage-ledger.js +51 -0
- package/dist/whole-read/decode.js +234 -0
- package/dist/whole-read/drafting.js +502 -0
- package/dist/whole-read/generated-rules.js +68 -0
- package/dist/whole-read/go-proof.js +255 -0
- package/dist/whole-read/grouping-prompt.js +19 -0
- package/dist/whole-read/grouping.js +247 -0
- package/dist/whole-read/identity.js +103 -0
- package/dist/whole-read/import-graph.js +293 -0
- package/dist/whole-read/inventory-collector.js +55 -0
- package/dist/whole-read/inventory-rules.js +141 -0
- package/dist/whole-read/lockfiles.js +947 -0
- package/dist/whole-read/notes.js +216 -0
- package/dist/whole-read/npm-proof.js +272 -0
- package/dist/whole-read/owner-decision-detectors.js +270 -0
- package/dist/whole-read/packing.js +612 -0
- package/dist/whole-read/pages.js +429 -0
- package/dist/whole-read/phase-policy.js +54 -0
- package/dist/whole-read/prd-build.js +279 -0
- package/dist/whole-read/prd-fidelity.js +14 -0
- package/dist/whole-read/python-proof.js +489 -0
- package/dist/whole-read/read-prompt.js +67 -0
- package/dist/whole-read/redaction.js +411 -0
- package/dist/whole-read/relationship-mapping.js +98 -0
- package/dist/whole-read/sanitize.js +266 -0
- package/dist/whole-read/secret-rules.js +26 -0
- package/dist/whole-read/structure.js +300 -0
- package/dist/whole-read/types.js +2 -0
- package/dist/whole-read-claims.js +12 -0
- package/dist/whole-read-git.js +340 -0
- package/dist/whole-read-pool.js +80 -0
- package/dist/whole-read-runner.js +945 -0
- package/dist/whole-read-shared-source.js +194 -0
- package/dist/whole-read-temp.js +479 -0
- package/package.json +8 -2
package/dist/observed-sources.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { createSandbox } from './sandbox.js';
|
|
2
|
+
import { RepositoryProviderError, } from './repository-provider.js';
|
|
3
|
+
const isPrdSource = (source) => source.kind === 'prd';
|
|
4
|
+
function unavailableSource(source, reason) {
|
|
5
|
+
return {
|
|
6
|
+
sourceEntryId: source.sourceEntryId,
|
|
7
|
+
role: source.role,
|
|
8
|
+
connectionId: source.connectionId,
|
|
9
|
+
sourceIdentity: `unavailable:${reason}`,
|
|
10
|
+
obtainedBy: 'unavailable',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
2
13
|
/** Resolve every immutable identity before exposing any worktree for reading. This prevents an
|
|
3
14
|
* earlier source from being inspected before a later required source proves unavailable. */
|
|
4
|
-
export async function obtainRequestedSources(request, connections) {
|
|
15
|
+
export async function obtainRequestedSources(request, connections, acquisition) {
|
|
5
16
|
const sourceEntryIds = new Set();
|
|
6
17
|
for (const source of request.requestedSources) {
|
|
7
18
|
if (sourceEntryIds.has(source.sourceEntryId)) {
|
|
@@ -11,13 +22,123 @@ export async function obtainRequestedSources(request, connections) {
|
|
|
11
22
|
}
|
|
12
23
|
const observedSources = [];
|
|
13
24
|
const sandboxes = new Map();
|
|
25
|
+
const leases = new Map();
|
|
26
|
+
const leasesByIdentity = new Map();
|
|
27
|
+
const verifiedSources = new Set();
|
|
14
28
|
const cleanup = async () => {
|
|
15
|
-
const pending =
|
|
29
|
+
const pending = acquisition
|
|
30
|
+
? [...new Set(leases.values())].map((lease) => () => lease.release().then(() => undefined))
|
|
31
|
+
: [...sandboxes.values()].map((sandbox) => () => sandbox.cleanup());
|
|
16
32
|
sandboxes.clear();
|
|
17
|
-
|
|
33
|
+
leases.clear();
|
|
34
|
+
leasesByIdentity.clear();
|
|
35
|
+
const results = await Promise.allSettled(pending.map((release) => release()));
|
|
36
|
+
const failure = results.find((result) => result.status === 'rejected');
|
|
37
|
+
if (failure)
|
|
38
|
+
throw failure.reason;
|
|
18
39
|
};
|
|
40
|
+
if (acquisition) {
|
|
41
|
+
let failureCode = null;
|
|
42
|
+
try {
|
|
43
|
+
await acquisition.repositoryProvider.sweepRepositories();
|
|
44
|
+
for (const source of request.requestedSources) {
|
|
45
|
+
if (isPrdSource(source))
|
|
46
|
+
continue;
|
|
47
|
+
const connection = connections.find((candidate) => candidate.connectionId === source.connectionId);
|
|
48
|
+
if (!connection) {
|
|
49
|
+
failureCode = 'source_identity_unavailable';
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const identityKey = JSON.stringify([source.connectionId, source.commit]);
|
|
54
|
+
const lease = leasesByIdentity.get(identityKey)
|
|
55
|
+
?? await acquisition.repositoryProvider.acquireRepository({
|
|
56
|
+
connection,
|
|
57
|
+
target: { kind: 'snapshot', sha: source.commit },
|
|
58
|
+
owner: acquisition.owner,
|
|
59
|
+
signal: acquisition.signal,
|
|
60
|
+
});
|
|
61
|
+
if (lease.identities.connectionId !== source.connectionId
|
|
62
|
+
|| lease.identities.source !== `commit:${source.commit}`
|
|
63
|
+
|| lease.identities.target.kind !== 'snapshot'
|
|
64
|
+
|| lease.identities.target.sha !== source.commit) {
|
|
65
|
+
await lease.release();
|
|
66
|
+
throw new RepositoryProviderError('snapshot_unavailable');
|
|
67
|
+
}
|
|
68
|
+
leasesByIdentity.set(identityKey, lease);
|
|
69
|
+
leases.set(source.sourceEntryId, lease);
|
|
70
|
+
verifiedSources.add(source.sourceEntryId);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
failureCode = error instanceof RepositoryProviderError ? error.code : 'snapshot_unavailable';
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (failureCode === null) {
|
|
78
|
+
for (const source of request.requestedSources) {
|
|
79
|
+
if (isPrdSource(source))
|
|
80
|
+
continue;
|
|
81
|
+
const lease = leases.get(source.sourceEntryId);
|
|
82
|
+
try {
|
|
83
|
+
const checkout = await lease.checkout();
|
|
84
|
+
sandboxes.set(source.sourceEntryId, {
|
|
85
|
+
target: 'requested_commit',
|
|
86
|
+
obtainedBy: 'sandbox_worktree',
|
|
87
|
+
path: checkout.path,
|
|
88
|
+
resolvedCommit: checkout.commit,
|
|
89
|
+
sourceIdentity: `commit:${checkout.commit}`,
|
|
90
|
+
cleanup: () => lease.release().then(() => undefined),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
failureCode = error instanceof RepositoryProviderError ? error.code : 'snapshot_unavailable';
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (failureCode !== null)
|
|
100
|
+
await cleanup();
|
|
101
|
+
for (const source of request.requestedSources) {
|
|
102
|
+
if (isPrdSource(source)) {
|
|
103
|
+
// B31 document identities are already immutable and use their own content delivery path.
|
|
104
|
+
observedSources.push({ ...source });
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const sandbox = sandboxes.get(source.sourceEntryId);
|
|
108
|
+
observedSources.push(sandbox || verifiedSources.has(source.sourceEntryId)
|
|
109
|
+
? {
|
|
110
|
+
sourceEntryId: source.sourceEntryId,
|
|
111
|
+
role: source.role,
|
|
112
|
+
connectionId: source.connectionId,
|
|
113
|
+
sourceIdentity: sandbox?.sourceIdentity ?? `commit:${source.commit}`,
|
|
114
|
+
obtainedBy: 'sandbox_worktree',
|
|
115
|
+
}
|
|
116
|
+
: unavailableSource(source, failureCode ?? 'source_identity_unavailable'));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
await cleanup();
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
observedSources,
|
|
125
|
+
sandboxes,
|
|
126
|
+
unavailable: failureCode !== null,
|
|
127
|
+
assertAvailable() {
|
|
128
|
+
if (acquisition.signal.aborted)
|
|
129
|
+
throw new RepositoryProviderError('repository_lease_lost');
|
|
130
|
+
for (const lease of new Set(leases.values()))
|
|
131
|
+
lease.assertLive();
|
|
132
|
+
},
|
|
133
|
+
cleanup,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
19
136
|
try {
|
|
20
137
|
for (const source of request.requestedSources) {
|
|
138
|
+
if (isPrdSource(source)) {
|
|
139
|
+
observedSources.push({ ...source });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
21
142
|
const connection = connections.find((candidate) => candidate.connectionId === source.connectionId);
|
|
22
143
|
if (!connection) {
|
|
23
144
|
observedSources.push({
|
|
@@ -59,22 +180,27 @@ export async function obtainRequestedSources(request, connections) {
|
|
|
59
180
|
return {
|
|
60
181
|
observedSources,
|
|
61
182
|
sandboxes,
|
|
62
|
-
unavailable: observedSources.some((source) => source.obtainedBy === 'unavailable'),
|
|
183
|
+
unavailable: observedSources.some((source) => 'obtainedBy' in source && source.obtainedBy === 'unavailable'),
|
|
184
|
+
assertAvailable() { },
|
|
63
185
|
cleanup,
|
|
64
186
|
};
|
|
65
187
|
}
|
|
66
188
|
/** Obtain each requested immutable source, optionally inspect it while its worktree is alive, and
|
|
67
189
|
* return the observed-source half of a reader result. Every created worktree is cleaned in a
|
|
68
190
|
* finally block, including when the inspection callback crashes. */
|
|
69
|
-
export async function buildObservedSources(request, connections, inspectSource = async () => { }) {
|
|
70
|
-
const obtained = await obtainRequestedSources(request, connections);
|
|
191
|
+
export async function buildObservedSources(request, connections, inspectSource = async () => { }, acquisition) {
|
|
192
|
+
const obtained = await obtainRequestedSources(request, connections, acquisition);
|
|
71
193
|
try {
|
|
72
194
|
if (!obtained.unavailable) {
|
|
195
|
+
obtained.assertAvailable();
|
|
73
196
|
for (const source of request.requestedSources) {
|
|
197
|
+
if (isPrdSource(source))
|
|
198
|
+
continue;
|
|
74
199
|
const sandbox = obtained.sandboxes.get(source.sourceEntryId);
|
|
75
200
|
if (!sandbox)
|
|
76
201
|
throw new Error(`No sandbox obtained for ${source.sourceEntryId}`);
|
|
77
202
|
await inspectSource(source, sandbox);
|
|
203
|
+
obtained.assertAvailable();
|
|
78
204
|
}
|
|
79
205
|
}
|
|
80
206
|
return obtained.observedSources;
|
|
@@ -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
|
|
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,
|