@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.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/dist/example-data/corrected-document-candidates.js +2 -2
  3. package/dist/example-data/public-directory-review-resource.d.ts +2 -2
  4. package/dist/example-data/public-directory-review-resource.js +2 -2
  5. package/dist/example-data/public-field-review.js +2 -2
  6. package/dist/example-data/regulated-document-review-resource.d.ts +2 -2
  7. package/dist/example-data/regulated-document-review-resource.js +2 -2
  8. package/dist/examples/public-field-observation.js +1 -1
  9. package/dist/examples/review-workbench/downstream-public-directory-adapter.js +1 -1
  10. package/dist/src/agent-utterance.d.ts +25 -0
  11. package/dist/src/agent-utterance.js +207 -95
  12. package/dist/src/console/review-console-server.js +3 -7
  13. package/dist/src/field-observation.d.ts +2 -16
  14. package/dist/src/field-observation.js +2 -14
  15. package/dist/src/index.d.ts +1 -1
  16. package/dist/src/inquiry-mapping.d.ts +2 -46
  17. package/dist/src/inquiry-mapping.js +37 -39
  18. package/dist/src/mcp/review-mcp.js +12 -12
  19. package/dist/src/observation-helper.d.ts +47 -1
  20. package/dist/src/observation-helper.js +41 -2
  21. package/dist/src/oversight-metrics.d.ts +2 -2
  22. package/dist/src/oversight-metrics.js +1 -1
  23. package/dist/src/producer-discipline.d.ts +40 -0
  24. package/dist/src/producer-discipline.js +13 -0
  25. package/dist/src/producer-profile.d.ts +134 -0
  26. package/dist/src/producer-profile.js +124 -0
  27. package/dist/src/raw-source.d.ts +18 -0
  28. package/dist/src/raw-source.js +28 -13
  29. package/dist/src/repeated-observation.d.ts +2 -16
  30. package/dist/src/repeated-observation.js +2 -14
  31. package/dist/src/review-proof.d.ts +2 -2
  32. package/dist/src/review-proof.js +2 -2
  33. package/dist/src/review-resource.d.ts +5 -1
  34. package/dist/src/review-workbench/review-workbench-data.d.ts +10 -10
  35. package/dist/src/review-workbench/review-workbench-data.js +6 -6
  36. package/dist/src/review-workbench/review-workbench.js +1 -1
  37. package/dist/src/review-workbench/server-review-session.d.ts +14 -1
  38. package/dist/src/review-workbench/server-review-session.js +16 -0
  39. package/dist/src/schema-mapping.js +32 -38
  40. package/dist/src/source-of-authority-observation.js +6 -9
  41. package/dist/src/to-surface.js +8 -11
  42. package/dist/src/types.d.ts +6 -1
  43. package/dist/src/vocabulary.d.ts +46 -4
  44. package/dist/src/vocabulary.js +29 -3
  45. package/package.json +6 -3
@@ -20,6 +20,7 @@
20
20
  * so that resolveInquiry can resolve across systems with weakest-link
21
21
  * capping.
22
22
  */
23
+ import { AUTO_ACCEPT_ACTOR, AUTO_ACCEPT_WITHIN_COMFORT_ZONE, getProducerProposal, meetsAutoAcceptThreshold, projectProposalsToCandidateSet, } from "./producer-profile.js";
23
24
  import { buildSurveyTrustBundle } from "./to-surface.js";
24
25
  // ---------------------------------------------------------------------------
25
26
  // Canonical pair key
