@kontourai/survey 1.3.0 → 1.5.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 +1 -1
- package/dist/example-data/corrected-document-candidates.js +2 -2
- package/dist/example-data/public-directory-review-resource.d.ts +2 -2
- package/dist/example-data/public-directory-review-resource.js +2 -2
- package/dist/example-data/public-field-review.js +2 -2
- package/dist/example-data/regulated-document-review-resource.d.ts +2 -2
- package/dist/example-data/regulated-document-review-resource.js +2 -2
- package/dist/examples/public-field-observation.js +1 -1
- package/dist/examples/review-workbench/downstream-public-directory-adapter.js +1 -1
- package/dist/src/agent-utterance.d.ts +25 -0
- package/dist/src/agent-utterance.js +207 -95
- package/dist/src/console/review-console-server.js +3 -7
- package/dist/src/field-observation.d.ts +2 -16
- package/dist/src/field-observation.js +2 -14
- package/dist/src/index.d.ts +1 -1
- package/dist/src/inquiry-mapping.d.ts +2 -46
- package/dist/src/inquiry-mapping.js +37 -39
- package/dist/src/mcp/review-mcp.js +12 -12
- package/dist/src/observation-helper.d.ts +47 -1
- package/dist/src/observation-helper.js +41 -2
- package/dist/src/oversight-metrics.d.ts +2 -2
- package/dist/src/oversight-metrics.js +1 -1
- package/dist/src/producer-discipline.d.ts +40 -0
- package/dist/src/producer-discipline.js +13 -0
- package/dist/src/producer-profile.d.ts +134 -0
- package/dist/src/producer-profile.js +124 -0
- package/dist/src/raw-source.d.ts +18 -0
- package/dist/src/raw-source.js +28 -13
- package/dist/src/repeated-observation.d.ts +2 -16
- package/dist/src/repeated-observation.js +2 -14
- package/dist/src/review-proof.d.ts +2 -2
- package/dist/src/review-proof.js +2 -2
- package/dist/src/review-resource.d.ts +5 -1
- package/dist/src/review-workbench/review-workbench-data.d.ts +10 -10
- package/dist/src/review-workbench/review-workbench-data.js +6 -6
- package/dist/src/review-workbench/review-workbench.js +1 -1
- package/dist/src/review-workbench/server-review-session.d.ts +14 -1
- package/dist/src/review-workbench/server-review-session.js +16 -0
- package/dist/src/schema-mapping.js +32 -38
- package/dist/src/source-of-authority-observation.js +6 -9
- package/dist/src/to-surface.js +8 -11
- package/dist/src/types.d.ts +6 -1
- package/dist/src/vocabulary.d.ts +46 -4
- package/dist/src/vocabulary.js +29 -3
- package/package.json +6 -3
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Producer Profile core — ADR 0003 §4, CONTEXT.md "Producer Profile".
|
|
3
|
+
*
|
|
4
|
+
* This module carries the shared scaffolding every Producer Profile
|
|
5
|
+
* (inquiry-mapping, schema-mapping, and — from Slice 4 — agent-utterance)
|
|
6
|
+
* needs to turn its own proposals into Survey's existing Candidate/Candidate
|
|
7
|
+
* Set records: a generic proposal -> Candidate Set projection grouped by
|
|
8
|
+
* target, the Candidate Conflict rule, and one canonical `Candidate.metadata`
|
|
9
|
+
* key with a typed accessor, replacing each profile's hand-rolled projection,
|
|
10
|
+
* conflict check, and `as`-cast metadata round-trip.
|
|
11
|
+
*
|
|
12
|
+
* This is a module-internal seam: its exports are consumed directly by
|
|
13
|
+
* profile modules via relative import and are NOT re-exported from
|
|
14
|
+
* `src/index.ts`.
|
|
15
|
+
*
|
|
16
|
+
* Hard constraint (ADR 0003 §4): this module never decides a review outcome
|
|
17
|
+
* or a claim status. It only shapes proposal-backed Candidate/Candidate Set
|
|
18
|
+
* records — every profile still routes its output through Survey's existing
|
|
19
|
+
* review -> claim machinery unchanged.
|
|
20
|
+
*/
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Canonical proposal-metadata key
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* The one canonical `Candidate.metadata` key every Producer Profile uses to
|
|
26
|
+
* carry its profile-specific proposal payload. Replaces the per-profile keys
|
|
27
|
+
* (`mappingProposal`, `schemaMappingProposal`) each profile used before
|
|
28
|
+
* adopting this core module.
|
|
29
|
+
*/
|
|
30
|
+
export const PRODUCER_PROPOSAL_METADATA_KEY = "producerProposal";
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Candidate Conflict rule
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* The shared Candidate Conflict rule: a group of proposals conflicts iff it
|
|
36
|
+
* carries more than one distinct `equivalenceKey`. A group of 0 or 1
|
|
37
|
+
* proposals can never conflict.
|
|
38
|
+
*/
|
|
39
|
+
export function hasCandidateConflict(proposals) {
|
|
40
|
+
return new Set(proposals.map((p) => p.equivalenceKey)).size > 1;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build one Candidate Set (and its Candidates) from one target's proposal
|
|
44
|
+
* group. Grouping proposals by target itself stays a caller concern — this
|
|
45
|
+
* function projects exactly one group per call; it never reaches across
|
|
46
|
+
* multiple targets on its own. `status` is `"conflict"` when
|
|
47
|
+
* {@link hasCandidateConflict} is true for `proposals`, otherwise
|
|
48
|
+
* `"needs-review"` (an empty `proposals` array yields `"needs-review"` with
|
|
49
|
+
* an empty `candidates` array). `selectedCandidateId` is left unset — both
|
|
50
|
+
* profiles compute it themselves, or not at all, per their own review flow.
|
|
51
|
+
*/
|
|
52
|
+
export function projectProposalsToCandidateSet(target, proposals, options) {
|
|
53
|
+
const candidates = proposals.map((proposal) => ({
|
|
54
|
+
id: proposal.candidateId,
|
|
55
|
+
extractionId: proposal.extractionId,
|
|
56
|
+
value: proposal.value,
|
|
57
|
+
confidence: proposal.confidence,
|
|
58
|
+
metadata: {
|
|
59
|
+
[PRODUCER_PROPOSAL_METADATA_KEY]: proposal.metadata,
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
const status = hasCandidateConflict(proposals) ? "conflict" : "needs-review";
|
|
63
|
+
const candidateSet = {
|
|
64
|
+
id: options.candidateSetId,
|
|
65
|
+
target,
|
|
66
|
+
candidates,
|
|
67
|
+
status,
|
|
68
|
+
rationale: options.candidateSetRationale?.(status, proposals),
|
|
69
|
+
metadata: options.candidateSetMetadata,
|
|
70
|
+
};
|
|
71
|
+
return { candidateSet, candidates };
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Typed proposal-metadata accessor
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Typed read-back of the proposal payload a Candidate carries under
|
|
78
|
+
* {@link PRODUCER_PROPOSAL_METADATA_KEY}. Returns `undefined` if the
|
|
79
|
+
* Candidate, its `metadata`, or the key itself is absent — never throws.
|
|
80
|
+
*
|
|
81
|
+
* No fallback reads of any legacy per-profile metadata key are performed
|
|
82
|
+
* (Owner decision: no legacy support).
|
|
83
|
+
*/
|
|
84
|
+
export function getProducerProposal(candidate) {
|
|
85
|
+
return candidate?.metadata?.[PRODUCER_PROPOSAL_METADATA_KEY];
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Shared auto-accept primitives
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
/**
|
|
91
|
+
* Actor identity every Producer Profile's auto-accept policy uses when it
|
|
92
|
+
* accepts a proposal without human review. Shared literal — see ADR 0003
|
|
93
|
+
* §4 (the core never decides "verified"; auto-accept only ever produces
|
|
94
|
+
* "assumed" + comfort-zone true).
|
|
95
|
+
*/
|
|
96
|
+
export const AUTO_ACCEPT_ACTOR = "auto-accept-policy";
|
|
97
|
+
/**
|
|
98
|
+
* The comfort-zone posture every Producer Profile's auto-accept policy
|
|
99
|
+
* sets when it accepts a proposal: `withinComfortZone: true` always — an
|
|
100
|
+
* auto-accepted proposal is, by definition, one the policy's declared
|
|
101
|
+
* threshold covers, so there is nothing "outside comfort zone" about an
|
|
102
|
+
* auto-accept decision (ADR 0003 §4).
|
|
103
|
+
*/
|
|
104
|
+
export const AUTO_ACCEPT_WITHIN_COMFORT_ZONE = true;
|
|
105
|
+
/**
|
|
106
|
+
* The one auto-accept threshold rule every Producer Profile applies: a
|
|
107
|
+
* confidence value clears an auto-accept policy iff it is at or above
|
|
108
|
+
* (inclusive) the policy's minimum confidence. This is the only piece of
|
|
109
|
+
* auto-accept *mechanics* that is identical across profiles today — each
|
|
110
|
+
* profile decides its own iteration granularity (per-proposal filter vs.
|
|
111
|
+
* per-group max-confidence gate) and output record shape around this call;
|
|
112
|
+
* the core does not decide that.
|
|
113
|
+
*
|
|
114
|
+
* These three exports are the ONLY auto-accept semantics the two profiles
|
|
115
|
+
* genuinely share today (see the Slice 3 plan's Part (a) field-by-field
|
|
116
|
+
* diff table). Everything else about auto-accept — iteration granularity
|
|
117
|
+
* (per-proposal vs. per-group), output record type and cardinality, id
|
|
118
|
+
* templates, timestamp source, and rationale string format — diverges
|
|
119
|
+
* between profiles and stays entirely per-profile; this module does not
|
|
120
|
+
* decide any of it.
|
|
121
|
+
*/
|
|
122
|
+
export function meetsAutoAcceptThreshold(confidence, minConfidence) {
|
|
123
|
+
return confidence >= minConfidence;
|
|
124
|
+
}
|
package/dist/src/raw-source.d.ts
CHANGED
|
@@ -39,6 +39,24 @@ export interface PolicyStandardSourceInput extends Omit<RawSourceInput, "locator
|
|
|
39
39
|
locatorScheme?: LocatorScheme;
|
|
40
40
|
metadata?: Record<string, unknown>;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Raw Source kinds whose factories accept a caller-supplied `locatorScheme`
|
|
44
|
+
* override and otherwise fall back to a per-kind default. `uploaded-document`
|
|
45
|
+
* has no default (`undefined`) because `UploadedDocumentSourceInput.locatorScheme`
|
|
46
|
+
* is required, not optional — modeled here as a real, explicit table entry
|
|
47
|
+
* rather than an omission, so a caller-supplied value is the only source of
|
|
48
|
+
* truth for that kind.
|
|
49
|
+
*
|
|
50
|
+
* Module-internal seam: consumed by relative import from
|
|
51
|
+
* `rawSourceWithDefaultLocatorScheme` below and by
|
|
52
|
+
* `tests/raw-source-defaults.test.ts`, NOT re-exported from `src/index.ts`.
|
|
53
|
+
*/
|
|
54
|
+
export declare const DEFAULT_LOCATOR_SCHEME: {
|
|
55
|
+
"uploaded-document": undefined;
|
|
56
|
+
"api-record": LocatorScheme;
|
|
57
|
+
"web-page": LocatorScheme;
|
|
58
|
+
"manual-entry": LocatorScheme;
|
|
59
|
+
};
|
|
42
60
|
export declare function uploadedDocumentSource(input: UploadedDocumentSourceInput): RawSource;
|
|
43
61
|
export declare function apiRecordSource(input: ApiRecordSourceInput): RawSource;
|
|
44
62
|
export declare function webPageSource(input: WebPageSourceInput): RawSource;
|
package/dist/src/raw-source.js
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw Source kinds whose factories accept a caller-supplied `locatorScheme`
|
|
3
|
+
* override and otherwise fall back to a per-kind default. `uploaded-document`
|
|
4
|
+
* has no default (`undefined`) because `UploadedDocumentSourceInput.locatorScheme`
|
|
5
|
+
* is required, not optional — modeled here as a real, explicit table entry
|
|
6
|
+
* rather than an omission, so a caller-supplied value is the only source of
|
|
7
|
+
* truth for that kind.
|
|
8
|
+
*
|
|
9
|
+
* Module-internal seam: consumed by relative import from
|
|
10
|
+
* `rawSourceWithDefaultLocatorScheme` below and by
|
|
11
|
+
* `tests/raw-source-defaults.test.ts`, NOT re-exported from `src/index.ts`.
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_LOCATOR_SCHEME = {
|
|
14
|
+
"uploaded-document": undefined,
|
|
15
|
+
"api-record": "structured-field",
|
|
16
|
+
"web-page": "html",
|
|
17
|
+
"manual-entry": "structured-field",
|
|
18
|
+
};
|
|
19
|
+
function rawSourceWithDefaultLocatorScheme(kind, input) {
|
|
20
|
+
return rawSource(kind, {
|
|
21
|
+
locatorScheme: DEFAULT_LOCATOR_SCHEME[kind],
|
|
22
|
+
...input,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
1
25
|
export function uploadedDocumentSource(input) {
|
|
2
|
-
return
|
|
26
|
+
return rawSourceWithDefaultLocatorScheme("uploaded-document", input);
|
|
3
27
|
}
|
|
4
28
|
export function apiRecordSource(input) {
|
|
5
|
-
return
|
|
6
|
-
locatorScheme: "structured-field",
|
|
7
|
-
...input,
|
|
8
|
-
});
|
|
29
|
+
return rawSourceWithDefaultLocatorScheme("api-record", input);
|
|
9
30
|
}
|
|
10
31
|
export function webPageSource(input) {
|
|
11
|
-
return
|
|
12
|
-
locatorScheme: "html",
|
|
13
|
-
...input,
|
|
14
|
-
});
|
|
32
|
+
return rawSourceWithDefaultLocatorScheme("web-page", input);
|
|
15
33
|
}
|
|
16
34
|
export function manualEntrySource(input) {
|
|
17
|
-
return
|
|
18
|
-
locatorScheme: "structured-field",
|
|
19
|
-
...input,
|
|
20
|
-
});
|
|
35
|
+
return rawSourceWithDefaultLocatorScheme("manual-entry", input);
|
|
21
36
|
}
|
|
22
37
|
export function policyStandardSource(input) {
|
|
23
38
|
const policyStandard = {
|
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import type { SurveyObservationInput } from "./builder.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
field: string;
|
|
5
|
-
value: readonly TItem[];
|
|
6
|
-
rawSource: SurveyObservationInput["rawSource"];
|
|
7
|
-
extraction: Omit<SurveyObservationInput["extraction"], "target" | "value" | "excerpt"> & {
|
|
8
|
-
target?: string;
|
|
9
|
-
excerpt?: string | null;
|
|
10
|
-
};
|
|
11
|
-
reviewOutcome?: SurveyObservationInput["reviewOutcome"];
|
|
12
|
-
claim: Omit<SurveyObservationInput["claim"], "fieldOrBehavior" | "value"> & {
|
|
13
|
-
fieldOrBehavior?: string;
|
|
14
|
-
};
|
|
15
|
-
candidate?: SurveyObservationInput["candidate"];
|
|
16
|
-
candidateSet?: SurveyObservationInput["candidateSet"];
|
|
2
|
+
import { type ObservationAuthoringInput } from "./observation-helper.js";
|
|
3
|
+
export interface RepeatedObservationInput<TItem> extends ObservationAuthoringInput<readonly TItem[]> {
|
|
17
4
|
representation?: "aggregate-array";
|
|
18
|
-
metadata?: Record<string, unknown>;
|
|
19
5
|
}
|
|
20
6
|
export declare function repeatedObservation<TItem>(input: RepeatedObservationInput<TItem>): SurveyObservationInput;
|
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildRepeatedObservation } from "./observation-helper.js";
|
|
2
2
|
export function repeatedObservation(input) {
|
|
3
|
-
|
|
4
|
-
const value = [...input.value];
|
|
5
|
-
return buildObservation({
|
|
6
|
-
...input,
|
|
7
|
-
value,
|
|
8
|
-
surveyMetadata: {
|
|
9
|
-
repeated: {
|
|
10
|
-
representation,
|
|
11
|
-
itemCount: value.length,
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
defaultExcerpt: `${input.field}: ${value.length} item(s)`,
|
|
15
|
-
});
|
|
3
|
+
return buildRepeatedObservation(input);
|
|
16
4
|
}
|
|
@@ -31,7 +31,7 @@ export interface CanonicalReviewProofPayload {
|
|
|
31
31
|
candidateId?: string;
|
|
32
32
|
subjectType: string;
|
|
33
33
|
subjectId: string;
|
|
34
|
-
|
|
34
|
+
facet: string;
|
|
35
35
|
claimType: string;
|
|
36
36
|
fieldOrBehavior: string;
|
|
37
37
|
};
|
|
@@ -92,7 +92,7 @@ export interface CanonicalReviewProofPayload {
|
|
|
92
92
|
candidateId?: string;
|
|
93
93
|
subjectType: string;
|
|
94
94
|
subjectId: string;
|
|
95
|
-
|
|
95
|
+
facet: string;
|
|
96
96
|
claimType: string;
|
|
97
97
|
fieldOrBehavior: string;
|
|
98
98
|
value?: unknown;
|
package/dist/src/review-proof.js
CHANGED
|
@@ -23,7 +23,7 @@ export function buildCanonicalReviewProofPayload(input) {
|
|
|
23
23
|
candidateId: input.candidate.id,
|
|
24
24
|
subjectType: input.claim.subjectType,
|
|
25
25
|
subjectId: input.claim.subjectId,
|
|
26
|
-
|
|
26
|
+
facet: input.claim.facet,
|
|
27
27
|
claimType: input.claim.claimType,
|
|
28
28
|
fieldOrBehavior: input.claim.fieldOrBehavior,
|
|
29
29
|
},
|
|
@@ -86,7 +86,7 @@ export function buildCanonicalReviewProofPayload(input) {
|
|
|
86
86
|
candidateId: input.claim.candidateId,
|
|
87
87
|
subjectType: input.claim.subjectType,
|
|
88
88
|
subjectId: input.claim.subjectId,
|
|
89
|
-
|
|
89
|
+
facet: input.claim.facet,
|
|
90
90
|
claimType: input.claim.claimType,
|
|
91
91
|
fieldOrBehavior: input.claim.fieldOrBehavior,
|
|
92
92
|
value: input.claim.value,
|
|
@@ -42,7 +42,11 @@ export interface ClaimTargetHint {
|
|
|
42
42
|
claimId?: string;
|
|
43
43
|
subjectType: string;
|
|
44
44
|
subjectId: string;
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Producer-defined grouping or namespace for this claim (Hachure schema 5,
|
|
47
|
+
* surface@2.0.0: Claim.surface -> Claim.facet).
|
|
48
|
+
*/
|
|
49
|
+
facet: string;
|
|
46
50
|
claimType: string;
|
|
47
51
|
fieldOrBehavior: string;
|
|
48
52
|
impactLevel: ClaimTarget["impactLevel"];
|
|
@@ -46,7 +46,7 @@ export declare const publicDirectoryReviewItemExample: {
|
|
|
46
46
|
claimId: string;
|
|
47
47
|
subjectType: string;
|
|
48
48
|
subjectId: string;
|
|
49
|
-
|
|
49
|
+
facet: string;
|
|
50
50
|
claimType: string;
|
|
51
51
|
fieldOrBehavior: string;
|
|
52
52
|
impactLevel: "medium";
|
|
@@ -98,7 +98,7 @@ export declare const publicDirectoryReviewItemExample: {
|
|
|
98
98
|
claimId: string;
|
|
99
99
|
subjectType: string;
|
|
100
100
|
subjectId: string;
|
|
101
|
-
|
|
101
|
+
facet: string;
|
|
102
102
|
claimType: string;
|
|
103
103
|
fieldOrBehavior: string;
|
|
104
104
|
impactLevel: "medium";
|
|
@@ -184,7 +184,7 @@ export declare const regulatedRuleConflictReviewItemExample: {
|
|
|
184
184
|
claimId: string;
|
|
185
185
|
subjectType: string;
|
|
186
186
|
subjectId: string;
|
|
187
|
-
|
|
187
|
+
facet: string;
|
|
188
188
|
claimType: string;
|
|
189
189
|
fieldOrBehavior: string;
|
|
190
190
|
impactLevel: "high";
|
|
@@ -235,7 +235,7 @@ export declare const regulatedRuleConflictReviewItemExample: {
|
|
|
235
235
|
claimId: string;
|
|
236
236
|
subjectType: string;
|
|
237
237
|
subjectId: string;
|
|
238
|
-
|
|
238
|
+
facet: string;
|
|
239
239
|
claimType: string;
|
|
240
240
|
fieldOrBehavior: string;
|
|
241
241
|
impactLevel: "high";
|
|
@@ -334,7 +334,7 @@ export declare const facilityCredentialReviewItemExample: {
|
|
|
334
334
|
claimId: string;
|
|
335
335
|
subjectType: string;
|
|
336
336
|
subjectId: string;
|
|
337
|
-
|
|
337
|
+
facet: string;
|
|
338
338
|
claimType: string;
|
|
339
339
|
fieldOrBehavior: string;
|
|
340
340
|
impactLevel: "high";
|
|
@@ -395,7 +395,7 @@ export declare const facilityCredentialReviewItemExample: {
|
|
|
395
395
|
claimId: string;
|
|
396
396
|
subjectType: string;
|
|
397
397
|
subjectId: string;
|
|
398
|
-
|
|
398
|
+
facet: string;
|
|
399
399
|
claimType: string;
|
|
400
400
|
fieldOrBehavior: string;
|
|
401
401
|
impactLevel: "high";
|
|
@@ -472,7 +472,7 @@ export declare const reviewWorkbenchQueueExamples: (ReviewItem | {
|
|
|
472
472
|
claimId: string;
|
|
473
473
|
subjectType: string;
|
|
474
474
|
subjectId: string;
|
|
475
|
-
|
|
475
|
+
facet: string;
|
|
476
476
|
claimType: string;
|
|
477
477
|
fieldOrBehavior: string;
|
|
478
478
|
impactLevel: "medium";
|
|
@@ -524,7 +524,7 @@ export declare const reviewWorkbenchQueueExamples: (ReviewItem | {
|
|
|
524
524
|
claimId: string;
|
|
525
525
|
subjectType: string;
|
|
526
526
|
subjectId: string;
|
|
527
|
-
|
|
527
|
+
facet: string;
|
|
528
528
|
claimType: string;
|
|
529
529
|
fieldOrBehavior: string;
|
|
530
530
|
impactLevel: "medium";
|
|
@@ -609,7 +609,7 @@ export declare const reviewWorkbenchQueueExamples: (ReviewItem | {
|
|
|
609
609
|
claimId: string;
|
|
610
610
|
subjectType: string;
|
|
611
611
|
subjectId: string;
|
|
612
|
-
|
|
612
|
+
facet: string;
|
|
613
613
|
claimType: string;
|
|
614
614
|
fieldOrBehavior: string;
|
|
615
615
|
impactLevel: "high";
|
|
@@ -660,7 +660,7 @@ export declare const reviewWorkbenchQueueExamples: (ReviewItem | {
|
|
|
660
660
|
claimId: string;
|
|
661
661
|
subjectType: string;
|
|
662
662
|
subjectId: string;
|
|
663
|
-
|
|
663
|
+
facet: string;
|
|
664
664
|
claimType: string;
|
|
665
665
|
fieldOrBehavior: string;
|
|
666
666
|
impactLevel: "high";
|
|
@@ -49,7 +49,7 @@ export const publicDirectoryReviewItemExample = {
|
|
|
49
49
|
claimId: "public-field.entity-123.availability-status.current",
|
|
50
50
|
subjectType: "public-record.entity",
|
|
51
51
|
subjectId: "entity-123",
|
|
52
|
-
|
|
52
|
+
facet: "public-record.profile",
|
|
53
53
|
claimType: "public-data.field",
|
|
54
54
|
fieldOrBehavior: "availabilityStatus",
|
|
55
55
|
impactLevel: "medium",
|
|
@@ -100,7 +100,7 @@ export const publicDirectoryReviewItemExample = {
|
|
|
100
100
|
claimId: "public-field.entity-123.availability-status.proposal-456",
|
|
101
101
|
subjectType: "public-record.entity",
|
|
102
102
|
subjectId: "entity-123",
|
|
103
|
-
|
|
103
|
+
facet: "public-record.profile",
|
|
104
104
|
claimType: "public-data.field-candidate",
|
|
105
105
|
fieldOrBehavior: "availabilityStatus",
|
|
106
106
|
impactLevel: "medium",
|
|
@@ -187,7 +187,7 @@ export const regulatedRuleConflictReviewItemExample = {
|
|
|
187
187
|
claimId: "regulated-rule.example-jurisdiction.2026.standard-threshold.current",
|
|
188
188
|
subjectType: "regulated-rule-source",
|
|
189
189
|
subjectId: "example-jurisdiction:2026:standardThreshold",
|
|
190
|
-
|
|
190
|
+
facet: "regulated.rules",
|
|
191
191
|
claimType: "regulated.rule-source-value",
|
|
192
192
|
fieldOrBehavior: "standardThreshold",
|
|
193
193
|
impactLevel: "high",
|
|
@@ -235,7 +235,7 @@ export const regulatedRuleConflictReviewItemExample = {
|
|
|
235
235
|
claimId: "regulated-rule.example-jurisdiction.2026.standard-threshold.proposed",
|
|
236
236
|
subjectType: "regulated-rule-source",
|
|
237
237
|
subjectId: "example-jurisdiction:2026:standardThreshold",
|
|
238
|
-
|
|
238
|
+
facet: "regulated.rules",
|
|
239
239
|
claimType: "regulated.rule-source-value",
|
|
240
240
|
fieldOrBehavior: "standardThreshold",
|
|
241
241
|
impactLevel: "high",
|
|
@@ -332,7 +332,7 @@ export const facilityCredentialReviewItemExample = {
|
|
|
332
332
|
claimId: "facility-credential.facility-42.operating-license.current",
|
|
333
333
|
subjectType: "facility",
|
|
334
334
|
subjectId: "facility-42",
|
|
335
|
-
|
|
335
|
+
facet: "facility.credential-profile",
|
|
336
336
|
claimType: "facility.credential",
|
|
337
337
|
fieldOrBehavior: "operatingLicenseCredential",
|
|
338
338
|
impactLevel: "high",
|
|
@@ -391,7 +391,7 @@ export const facilityCredentialReviewItemExample = {
|
|
|
391
391
|
claimId: "facility-credential.facility-42.operating-license.registry",
|
|
392
392
|
subjectType: "facility",
|
|
393
393
|
subjectId: "facility-42",
|
|
394
|
-
|
|
394
|
+
facet: "facility.credential-profile",
|
|
395
395
|
claimType: "facility.credential-candidate",
|
|
396
396
|
fieldOrBehavior: "operatingLicenseCredential",
|
|
397
397
|
impactLevel: "high",
|
|
@@ -1308,7 +1308,7 @@ function isReviewCandidate(value) {
|
|
|
1308
1308
|
&& isRecord(value.claimTarget)
|
|
1309
1309
|
&& typeof value.claimTarget.subjectType === "string"
|
|
1310
1310
|
&& typeof value.claimTarget.subjectId === "string"
|
|
1311
|
-
&& typeof value.claimTarget.
|
|
1311
|
+
&& typeof value.claimTarget.facet === "string"
|
|
1312
1312
|
&& typeof value.claimTarget.claimType === "string"
|
|
1313
1313
|
&& typeof value.claimTarget.fieldOrBehavior === "string"
|
|
1314
1314
|
&& typeof value.claimTarget.impactLevel === "string";
|
|
@@ -2,7 +2,7 @@ import { type DeriveReviewSessionApplyResultForSnapshotResult, type MapReviewWor
|
|
|
2
2
|
import { type ReviewDecisionModeIssue } from "./producer-decision-mode.js";
|
|
3
3
|
import { type ReviewSessionReplayIssue } from "./review-session-replay.js";
|
|
4
4
|
import type { ReviewDecision, ReviewSessionEvent } from "../review-resource.js";
|
|
5
|
-
import type
|
|
5
|
+
import { type ReviewQueueSessionState } from "./review-queue-session.js";
|
|
6
6
|
export interface ServerReviewSessionRecord {
|
|
7
7
|
readonly sessionName: string;
|
|
8
8
|
readonly snapshot: ReviewQueueSessionState;
|
|
@@ -59,6 +59,19 @@ export interface DeriveServerReviewSessionApplyResultOptions {
|
|
|
59
59
|
}
|
|
60
60
|
export declare function createServerReviewSessionRecord(options: CreateServerReviewSessionRecordOptions): ServerReviewSessionRecord;
|
|
61
61
|
export declare function hashReviewSessionSnapshot(snapshot: ReviewQueueSessionState): string;
|
|
62
|
+
/**
|
|
63
|
+
* Project the current review session state from a snapshot plus its
|
|
64
|
+
* append-only event log: replay events over the snapshot when any exist,
|
|
65
|
+
* otherwise return the snapshot itself unchanged (same object reference —
|
|
66
|
+
* no eager replay, no copy). Collapses the
|
|
67
|
+
* `events.length > 0 ? replayReviewSessionEvents(snapshot, events) : snapshot`
|
|
68
|
+
* conditional that was duplicated across the MCP and console review
|
|
69
|
+
* adapters (src/mcp/review-mcp.ts, src/console/review-console-server.ts) —
|
|
70
|
+
* same shape of fix as canonicalJson in ./canonical.ts, which was
|
|
71
|
+
* extracted after two near-identical copies of a derivation desynced
|
|
72
|
+
* (audit 2026-06-28, ops#24).
|
|
73
|
+
*/
|
|
74
|
+
export declare function currentSessionState(snapshot: ReviewQueueSessionState, events: readonly ReviewSessionEvent[]): ReviewQueueSessionState;
|
|
62
75
|
export declare function compareServerReviewSessionFreshness(record: ServerReviewSessionRecord, snapshot: ReviewQueueSessionState, eventCount?: number): ServerReviewSessionFreshnessComparison;
|
|
63
76
|
export declare function assertServerReviewSessionFreshness(record: ServerReviewSessionRecord, snapshot: ReviewQueueSessionState, eventCount?: number): void;
|
|
64
77
|
export declare function validateServerReviewSessionEvents(record: ServerReviewSessionRecord, events: readonly ReviewSessionEvent[]): ServerReviewSessionEventValidationIssue[];
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { deriveReviewSessionApplyResultForSnapshot, mapReviewWorkbenchResultsToApplyActions, ReviewApplyActionMappingError, } from "./review-workbench.js";
|
|
3
3
|
import { validateReviewDecisionMode, } from "./producer-decision-mode.js";
|
|
4
4
|
import { validateReviewSessionEventsForSnapshot, } from "./review-session-replay.js";
|
|
5
|
+
import { replayReviewSessionEvents } from "./review-queue-session.js";
|
|
5
6
|
import { canonicalJson } from "./canonical.js";
|
|
6
7
|
export class StaleServerReviewSessionError extends Error {
|
|
7
8
|
name = "StaleServerReviewSessionError";
|
|
@@ -34,6 +35,21 @@ export function createServerReviewSessionRecord(options) {
|
|
|
34
35
|
export function hashReviewSessionSnapshot(snapshot) {
|
|
35
36
|
return sha256(canonicalJson(snapshot));
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Project the current review session state from a snapshot plus its
|
|
40
|
+
* append-only event log: replay events over the snapshot when any exist,
|
|
41
|
+
* otherwise return the snapshot itself unchanged (same object reference —
|
|
42
|
+
* no eager replay, no copy). Collapses the
|
|
43
|
+
* `events.length > 0 ? replayReviewSessionEvents(snapshot, events) : snapshot`
|
|
44
|
+
* conditional that was duplicated across the MCP and console review
|
|
45
|
+
* adapters (src/mcp/review-mcp.ts, src/console/review-console-server.ts) —
|
|
46
|
+
* same shape of fix as canonicalJson in ./canonical.ts, which was
|
|
47
|
+
* extracted after two near-identical copies of a derivation desynced
|
|
48
|
+
* (audit 2026-06-28, ops#24).
|
|
49
|
+
*/
|
|
50
|
+
export function currentSessionState(snapshot, events) {
|
|
51
|
+
return events.length > 0 ? replayReviewSessionEvents(snapshot, events) : snapshot;
|
|
52
|
+
}
|
|
37
53
|
export function compareServerReviewSessionFreshness(record, snapshot, eventCount) {
|
|
38
54
|
const actualSnapshotHash = hashReviewSessionSnapshot(snapshot);
|
|
39
55
|
const eventCountMatches = record.eventCount === undefined || eventCount === undefined || record.eventCount === eventCount;
|