@kontourai/survey 0.4.23 → 0.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.
Files changed (39) hide show
  1. package/README.md +86 -872
  2. package/dist/example-data/corrected-document-candidates.d.ts +2 -0
  3. package/dist/{fixtures → example-data}/corrected-document-candidates.js +3 -3
  4. package/dist/{fixtures → example-data}/downstream-public-directory-proposal.d.ts +1 -1
  5. package/dist/{fixtures → example-data}/downstream-public-directory-proposal.js +1 -1
  6. package/dist/{fixtures → example-data}/public-directory-review-resource.d.ts +2 -2
  7. package/dist/{fixtures → example-data}/public-directory-review-resource.js +2 -2
  8. package/dist/example-data/public-field-review.d.ts +2 -0
  9. package/dist/{fixtures → example-data}/public-field-review.js +2 -2
  10. package/dist/{fixtures → example-data}/regulated-document-review-resource.d.ts +1 -1
  11. package/dist/{fixtures → example-data}/regulated-document-review-resource.js +3 -3
  12. package/dist/examples/public-field-observation.js +4 -4
  13. package/dist/examples/review-workbench/downstream-public-directory-adapter.d.ts +1 -1
  14. package/dist/examples/review-workbench/facility-credential-consumer.d.ts +2 -2
  15. package/dist/examples/review-workbench/facility-credential-consumer.js +8 -8
  16. package/dist/examples/review-workbench/server-apply-consumer.d.ts +1 -1
  17. package/dist/examples/review-workbench/server-apply-consumer.js +3 -3
  18. package/dist/src/agent-utterance.d.ts +166 -0
  19. package/dist/src/agent-utterance.js +373 -0
  20. package/dist/src/anthropic.d.ts +104 -0
  21. package/dist/src/anthropic.js +383 -0
  22. package/dist/src/index.d.ts +10 -2
  23. package/dist/src/index.js +5 -1
  24. package/dist/src/inquiry-mapping.d.ts +256 -0
  25. package/dist/src/inquiry-mapping.js +385 -0
  26. package/dist/src/review-workbench/review-queue-session.js +3 -3
  27. package/dist/src/review-workbench/review-surface-preview.js +1 -1
  28. package/dist/src/review-workbench/review-workbench-data.d.ts +4 -4
  29. package/dist/src/review-workbench/review-workbench-data.js +14 -14
  30. package/dist/src/schema-mapping.d.ts +196 -0
  31. package/dist/src/schema-mapping.js +486 -0
  32. package/dist/src/to-flow-artifact.d.ts +41 -0
  33. package/dist/src/to-flow-artifact.js +31 -0
  34. package/dist/src/to-surface.d.ts +3 -3
  35. package/dist/src/to-surface.js +1 -1
  36. package/dist/src/types.d.ts +2 -2
  37. package/package.json +25 -9
  38. package/dist/fixtures/corrected-document-candidates.d.ts +0 -2
  39. package/dist/fixtures/public-field-review.d.ts +0 -2
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Inquiry mapping — ADR 0003 step 5.
3
+ *
4
+ * This module implements the "memoize the mapping, never the answer" principle.
5
+ * A MappingProposal is a reviewable record produced by a pluggable MappingProposer.
6
+ * Proposals flow through Survey's existing candidate → review machinery.
7
+ * The durable artifact after review is an InquiryMapping.
8
+ * Answers always recompute live from the TrustBundle.
9
+ *
10
+ * Nothing here silently decides. Exact canonical-form matching is the only thing
11
+ * that resolves without review. A proposer may suggest that a question maps to a
12
+ * registered claim or rule — but every suggestion lands as a MappingProposal with
13
+ * provenance before it counts. (ADR 0003 §4)
14
+ *
15
+ * Integration point: resolveQuestion is the entry point for consumers checking
16
+ * whether a cached mapping already covers a question. Flow-agent hook wiring
17
+ * (connecting this to an agent's output pipeline) is out of scope for this module
18
+ * and lives in the flow-agents repo.
19
+ */
20
+ import { resolveInquiry } from "@kontourai/surface";
21
+ // ---------------------------------------------------------------------------
22
+ // Question normalization
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Deterministic normalization for question strings.
26
+ *
27
+ * Rules:
28
+ * - Lowercase
29
+ * - Collapse internal whitespace runs to a single space
30
+ * - Trim leading/trailing whitespace
31
+ * - Strip terminal punctuation (. ? ! , ;) from the end
32
+ *
33
+ * This is exact normalized-text memoization, not semantic matching.
34
+ * Two questions that differ only in case, whitespace, or trailing punctuation
35
+ * are considered the same question. Questions with different wording but the
36
+ * same intent are NOT matched here; a MappingProposer handles that.
37
+ */
38
+ export function normalizeQuestion(question) {
39
+ return question
40
+ .toLowerCase()
41
+ .replace(/\s+/g, " ")
42
+ .trim()
43
+ .replace(/[.?!,;]+$/u, "");
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // Proposal → CandidateSet projection
47
+ // ---------------------------------------------------------------------------
48
+ /**
49
+ * Project an array of proposals for a single question into Survey's existing
50
+ * Candidate / CandidateSet shapes so they flow through the existing review
51
+ * machinery rather than a parallel system.
52
+ *
53
+ * Status rules:
54
+ * - All proposals agree on the same target/ruleId → "needs-review"
55
+ * - Proposals disagree (more than one distinct resolved target/rule) → "conflict"
56
+ * - Empty proposals → "needs-review" with empty candidates
57
+ *
58
+ * The CandidateSet target is the normalized question; each Candidate carries
59
+ * the proposal id as extractionId and the proposal metadata.
60
+ */
61
+ export function proposalsToCandidateSet(question, proposals) {
62
+ const normalized = normalizeQuestion(question);
63
+ const candidates = proposals.map((proposal) => ({
64
+ id: `mapping-candidate.${proposal.id}`,
65
+ extractionId: proposal.id,
66
+ value: proposal.proposedTarget ?? proposal.proposedRuleId ?? null,
67
+ confidence: proposal.confidence,
68
+ metadata: {
69
+ mappingProposal: {
70
+ proposalId: proposal.id,
71
+ proposedTarget: proposal.proposedTarget,
72
+ proposedRuleId: proposal.proposedRuleId,
73
+ confidence: proposal.confidence,
74
+ rationale: proposal.rationale,
75
+ excerpt: proposal.excerpt,
76
+ proposedBy: proposal.proposedBy,
77
+ proposedAt: proposal.proposedAt,
78
+ },
79
+ },
80
+ }));
81
+ // Determine status: conflict if proposals disagree on target/rule
82
+ const status = proposals.length > 1 && proposalsDisagree(proposals) ? "conflict" : "needs-review";
83
+ const candidateSet = {
84
+ id: `mapping-candidate-set.${normalized}`,
85
+ target: normalized,
86
+ candidates,
87
+ status,
88
+ metadata: {
89
+ inquiryMapping: {
90
+ question,
91
+ normalizedQuestion: normalized,
92
+ kind: "inquiry-question",
93
+ },
94
+ },
95
+ };
96
+ return { candidateSet, candidates };
97
+ }
98
+ function proposalsDisagree(proposals) {
99
+ const keys = new Set(proposals.map((p) => p.proposedTarget
100
+ ? `target:${p.proposedTarget.subjectType}/${p.proposedTarget.subjectId}/${p.proposedTarget.fieldOrBehavior}`
101
+ : `rule:${p.proposedRuleId}`));
102
+ return keys.size > 1;
103
+ }
104
+ // ---------------------------------------------------------------------------
105
+ // Review outcome → InquiryMapping
106
+ // ---------------------------------------------------------------------------
107
+ /**
108
+ * Turn a Survey ReviewOutcome on a mapping candidate set into a durable
109
+ * InquiryMapping record.
110
+ *
111
+ * The candidateSet must have been built by proposalsToCandidateSet.
112
+ * The reviewOutcome's candidateId must match one of the candidates.
113
+ */
114
+ export function applyMappingReview(candidateSet, reviewOutcome) {
115
+ const normalized = candidateSet.target;
116
+ // Find the reviewed candidate
117
+ const candidateId = reviewOutcome.candidateId ?? candidateSet.selectedCandidateId ?? candidateSet.candidates[0]?.id;
118
+ const candidate = candidateSet.candidates.find((c) => c.id === candidateId);
119
+ if (!candidate) {
120
+ throw new Error(`applyMappingReview: no candidate found for id ${candidateId ?? "<none>"}`);
121
+ }
122
+ const meta = candidate.metadata?.mappingProposal;
123
+ const proposalId = meta?.proposalId ?? candidate.extractionId;
124
+ const status = reviewOutcome.status === "verified" || reviewOutcome.status === "assumed" || reviewOutcome.status === "rejected"
125
+ ? reviewOutcome.status
126
+ : "rejected";
127
+ return {
128
+ id: `inquiry-mapping.${normalized}`,
129
+ normalizedQuestion: normalized,
130
+ target: meta?.proposedTarget,
131
+ ruleId: meta?.proposedRuleId,
132
+ status: status,
133
+ reviewedBy: reviewOutcome.actor ?? "unknown",
134
+ reviewedAt: reviewOutcome.reviewedAt ?? new Date().toISOString(),
135
+ rationale: reviewOutcome.rationale,
136
+ withinComfortZone: reviewOutcome.withinComfortZone,
137
+ proposalId,
138
+ };
139
+ }
140
+ /**
141
+ * Apply an auto-accept policy to a list of proposals, returning InquiryMappings.
142
+ *
143
+ * Proposals at or above minConfidence → status "assumed", withinComfortZone: true
144
+ * Proposals below minConfidence → return a "needs-review" mapping (not yet durable)
145
+ *
146
+ * Only non-conflicting proposals are auto-accepted. If proposals disagree, they
147
+ * need human review regardless of confidence.
148
+ *
149
+ * Returns an array of InquiryMappings (only for accepted proposals).
150
+ */
151
+ export function applyAutoAcceptPolicy(proposals, policy) {
152
+ if (proposals.length === 0)
153
+ return [];
154
+ // If proposals disagree, none can be auto-accepted
155
+ if (proposals.length > 1 && proposalsDisagree(proposals))
156
+ return [];
157
+ return proposals
158
+ .filter((p) => p.confidence >= policy.minConfidence)
159
+ .map((proposal) => ({
160
+ id: `inquiry-mapping.auto.${normalizeQuestion(proposal.question)}`,
161
+ normalizedQuestion: normalizeQuestion(proposal.question),
162
+ target: proposal.proposedTarget,
163
+ ruleId: proposal.proposedRuleId,
164
+ status: "assumed",
165
+ reviewedBy: "auto-accept-policy",
166
+ reviewedAt: proposal.proposedAt,
167
+ rationale: `Auto-accepted: confidence ${proposal.confidence} >= threshold ${policy.minConfidence}. ${proposal.rationale}`,
168
+ withinComfortZone: true,
169
+ proposalId: proposal.id,
170
+ }));
171
+ }
172
+ // ---------------------------------------------------------------------------
173
+ // Mapping lookup
174
+ // ---------------------------------------------------------------------------
175
+ /**
176
+ * Look up an InquiryMapping for a question by exact normalized-text match.
177
+ *
178
+ * Rejected mappings are remembered but never resolve: this function returns
179
+ * undefined for rejected mappings so callers treat the question as a miss.
180
+ * To check whether a question was previously rejected (and should not be
181
+ * re-proposed), call lookupRejectedMapping.
182
+ */
183
+ export function lookupMapping(mappings, question) {
184
+ const normalized = normalizeQuestion(question);
185
+ const match = mappings.find((m) => m.normalizedQuestion === normalized);
186
+ if (!match)
187
+ return undefined;
188
+ // Rejected mappings are remembered but never resolve
189
+ if (match.status === "rejected")
190
+ return undefined;
191
+ return match;
192
+ }
193
+ /**
194
+ * Check whether a question was previously rejected.
195
+ * Rejected mappings prevent re-proposing: if this returns a mapping, the
196
+ * question should not be sent to a proposer again without human escalation.
197
+ */
198
+ export function lookupRejectedMapping(mappings, question) {
199
+ const normalized = normalizeQuestion(question);
200
+ return mappings.find((m) => m.normalizedQuestion === normalized && m.status === "rejected");
201
+ }
202
+ // ---------------------------------------------------------------------------
203
+ // Live question resolution
204
+ // ---------------------------------------------------------------------------
205
+ /**
206
+ * Resolve a natural-language question against a TrustBundle.
207
+ *
208
+ * On mapping hit (verified or assumed): constructs a Surface Inquiry from the
209
+ * mapped target/rule and returns resolveInquiry(...) — answers always recompute
210
+ * live from the current bundle state; the mapping is memoized, not the answer.
211
+ *
212
+ * On miss (no mapping, or rejected mapping): returns an InquiryRecord with
213
+ * outcome "unsupported" so the gap is honest and recordable.
214
+ *
215
+ * This function is the clean integration point for consumers. Flow-agent hook
216
+ * wiring (connecting this to an agent's output pipeline) lives in the
217
+ * flow-agents repo.
218
+ */
219
+ export function resolveQuestion(bundle, question, options) {
220
+ const { mappings, rules, now, askedBy } = options;
221
+ const normalized = normalizeQuestion(question);
222
+ const mapping = lookupMapping(mappings, question);
223
+ const askedAt = (now ?? new Date()).toISOString();
224
+ const inquiryId = `inquiry.${normalized}.${askedAt}`;
225
+ if (!mapping) {
226
+ // No mapping or rejected: return honest unsupported record
227
+ return {
228
+ id: inquiryId,
229
+ inquiry: {
230
+ id: inquiryId,
231
+ question,
232
+ askedBy,
233
+ askedAt,
234
+ },
235
+ outcome: "unsupported",
236
+ resolutionPath: { claimIds: [] },
237
+ inputSnapshot: [],
238
+ statusFunctionVersion: "1",
239
+ resolvedAt: askedAt,
240
+ };
241
+ }
242
+ // Build a Surface Inquiry from the mapping
243
+ const inquiry = {
244
+ id: inquiryId,
245
+ question,
246
+ target: mapping.target,
247
+ askedBy,
248
+ askedAt,
249
+ metadata: {
250
+ inquiryMappingId: mapping.id,
251
+ normalizedQuestion: normalized,
252
+ },
253
+ };
254
+ return resolveInquiry(bundle, inquiry, { now, rules });
255
+ }
256
+ // ---------------------------------------------------------------------------
257
+ // Review workbench integration
258
+ // ---------------------------------------------------------------------------
259
+ /**
260
+ * Build ReviewItem records for the existing review workbench from a list of
261
+ * mapping candidate sets.
262
+ *
263
+ * Follow the existing ReviewItem contract exactly. This helper produces
264
+ * ReviewItem payloads so mapping proposals can be reviewed through the same
265
+ * workbench as other Survey candidates.
266
+ */
267
+ export function buildMappingReviewItems(candidateSets) {
268
+ return candidateSets.map(({ candidateSet, candidates }) => ({
269
+ apiVersion: "survey.kontourai.io/v1alpha1",
270
+ kind: "ReviewItem",
271
+ metadata: {
272
+ name: candidateSet.id,
273
+ labels: { "survey.kontourai.io/kind": "inquiry-mapping" },
274
+ },
275
+ spec: {
276
+ target: candidateSet.target,
277
+ candidates: candidates.map((candidate) => {
278
+ const meta = candidate.metadata?.mappingProposal;
279
+ const targetOrRule = meta?.proposedTarget
280
+ ? `${meta.proposedTarget.subjectType}/${meta.proposedTarget.subjectId}/${meta.proposedTarget.fieldOrBehavior}`
281
+ : `rule:${meta?.proposedRuleId ?? "unknown"}`;
282
+ return {
283
+ id: candidate.id,
284
+ role: "proposed",
285
+ value: candidate.value,
286
+ confidence: candidate.confidence,
287
+ source: {
288
+ sourceRef: `inquiry-question:${candidateSet.target}`,
289
+ kind: "inquiry-question",
290
+ observedAt: meta?.proposedAt ?? new Date().toISOString(),
291
+ locatorScheme: "text",
292
+ },
293
+ extraction: {
294
+ target: targetOrRule,
295
+ confidence: meta?.confidence,
296
+ extractor: meta?.proposedBy ?? "unknown",
297
+ extractedAt: meta?.proposedAt ?? new Date().toISOString(),
298
+ },
299
+ claimTarget: {
300
+ subjectType: meta?.proposedTarget?.subjectType ?? "inquiry",
301
+ subjectId: meta?.proposedTarget?.subjectId ?? candidateSet.target,
302
+ surface: "inquiry.mapping",
303
+ claimType: "inquiry-mapping",
304
+ fieldOrBehavior: meta?.proposedTarget?.fieldOrBehavior ?? meta?.proposedRuleId ?? "unknown",
305
+ impactLevel: "low",
306
+ },
307
+ projection: {
308
+ candidateSetId: candidateSet.id,
309
+ candidateId: candidate.id,
310
+ },
311
+ };
312
+ }),
313
+ candidateSetStatus: candidateSet.status,
314
+ rationale: candidateSet.rationale,
315
+ },
316
+ status: {
317
+ observedCandidateCount: candidates.length,
318
+ },
319
+ }));
320
+ }
321
+ // ---------------------------------------------------------------------------
322
+ // Reference proposer (deterministic, for tests — not for production use)
323
+ // ---------------------------------------------------------------------------
324
+ /**
325
+ * Reference MappingProposer for tests.
326
+ *
327
+ * REFERENCE IMPLEMENTATION ONLY — not suitable for production matching.
328
+ *
329
+ * Matching strategy: a question maps to a claim if it contains both the
330
+ * claim's subjectId and fieldOrBehavior as token substrings (case-insensitive,
331
+ * space-delimited token match). This is intentionally simple and transparent
332
+ * so tests can be deterministic.
333
+ */
334
+ export const referenceMappingProposer = {
335
+ name: "reference-mapping-proposer",
336
+ propose(question, context) {
337
+ const q = question.toLowerCase();
338
+ const proposals = [];
339
+ const now = new Date().toISOString();
340
+ if (context.bundle) {
341
+ for (const claim of context.bundle.claims) {
342
+ const subjectTokens = claim.subjectId.toLowerCase().split(/[\s\-_.:/]+/);
343
+ const fieldTokens = claim.fieldOrBehavior.toLowerCase().split(/[\s\-_.:/]+/);
344
+ const subjectMatch = subjectTokens.some((tok) => tok.length > 2 && q.includes(tok));
345
+ const fieldMatch = fieldTokens.some((tok) => tok.length > 2 && q.includes(tok));
346
+ if (subjectMatch && fieldMatch) {
347
+ const proposalId = `proposal.ref.${claim.subjectId}.${claim.fieldOrBehavior}.${Date.now()}`;
348
+ proposals.push({
349
+ id: proposalId,
350
+ question,
351
+ proposedTarget: {
352
+ subjectType: claim.subjectType,
353
+ subjectId: claim.subjectId,
354
+ fieldOrBehavior: claim.fieldOrBehavior,
355
+ qualifiers: claim.qualifiers,
356
+ },
357
+ confidence: 0.75,
358
+ rationale: `Reference proposer: question contains subjectId token and fieldOrBehavior token for claim ${claim.id}.`,
359
+ proposedBy: "reference-mapping-proposer",
360
+ proposedAt: now,
361
+ });
362
+ }
363
+ }
364
+ }
365
+ if (context.rules) {
366
+ for (const rule of context.rules) {
367
+ const ruleTokens = rule.name.toLowerCase().split(/[\s\-_.:/]+/);
368
+ const ruleMatch = ruleTokens.some((tok) => tok.length > 2 && q.includes(tok));
369
+ if (ruleMatch) {
370
+ const proposalId = `proposal.ref.rule.${rule.id}.${Date.now()}`;
371
+ proposals.push({
372
+ id: proposalId,
373
+ question,
374
+ proposedRuleId: rule.id,
375
+ confidence: 0.65,
376
+ rationale: `Reference proposer: question contains rule name token for rule ${rule.id}.`,
377
+ proposedBy: "reference-mapping-proposer",
378
+ proposedAt: now,
379
+ });
380
+ }
381
+ }
382
+ }
383
+ return proposals;
384
+ },
385
+ };
@@ -1,4 +1,4 @@
1
- import { publicDirectoryReviewItemFixture, reviewWorkbenchQueueFixtures } from "./review-workbench-data.js";
1
+ import { publicDirectoryReviewItemExample, reviewWorkbenchQueueExamples } from "./review-workbench-data.js";
2
2
  import { reviewResourceApiVersion, } from "../../src/review-resource.js";
3
3
  export const reviewWorkbenchSessionStorageKey = "kontourai.survey.review-workbench.session-events.v1";
4
4
  export const defaultReviewSessionName = "review-workbench-session";
@@ -22,7 +22,7 @@ export const workbenchDecisionDefinitions = {
22
22
  status: "rejected",
23
23
  },
24
24
  };
25
- export function initialReviewWorkbenchState(item = publicDirectoryReviewItemFixture) {
25
+ export function initialReviewWorkbenchState(item = publicDirectoryReviewItemExample) {
26
26
  return {
27
27
  item,
28
28
  note: "",
@@ -31,7 +31,7 @@ export function initialReviewWorkbenchState(item = publicDirectoryReviewItemFixt
31
31
  actorId: "review-workbench-operator",
32
32
  };
33
33
  }
34
- export function initialReviewQueueSessionState(items = reviewWorkbenchQueueFixtures) {
34
+ export function initialReviewQueueSessionState(items = reviewWorkbenchQueueExamples) {
35
35
  return {
36
36
  items,
37
37
  activeItemName: items[0]?.metadata.name ?? "",
@@ -109,7 +109,7 @@ function portableAuthorityTrace(value) {
109
109
  return {
110
110
  status: "provided",
111
111
  label: "Portable authority trace provided",
112
- detail: `${value.length} authority trace entr${value.length === 1 ? "y" : "ies"} supplied by the fixture.`,
112
+ detail: `${value.length} authority trace entr${value.length === 1 ? "y" : "ies"} supplied by the example.`,
113
113
  };
114
114
  }
115
115
  return {
@@ -1,5 +1,5 @@
1
1
  import { type ReviewItem } from "../review-resource.js";
2
- export declare const publicDirectoryReviewItemFixture: {
2
+ export declare const publicDirectoryReviewItemExample: {
3
3
  apiVersion: "survey.kontourai.io/v1alpha1";
4
4
  kind: "ReviewItem";
5
5
  metadata: {
@@ -129,7 +129,7 @@ export declare const publicDirectoryReviewItemFixture: {
129
129
  reviewDecisionName: string;
130
130
  };
131
131
  };
132
- export declare const regulatedRuleConflictReviewItemFixture: {
132
+ export declare const regulatedRuleConflictReviewItemExample: {
133
133
  apiVersion: "survey.kontourai.io/v1alpha1";
134
134
  kind: "ReviewItem";
135
135
  metadata: {
@@ -267,7 +267,7 @@ export declare const regulatedRuleConflictReviewItemFixture: {
267
267
  selectedCandidateId: string;
268
268
  };
269
269
  };
270
- export declare const facilityCredentialReviewItemFixture: {
270
+ export declare const facilityCredentialReviewItemExample: {
271
271
  apiVersion: "survey.kontourai.io/v1alpha1";
272
272
  kind: "ReviewItem";
273
273
  metadata: {
@@ -425,7 +425,7 @@ export declare const facilityCredentialReviewItemFixture: {
425
425
  selectedCandidateId: string;
426
426
  };
427
427
  };
428
- export declare const reviewWorkbenchQueueFixtures: (ReviewItem | {
428
+ export declare const reviewWorkbenchQueueExamples: (ReviewItem | {
429
429
  apiVersion: "survey.kontourai.io/v1alpha1";
430
430
  kind: "ReviewItem";
431
431
  metadata: {
@@ -1,7 +1,7 @@
1
1
  import { reviewResourceApiVersion } from "../review-resource.js";
2
- // Browser-safe mirror of fixtures/public-directory-review-resource.ts.
3
- // scripts/check-review-workbench.cjs fails if this data drifts from the canonical fixture.
4
- export const publicDirectoryReviewItemFixture = {
2
+ // Browser-safe mirror of example-data/public-directory-review-resource.ts.
3
+ // scripts/check-review-workbench.cjs fails if this data drifts from the canonical example.
4
+ export const publicDirectoryReviewItemExample = {
5
5
  apiVersion: reviewResourceApiVersion,
6
6
  kind: "ReviewItem",
7
7
  metadata: {
@@ -131,7 +131,7 @@ export const publicDirectoryReviewItemFixture = {
131
131
  reviewDecisionName: "public-directory-availability-operator-decision",
132
132
  },
133
133
  };
134
- export const regulatedRuleConflictReviewItemFixture = {
134
+ export const regulatedRuleConflictReviewItemExample = {
135
135
  apiVersion: reviewResourceApiVersion,
136
136
  kind: "ReviewItem",
137
137
  metadata: {
@@ -267,7 +267,7 @@ export const regulatedRuleConflictReviewItemFixture = {
267
267
  selectedCandidateId: "regulated-rule-conflict-standard-threshold:candidate:current",
268
268
  },
269
269
  };
270
- export const facilityCredentialReviewItemFixture = {
270
+ export const facilityCredentialReviewItemExample = {
271
271
  apiVersion: reviewResourceApiVersion,
272
272
  kind: "ReviewItem",
273
273
  metadata: {
@@ -424,21 +424,21 @@ export const facilityCredentialReviewItemFixture = {
424
424
  };
425
425
  function queueFixture(name, target, currentValue, proposedValue, candidateSetStatus, feedbackTags) {
426
426
  return {
427
- ...publicDirectoryReviewItemFixture,
427
+ ...publicDirectoryReviewItemExample,
428
428
  metadata: {
429
- ...publicDirectoryReviewItemFixture.metadata,
429
+ ...publicDirectoryReviewItemExample.metadata,
430
430
  name,
431
431
  },
432
432
  spec: {
433
- ...publicDirectoryReviewItemFixture.spec,
433
+ ...publicDirectoryReviewItemExample.spec,
434
434
  target,
435
435
  selectedCandidateId: `${name}:candidate:current`,
436
436
  candidateSetStatus,
437
437
  producerPolicy: {
438
438
  feedbackTags,
439
439
  },
440
- rationale: `Fixture-backed local review queue item for ${target}.`,
441
- candidates: publicDirectoryReviewItemFixture.spec.candidates.map((candidate) => {
440
+ rationale: `Example-backed local review queue item for ${target}.`,
441
+ candidates: publicDirectoryReviewItemExample.spec.candidates.map((candidate) => {
442
442
  const role = candidate.role ?? "candidate";
443
443
  const value = role === "proposed" ? proposedValue : currentValue;
444
444
  return {
@@ -475,17 +475,17 @@ function queueFixture(name, target, currentValue, proposedValue, candidateSetSta
475
475
  }),
476
476
  },
477
477
  status: {
478
- observedCandidateCount: publicDirectoryReviewItemFixture.status?.observedCandidateCount,
478
+ observedCandidateCount: publicDirectoryReviewItemExample.status?.observedCandidateCount,
479
479
  selectedCandidateId: `${name}:candidate:current`,
480
480
  reviewDecisionName: candidateSetStatus === "resolved" ? `${name}-resolved-decision` : undefined,
481
481
  },
482
482
  };
483
483
  }
484
- export const reviewWorkbenchQueueFixtures = [
484
+ export const reviewWorkbenchQueueExamples = [
485
485
  queueFixture("public-directory-hours", "hours", "Weekdays 9am-5pm", "Weekdays 8am-6pm", "needs-review", ["hours-change", "crawler-suggested"]),
486
486
  queueFixture("public-directory-phone", "phoneNumber", "+1-555-0100", "+1-555-0199", "needs-review", ["contact-field", "source-conflict"]),
487
- publicDirectoryReviewItemFixture,
487
+ publicDirectoryReviewItemExample,
488
488
  queueFixture("public-directory-address", "streetAddress", "100 Main Street", "102 Main Street", "needs-review", ["address-change", "producer-escalation-candidate"]),
489
489
  queueFixture("public-directory-license", "licenseStatus", "ACTIVE", "EXPIRED", "escalated", ["licensing", "manual-review-required"]),
490
- regulatedRuleConflictReviewItemFixture,
490
+ regulatedRuleConflictReviewItemExample,
491
491
  ];