@@ -84,10 +85,7 @@ export async function surveySchemaMapping(context, extractor, options = {}) {
84
85
  const subjectId = mappingSubjectId(first.sourceField, first.targetField);
85
86
  const candidateSetId = `schema-mapping.candidate-set.${pairKey}`;
86
87
  const claimId = `schema-mapping.claim.${pairKey}`;
87
- // Detect conflict: proposals disagree on relation
88
- const relations = new Set(pairProposals.map((p) => p.relation));
89
- const status = relations.size > 1 ? "conflict" : "needs-review";
90
- const candidates = pairProposals.map((proposal) => {
88
+ const candidateSetProposals = pairProposals.map((proposal) => {
91
89
  // Use the source system's RawSource for this extraction
92
90
  const rawSource = sourceById.get(proposal.sourceField.system) ?? rawSources[0];
93
91
  const extractionId = `schema-mapping.extraction.${proposal.id}`;
@@ -120,7 +118,7 @@ export async function surveySchemaMapping(context, extractor, options = {}) {
120
118
  };
121
119
  extractions.push(extraction);
122
120
  return {
123
- id: `schema-mapping.candidate.${proposal.id}`,
121
+ candidateId: `schema-mapping.candidate.${proposal.id}`,
124
122
  extractionId,
125
123
  value: {
126
124
  relation: proposal.relation,
@@ -128,45 +126,41 @@ export async function surveySchemaMapping(context, extractor, options = {}) {
128
126
  conversion: proposal.conversion,
129
127
  },
130
128
  confidence: proposal.confidence,
129
+ equivalenceKey: proposal.relation,
131
130
  metadata: {
132
- schemaMappingProposal: {
133
- proposalId: proposal.id,
134
- sourceField: proposal.sourceField,
135
- targetField: proposal.targetField,
136
- relation: proposal.relation,
137
- conversion: proposal.conversion,
138
- evidence: proposal.evidence,
139
- confidence: proposal.confidence,
140
- rationale: proposal.rationale,
141
- proposedBy: proposal.proposedBy,
142
- proposedAt: proposal.proposedAt,
143
- },
131
+ proposalId: proposal.id,
132
+ sourceField: proposal.sourceField,
133
+ targetField: proposal.targetField,
134
+ relation: proposal.relation,
135
+ conversion: proposal.conversion,
136
+ evidence: proposal.evidence,
137
+ confidence: proposal.confidence,
138
+ rationale: proposal.rationale,
139
+ proposedBy: proposal.proposedBy,
140
+ proposedAt: proposal.proposedAt,
144
141
  },
145
142
  };
146
143
  });
147
- const candidateSet = {
148
- id: candidateSetId,
149
- target: `schema-mapping:${pairKey}`,
150
- candidates,
151
- selectedCandidateId: status !== "conflict" ? candidates[0]?.id : undefined,
152
- status,
153
- rationale: status === "conflict"
154
- ? `Proposals disagree on relation for pair ${pairKey}: ${[...relations].join(", ")}`
155
- : `${candidates.length} proposal(s) agree on relation "${first.relation}" for pair ${pairKey}.`,
156
- metadata: {
144
+ const { candidateSet, candidates } = projectProposalsToCandidateSet(`schema-mapping:${pairKey}`, candidateSetProposals, {
145
+ candidateSetId,
146
+ candidateSetMetadata: {
157
147
  schemaMapping: {
158
148
  pairKey,
159
149
  sourceField: first.sourceField,
160
150
  targetField: first.targetField,
161
151
  },
162
152
  },
163
- };
153
+ candidateSetRationale: (status, proposals) => status === "conflict"
154
+ ? `Proposals disagree on relation for pair ${pairKey}: ${[...new Set(proposals.map((p) => p.equivalenceKey))].join(", ")}`
155
+ : `${proposals.length} proposal(s) agree on relation "${first.relation}" for pair ${pairKey}.`,
156
+ });
157
+ candidateSet.selectedCandidateId = candidateSet.status !== "conflict" ? candidates[0]?.id : undefined;
164
158
  candidateSets.push(candidateSet);
165
159
  // Auto-accept policy: non-conflicting proposals above threshold → assumed
166
160
  let autoReviewStatus;
167
- if (status !== "conflict" && options.autoAcceptMinConfidence !== undefined) {
161
+ if (candidateSet.status !== "conflict" && options.autoAcceptMinConfidence !== undefined) {
168
162
  const topConfidence = Math.max(...pairProposals.map((p) => p.confidence));
169
- if (topConfidence >= options.autoAcceptMinConfidence) {
163
+ if (meetsAutoAcceptThreshold(topConfidence, options.autoAcceptMinConfidence)) {
170
164
  autoReviewStatus = "assumed";
171
165
  }
172
166
  }
@@ -180,10 +174,10 @@ export async function surveySchemaMapping(context, extractor, options = {}) {
180
174
  candidateSetId,
181
175
  candidateId: selectedCandidate.id,
182
176
  status: autoReviewStatus,
183
- actor: "auto-accept-policy",
177
+ actor: AUTO_ACCEPT_ACTOR,
184
178
  reviewedAt: generatedAt,
185
179
  rationale: `Auto-accepted: confidence ${selectedCandidate.confidence} >= threshold ${options.autoAcceptMinConfidence}`,
186
- withinComfortZone: true,
180
+ withinComfortZone: AUTO_ACCEPT_WITHIN_COMFORT_ZONE,
187
181
  });
188
182
  }
189
183
  // Project to ClaimTarget (subjectType "system-field", fieldOrBehavior "maps-to")
@@ -191,14 +185,14 @@ export async function surveySchemaMapping(context, extractor, options = {}) {
191
185
  const review = reviewOutcomes.find((r) => r.candidateSetId === candidateSetId);
192
186
  const claimStatus = review
193
187
  ? review.status
194
- : (status === "conflict" ? "disputed" : undefined);
188
+ : (candidateSet.status === "conflict" ? "disputed" : undefined);
195
189
  const claimTarget = {
196
190
  id: claimId,
197
191
  candidateSetId,
198
192
  candidateId: selectedCandidate.id,
199
193
  subjectType: "system-field",
200
194
  subjectId: subjectId,
201
- surface: "schema-mapping.profile",
195
+ facet: "schema-mapping.profile",
202
196
  claimType: "schema-mapping.field-link",
203
197
  fieldOrBehavior: "maps-to",
204
198
  value: {
@@ -266,7 +260,7 @@ export function mappingReviewToSurface(reviewedMappings, options = {}) {
266
260
  const rawSources = [];
267
261
  const seenSources = new Set();
268
262
  for (const rm of accepted) {
269
- const meta = rm.selectedCandidate.metadata?.schemaMappingProposal;
263
+ const meta = getProducerProposal(rm.selectedCandidate);
270
264
  const sourceField = meta?.sourceField ?? rm.proposal.sourceField;
271
265
  const sourceId = `schema-mapping.source.${sourceField.system}`;
272
266
  if (!seenSources.has(sourceId)) {
@@ -290,7 +284,7 @@ export function mappingReviewToSurface(reviewedMappings, options = {}) {
290
284
  const reviewOutcomes = [];
291
285
  const claims = [];
292
286
  for (const rm of accepted) {
293
- const meta = rm.selectedCandidate.metadata?.schemaMappingProposal;
287
+ const meta = getProducerProposal(rm.selectedCandidate);
294
288
  const sourceField = meta?.sourceField ?? rm.proposal.sourceField;
295
289
  const targetField = meta?.targetField ?? rm.proposal.targetField;
296
290
  const relation = (meta?.relation ?? rm.proposal.relation);
@@ -342,7 +336,7 @@ export function mappingReviewToSurface(reviewedMappings, options = {}) {
342
336
  candidateId: rm.selectedCandidate.id,
343
337
  subjectType: "system-field",
344
338
  subjectId,
345
- surface: "schema-mapping.profile",
339
+ facet: "schema-mapping.profile",
346
340
  claimType: "schema-mapping.field-link",
347
341
  fieldOrBehavior: "maps-to",
348
342
  value: { relation, sourceField, targetField, conversion },
@@ -379,7 +373,7 @@ export function mappingReviewToSurface(reviewedMappings, options = {}) {
379
373
  // subject and back-references the mapping claim via mappingClaimId.
380
374
  const identityLinks = [];
381
375
  for (const rm of accepted) {
382
- const meta = rm.selectedCandidate.metadata?.schemaMappingProposal;
376
+ const meta = getProducerProposal(rm.selectedCandidate);
383
377
  const sourceField = meta?.sourceField ?? rm.proposal.sourceField;
384
378
  const targetField = meta?.targetField ?? rm.proposal.targetField;
385
379
  const relation = (meta?.relation ?? rm.proposal.relation);
@@ -1,4 +1,5 @@
1
1
  import { buildObservation } from "./observation-helper.js";
2
+ import { assertReviewOutcomeDiscipline } from "./producer-discipline.js";
2
3
  export class SourceOfAuthorityObservationBuilder {
3
4
  state;
4
5
  constructor(args) {
@@ -115,15 +116,11 @@ function assertVerifiedPosture(input) {
115
116
  if (!input.extraction.locator) {
116
117
  throw new Error(`Source-of-authority observation ${input.id} cannot be ${status} without a source locator`);
117
118
  }
118
- if (!input.reviewOutcome) {
119
- throw new Error(`Source-of-authority observation ${input.id} cannot be ${status} without a review outcome`);
120
- }
121
- if (!input.reviewOutcome.actor) {
122
- throw new Error(`Source-of-authority observation ${input.id} cannot be ${status} without review actor authority`);
123
- }
124
- if (!input.reviewOutcome.reviewedAt) {
125
- throw new Error(`Source-of-authority observation ${input.id} cannot be ${status} without reviewedAt`);
126
- }
119
+ assertReviewOutcomeDiscipline({
120
+ subject: `Source-of-authority observation ${input.id}`,
121
+ status,
122
+ review: input.reviewOutcome,
123
+ });
127
124
  }
128
125
  function claimStatus(claimStatusValue, reviewStatus) {
129
126
  return claimStatusValue ?? reviewStatus;
@@ -1,4 +1,5 @@
1
1
  import { buildReviewProofAnchor } from "./review-proof.js";
2
+ import { assertReviewOutcomeDiscipline } from "./producer-discipline.js";
2
3
  export function buildSurveyTrustBundle(input, options = {}) {
3
4
  const rawSources = indexById(input.rawSources, "raw source");
4
5
  const extractions = indexById(input.extractions, "extraction");
@@ -23,7 +24,7 @@ export function buildSurveyTrustBundle(input, options = {}) {
23
24
  id: projection.id,
24
25
  subjectType: projection.subjectType,
25
26
  subjectId: projection.subjectId,
26
- surface: projection.surface,
27
+ facet: projection.facet,
27
28
  claimType: projection.claimType,
28
29
  fieldOrBehavior: projection.fieldOrBehavior,
29
30
  value: claimValue,
@@ -120,7 +121,7 @@ export function buildSurveyTrustBundle(input, options = {}) {
120
121
  }
121
122
  }
122
123
  return {
123
- schemaVersion: 3,
124
+ schemaVersion: 5,
124
125
  source: input.source,
125
126
  claims,
126
127
  evidence,
@@ -334,15 +335,11 @@ function statusFor(input) {
334
335
  return "proposed";
335
336
  }
336
337
  function assertProducerDiscipline(input) {
337
- if ((input.status === "verified" || input.status === "assumed") && !input.review) {
338
- throw new Error(`Claim ${input.projection.id} cannot be ${input.status} without a review outcome`);
339
- }
340
- if ((input.status === "verified" || input.status === "assumed") && !input.review?.actor) {
341
- throw new Error(`Claim ${input.projection.id} cannot be ${input.status} without review actor authority`);
342
- }
343
- if ((input.status === "verified" || input.status === "assumed") && !input.review?.reviewedAt) {
344
- throw new Error(`Claim ${input.projection.id} cannot be ${input.status} without reviewedAt`);
345
- }
338
+ assertReviewOutcomeDiscipline({
339
+ subject: `Claim ${input.projection.id}`,
340
+ status: input.status,
341
+ review: input.review,
342
+ });
346
343
  if (input.rawSource.kind !== "manual-entry" && !input.extraction.locator) {
347
344
  throw new Error(`Claim ${input.projection.id} needs a source locator for ${input.rawSource.kind}`);
348
345
  }
@@ -111,7 +111,12 @@ export interface ClaimTarget {
111
111
  candidateId?: string;
112
112
  subjectType: string;
113
113
  subjectId: string;
114
- surface: string;
114
+ /**
115
+ * Producer-defined grouping or namespace for this claim (Hachure schema 5,
116
+ * surface@2.0.0: Claim.surface -> Claim.facet). Projected onto the emitted
117
+ * Claim's `facet` field by {@link buildSurveyTrustBundle}.
118
+ */
119
+ facet: string;
115
120
  claimType: string;
116
121
  fieldOrBehavior: string;
117
122
  value?: unknown;
@@ -11,22 +11,64 @@ import type { ConfidenceBasis, ImpactLevel, TrustStatus } from "@kontourai/surfa
11
11
  */
12
12
  export declare function stableId(parts: ReadonlyArray<string | number>): string;
13
13
  /**
14
- * A product's Survey/Surface vocabulary: the subject type and surface it
14
+ * A product's Survey/Surface vocabulary: the subject type and facet it
15
15
  * projects onto, its claim-type names, and its decision-effect names. Generic
16
16
  * over the caller's claim-type and decision-effect key maps so the concrete
17
17
  * string literals stay visible to callers.
18
+ *
19
+ * `facet` mirrors Surface's `Claim.facet` (Hachure schema 5 facet rename:
20
+ * `Claim.surface` -> `Claim.facet`, surface@2.0.0). The now-deprecated
21
+ * `surface` property is kept, mirroring `facet`, for one release so existing
22
+ * readers of `.surface` do not break; read `.facet` going forward.
18
23
  */
19
24
  export interface ProductVocabularyDefinition<TClaimTypes extends Readonly<Record<string, string>>, TDecisionEffects extends Readonly<Record<string, string>>> {
20
25
  readonly subjectType: string;
26
+ readonly facet: string;
27
+ /**
28
+ * @deprecated Renamed to {@link ProductVocabularyDefinition.facet} (Hachure
29
+ * schema 5 facet rename: `Claim.surface` -> `Claim.facet`). Mirrors `facet`
30
+ * for one release; read `.facet` going forward.
31
+ */
21
32
  readonly surface: string;
22
33
  readonly claimTypes: TClaimTypes;
23
34
  readonly decisionEffects: TDecisionEffects;
24
35
  }
36
+ /**
37
+ * Input accepted by {@link defineProductVocabulary}: `facet` is the
38
+ * canonical name; the deprecated `surface` name is still accepted for one
39
+ * release as a read-compat alias (Hachure schema 5 facet rename). Exactly
40
+ * one of `facet` / `surface` is required — the union below is what makes
41
+ * "at least one of these two" a compile-time requirement rather than a
42
+ * runtime-only check.
43
+ */
44
+ export type ProductVocabularyInput<TClaimTypes extends Readonly<Record<string, string>>, TDecisionEffects extends Readonly<Record<string, string>>> = {
45
+ readonly subjectType: string;
46
+ readonly claimTypes: TClaimTypes;
47
+ readonly decisionEffects: TDecisionEffects;
48
+ } & ({
49
+ readonly facet: string;
50
+ readonly surface?: string;
51
+ } | {
52
+ readonly facet?: undefined;
53
+ /**
54
+ * @deprecated Renamed to `facet` (Hachure schema 5 facet rename:
55
+ * `Claim.surface` -> `Claim.facet`). Accepted for one release; using it
56
+ * without also passing `facet` emits a single deprecation warning per
57
+ * process. Prefer `facet`.
58
+ */
59
+ readonly surface: string;
60
+ });
25
61
  /**
26
62
  * Defines a product vocabulary as a deep-frozen, discoverable value that a
27
63
  * `currentProposedReviewItem` caller can pass instead of loose top-level
28
- * constants. Returns the same shape it received, frozen so callers cannot
29
- * mutate a shared vocabulary at runtime.
64
+ * constants. Returns the same shape it received (plus the mirrored
65
+ * deprecated `surface` alias — see {@link ProductVocabularyDefinition}),
66
+ * frozen so callers cannot mutate a shared vocabulary at runtime.
67
+ *
68
+ * Accepts either the canonical `facet` option or the deprecated `surface`
69
+ * option (not both required — `facet` wins when both are supplied, and
70
+ * using `surface` alone emits a single deprecation warning per process; see
71
+ * {@link ProductVocabularyInput}).
30
72
  *
31
73
  * The type parameters carry the `const` modifier, so `claimTypes` and
32
74
  * `decisionEffects` string properties keep their literal types whether or
@@ -40,7 +82,7 @@ export interface ProductVocabularyDefinition<TClaimTypes extends Readonly<Record
40
82
  * `peerDependencies.typescript` and `docs/upgrade-guide.md`); this is a
41
83
  * types-only floor and does not affect JavaScript consumers.
42
84
  */
43
- export declare function defineProductVocabulary<const TClaimTypes extends Readonly<Record<string, string>>, const TDecisionEffects extends Readonly<Record<string, string>>>(definition: ProductVocabularyDefinition<TClaimTypes, TDecisionEffects>): ProductVocabularyDefinition<TClaimTypes, TDecisionEffects>;
85
+ export declare function defineProductVocabulary<const TClaimTypes extends Readonly<Record<string, string>>, const TDecisionEffects extends Readonly<Record<string, string>>>(definition: ProductVocabularyInput<TClaimTypes, TDecisionEffects>): ProductVocabularyDefinition<TClaimTypes, TDecisionEffects>;
44
86
  export interface ConfidenceBasisForReviewInput {
45
87
  readonly status: TrustStatus;
46
88
  readonly impactLevel: ImpactLevel;
@@ -16,11 +16,35 @@ export function stableId(parts) {
16
16
  .toLowerCase())
17
17
  .join(".");
18
18
  }
19
+ let warnedLegacyVocabularySurfaceOnce = false;
20
+ function warnLegacyVocabularySurfaceOnce() {
21
+ if (warnedLegacyVocabularySurfaceOnce)
22
+ return;
23
+ warnedLegacyVocabularySurfaceOnce = true;
24
+ console.warn("[@kontourai/survey] deprecated: defineProductVocabulary's \"surface\" option is renamed to " +
25
+ "\"facet\" (Hachure schema 5 facet rename: Claim.surface -> Claim.facet, surface@2.0.0). " +
26
+ "Pass \"facet\" instead of \"surface\"; \"surface\" is accepted for one release as a read-compat alias.");
27
+ }
28
+ function resolveFacet(definition) {
29
+ if (definition.facet !== undefined)
30
+ return definition.facet;
31
+ if (definition.surface !== undefined) {
32
+ warnLegacyVocabularySurfaceOnce();
33
+ return definition.surface;
34
+ }
35
+ throw new Error('defineProductVocabulary requires "facet" (or deprecated "surface")');
36
+ }
19
37
  /**
20
38
  * Defines a product vocabulary as a deep-frozen, discoverable value that a
21
39
  * `currentProposedReviewItem` caller can pass instead of loose top-level
22
- * constants. Returns the same shape it received, frozen so callers cannot
23
- * mutate a shared vocabulary at runtime.
40
+ * constants. Returns the same shape it received (plus the mirrored
41
+ * deprecated `surface` alias — see {@link ProductVocabularyDefinition}),
42
+ * frozen so callers cannot mutate a shared vocabulary at runtime.
43
+ *
44
+ * Accepts either the canonical `facet` option or the deprecated `surface`
45
+ * option (not both required — `facet` wins when both are supplied, and
46
+ * using `surface` alone emits a single deprecation warning per process; see
47
+ * {@link ProductVocabularyInput}).
24
48
  *
25
49
  * The type parameters carry the `const` modifier, so `claimTypes` and
26
50
  * `decisionEffects` string properties keep their literal types whether or
@@ -35,9 +59,11 @@ export function stableId(parts) {
35
59
  * types-only floor and does not affect JavaScript consumers.
36
60
  */
37
61
  export function defineProductVocabulary(definition) {
62
+ const facet = resolveFacet(definition);
38
63
  return deepFreeze({
39
64
  subjectType: definition.subjectType,
40
- surface: definition.surface,
65
+ facet,
66
+ surface: facet,
41
67
  claimTypes: { ...definition.claimTypes },
42
68
  decisionEffects: { ...definition.decisionEffects },
43
69
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontourai/survey",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Producer-side source, extraction, candidate, and review contracts for projecting verified claims into Surface.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -55,8 +55,11 @@
55
55
  "typecheck": "tsc --noEmit",
56
56
  "test": "npm run build && node --test dist/tests/*.test.js",
57
57
  "test:browser": "playwright test",
58
- "verify": "node scripts/check-content-boundary.cjs && npm run typecheck && npm test && npm run check:review-workbench-assets && npm run check:review-workbench && npm run test:browser",
58
+ "verify": "node scripts/check-content-boundary.cjs && npm run check:decisions && npm run typecheck && npm test && npm run check:review-workbench-assets && npm run check:review-workbench && npm run test:browser",
59
59
  "check:content-boundary": "node scripts/check-content-boundary.cjs",
60
+ "check:decisions": "node scripts/check-decisions.cjs check",
61
+ "gen:decisions-index": "node scripts/check-decisions.cjs gen-index",
62
+ "freeze:adrs": "node scripts/freeze-adrs.mjs",
60
63
  "sync:review-workbench-assets": "node scripts/sync-review-workbench-assets.cjs",
61
64
  "check:review-workbench-assets": "node scripts/sync-review-workbench-assets.cjs --check",
62
65
  "check:review-workbench": "npm run check:review-workbench:static && npm run check:generated-css",
@@ -68,7 +71,7 @@
68
71
  "check:generated-css": "node scripts/copy-review-workbench-package-assets.cjs --check"
69
72
  },
70
73
  "dependencies": {
71
- "@kontourai/surface": "^1.3.0"
74
+ "@kontourai/surface": "^2.0.0"
72
75
  },
73
76
  "peerDependencies": {
74
77
  "@anthropic-ai/sdk": ">=0.20.0",