@company-semantics/contracts 88.1.0 → 89.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "88.1.0",
3
+ "version": "89.0.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = 'edc033bdac67' as const;
3
- export const SPEC_HASH_FULL = 'edc033bdac6767eaf9965214f6dfed6c536d48fb5f1f77a48e1af26533633fe1' as const;
2
+ export const SPEC_HASH = '280a694d540b' as const;
3
+ export const SPEC_HASH_FULL = '280a694d540b48ca0e4f5985e8529cc5a88f96efca70980012b011cc914cc08c' as const;
@@ -3834,6 +3834,80 @@ export interface paths {
3834
3834
  patch?: never;
3835
3835
  trace?: never;
3836
3836
  };
3837
+ "/api/factory/quality/changes": {
3838
+ parameters: {
3839
+ query?: never;
3840
+ header?: never;
3841
+ path?: never;
3842
+ cookie?: never;
3843
+ };
3844
+ get?: never;
3845
+ put?: never;
3846
+ /**
3847
+ * Report a pull request head and get the evaluation plan to run for it
3848
+ * @description Resolves the changed files to an impact set, records the change and plans its evaluation. Idempotent: the same head reported again answers the same plan. A change no impact rule matches answers { noImpact: true } and records nothing.
3849
+ */
3850
+ post: operations["createQualityChange"];
3851
+ delete?: never;
3852
+ options?: never;
3853
+ head?: never;
3854
+ patch?: never;
3855
+ trace?: never;
3856
+ };
3857
+ "/api/factory/quality/plans/{planId}": {
3858
+ parameters: {
3859
+ query?: never;
3860
+ header?: never;
3861
+ path?: never;
3862
+ cookie?: never;
3863
+ };
3864
+ /** Read an evaluation plan: suites, pinned manifests, samples, model profile, budget */
3865
+ get: operations["getQualityPlan"];
3866
+ put?: never;
3867
+ post?: never;
3868
+ delete?: never;
3869
+ options?: never;
3870
+ head?: never;
3871
+ patch?: never;
3872
+ trace?: never;
3873
+ };
3874
+ "/api/factory/quality/plans/{planId}/decision": {
3875
+ parameters: {
3876
+ query?: never;
3877
+ header?: never;
3878
+ path?: never;
3879
+ cookie?: never;
3880
+ };
3881
+ /** Read where a plan's evaluation stands and, once recorded, its decision */
3882
+ get: operations["getQualityPlanDecision"];
3883
+ put?: never;
3884
+ post?: never;
3885
+ delete?: never;
3886
+ options?: never;
3887
+ head?: never;
3888
+ patch?: never;
3889
+ trace?: never;
3890
+ };
3891
+ "/api/factory/quality/deployments": {
3892
+ parameters: {
3893
+ query?: never;
3894
+ header?: never;
3895
+ path?: never;
3896
+ cookie?: never;
3897
+ };
3898
+ get?: never;
3899
+ put?: never;
3900
+ /**
3901
+ * Report one phase of a deploy run so an applied backend deployment is verified
3902
+ * @description An applied phase that names a backend commit pins the release references, records the deployment as a change to verify, plans the verification and asks for its runs. Idempotent: the same deployment reported again answers the same change and plan. Any other report answers 202 { ignored: true } and records nothing. The deployment is never held for its verification.
3903
+ */
3904
+ post: operations["recordQualityDeployment"];
3905
+ delete?: never;
3906
+ options?: never;
3907
+ head?: never;
3908
+ patch?: never;
3909
+ trace?: never;
3910
+ };
3837
3911
  "/api/internal-admin/impersonate/start": {
3838
3912
  parameters: {
3839
3913
  query?: never;
@@ -8255,6 +8329,87 @@ export interface components {
8255
8329
  [key: string]: unknown;
8256
8330
  } | null;
8257
8331
  };
8332
+ CreateQualityChangeResponse: {
8333
+ /** Format: uuid */
8334
+ planId: string;
8335
+ suites: string[];
8336
+ samples: number;
8337
+ budgetUsd: number;
8338
+ modelProfile: {
8339
+ [key: string]: unknown;
8340
+ };
8341
+ } | {
8342
+ /** @constant */
8343
+ noImpact: true;
8344
+ };
8345
+ CreateQualityChangeRequest: {
8346
+ /** @constant */
8347
+ sourceKind: "pull_request";
8348
+ sourceRef: string;
8349
+ gitSha: string;
8350
+ changedFiles: string[];
8351
+ };
8352
+ QualityPlanResponse: {
8353
+ /** Format: uuid */
8354
+ planId: string;
8355
+ suites: string[];
8356
+ samples: number;
8357
+ budgetUsd: number;
8358
+ modelProfile: {
8359
+ [key: string]: unknown;
8360
+ };
8361
+ /** Format: uuid */
8362
+ changeId: string;
8363
+ /** @enum {string} */
8364
+ state: "active" | "superseded";
8365
+ supersededBy: string | null;
8366
+ manifests: {
8367
+ suite: string;
8368
+ manifestId: string;
8369
+ entries: {
8370
+ caseId: string;
8371
+ caseRevision: string;
8372
+ }[] | null;
8373
+ }[];
8374
+ };
8375
+ QualityPlanDecisionResponse: {
8376
+ /** @enum {string} */
8377
+ state: "queued" | "running" | "blocked" | "complete";
8378
+ /** @enum {string} */
8379
+ blockReason?: "budget_exhausted" | "grader_calibration_pending" | "required_run_pending" | "provider_unavailable" | "manifest_unavailable";
8380
+ decision?: {
8381
+ /** @enum {string} */
8382
+ outcome: "promote" | "reject" | "escalate";
8383
+ reasons: {
8384
+ /** @enum {string} */
8385
+ code: "critical_regression" | "invariant_regression" | "protected_case_failed" | "below_floor" | "bucket_budget_exceeded" | "insufficient_improvement" | "cost_regression" | "latency_regression" | "grader_ineligible" | "insufficient_samples" | "flaky_case" | "policy_ambiguous" | "all_gates_passed" | "human_override";
8386
+ detail?: string;
8387
+ }[];
8388
+ };
8389
+ /** Format: uuid */
8390
+ supersededBy?: string;
8391
+ };
8392
+ RecordQualityDeploymentResponse: {
8393
+ /** Format: uuid */
8394
+ changeId: string;
8395
+ /** Format: uuid */
8396
+ planId: string;
8397
+ releaseReferences: number;
8398
+ };
8399
+ QualityDeploymentIgnored: {
8400
+ /** @constant */
8401
+ ignored: true;
8402
+ };
8403
+ RecordQualityDeploymentRequest: {
8404
+ manifestId: string | null;
8405
+ phase: string;
8406
+ shas: {
8407
+ backend?: string;
8408
+ app?: string;
8409
+ edge?: string;
8410
+ } | null;
8411
+ surfaces: string[];
8412
+ };
8258
8413
  ImpersonationSessionResponse: {
8259
8414
  session: {
8260
8415
  impersonationSessionId: string;
@@ -8816,6 +8971,8 @@ export interface components {
8816
8971
  /** @enum {string} */
8817
8972
  classificationState: "unclassified" | "classified";
8818
8973
  sourceClass: ("board_deck" | "board_minutes" | "investor_update" | "financial_brief" | "budget_forecast" | "okr_plan" | "annual_plan" | "all_hands" | "unit_strategy" | "roadmap" | "qbr" | "other" | "decoy") | null;
8974
+ documentType: ("board_deck" | "board_minutes" | "strategy_memo" | "planning_doc" | "okr_doc" | "roadmap" | "operating_review" | "all_hands_deck" | "offsite_notes" | "investor_update" | "pitch_deck" | "product_spec" | "research_analysis" | "general_meeting_notes" | "financial_plan" | "financial_actuals" | "equity_admin_record" | "corporate_formation_record" | "corporate_governance_record" | "government_filing" | "equity_agreement" | "commercial_agreement" | "employment_agreement" | "transaction_record" | "personal_document" | "social_event_material" | "coursework" | "empty_template" | "image_or_screenshot" | "unknown") | null;
8975
+ documentTypeConfidence: number | null;
8819
8976
  periodLabel: string | null;
8820
8977
  seriesRole: ("current" | "history") | null;
8821
8978
  relevance: number;
@@ -8848,12 +9005,12 @@ export interface components {
8848
9005
  /** @constant */
8849
9006
  kind: "evaluation";
8850
9007
  /** @enum {string} */
8851
- code: "not_evaluated_t1" | "not_evaluated_t2" | "folder_unresolved" | "folder_truncated" | "explored_t1" | "explored_t2";
9008
+ code: "not_evaluated_t1" | "not_evaluated_t2" | "folder_unresolved" | "folder_truncated" | "explored_t1" | "explored_t2" | "peek_insufficient_evidence" | "type_disagreement" | "type_below_confidence" | "execution_failed_t1" | "execution_failed_t2";
8852
9009
  } | {
8853
9010
  /** @constant */
8854
9011
  kind: "model";
8855
9012
  /** @enum {string} */
8856
- code: "title_path_score" | "peek_confirms" | "peek_contradicts";
9013
+ code: "title_path_score" | "peek_confirms" | "peek_contradicts" | "type_policy" | "peek_strategy_evidence_present" | "peek_strategy_evidence_absent";
8857
9014
  score: number | null;
8858
9015
  })[];
8859
9016
  targetAudience: ("restricted" | "unit" | "org_wide") | null;
@@ -8897,6 +9054,20 @@ export interface components {
8897
9054
  text: string;
8898
9055
  detail?: string;
8899
9056
  };
9057
+ rows?: {
9058
+ rowId: string;
9059
+ label: string;
9060
+ note?: string;
9061
+ evidence: {
9062
+ label: string;
9063
+ value: string;
9064
+ withheld: boolean;
9065
+ }[];
9066
+ selectedChoiceId: string | null;
9067
+ recommendedChoiceId: string | null;
9068
+ allowedChoiceIds: string[];
9069
+ required: boolean;
9070
+ }[];
8900
9071
  }[];
8901
9072
  withheldQuestionCount: number;
8902
9073
  totals: {
@@ -9023,6 +9194,20 @@ export interface components {
9023
9194
  text: string;
9024
9195
  detail?: string;
9025
9196
  };
9197
+ rows?: {
9198
+ rowId: string;
9199
+ label: string;
9200
+ note?: string;
9201
+ evidence: {
9202
+ label: string;
9203
+ value: string;
9204
+ withheld: boolean;
9205
+ }[];
9206
+ selectedChoiceId: string | null;
9207
+ recommendedChoiceId: string | null;
9208
+ allowedChoiceIds: string[];
9209
+ required: boolean;
9210
+ }[];
9026
9211
  }[];
9027
9212
  withheldQuestionCount: number;
9028
9213
  changes: {
@@ -16423,6 +16608,184 @@ export interface operations {
16423
16608
  };
16424
16609
  };
16425
16610
  };
16611
+ createQualityChange: {
16612
+ parameters: {
16613
+ query?: never;
16614
+ header?: never;
16615
+ path?: never;
16616
+ cookie?: never;
16617
+ };
16618
+ requestBody: {
16619
+ content: {
16620
+ "application/json": components["schemas"]["CreateQualityChangeRequest"];
16621
+ };
16622
+ };
16623
+ responses: {
16624
+ /** @description The plan to run (planId, suites, samples, budgetUsd, modelProfile), or noImpact */
16625
+ 200: {
16626
+ headers: {
16627
+ [name: string]: unknown;
16628
+ };
16629
+ content: {
16630
+ "application/json": components["schemas"]["CreateQualityChangeResponse"];
16631
+ };
16632
+ };
16633
+ /** @description Payload invalid */
16634
+ 400: {
16635
+ headers: {
16636
+ [name: string]: unknown;
16637
+ };
16638
+ content?: never;
16639
+ };
16640
+ /** @description Missing or invalid Floor bearer token */
16641
+ 401: {
16642
+ headers: {
16643
+ [name: string]: unknown;
16644
+ };
16645
+ content?: never;
16646
+ };
16647
+ /** @description The change was replaced; its replacement is what is planned */
16648
+ 409: {
16649
+ headers: {
16650
+ [name: string]: unknown;
16651
+ };
16652
+ content?: never;
16653
+ };
16654
+ };
16655
+ };
16656
+ getQualityPlan: {
16657
+ parameters: {
16658
+ query?: never;
16659
+ header?: never;
16660
+ path: {
16661
+ planId: string;
16662
+ };
16663
+ cookie?: never;
16664
+ };
16665
+ requestBody?: never;
16666
+ responses: {
16667
+ /** @description The plan as a runner needs it */
16668
+ 200: {
16669
+ headers: {
16670
+ [name: string]: unknown;
16671
+ };
16672
+ content: {
16673
+ "application/json": components["schemas"]["QualityPlanResponse"];
16674
+ };
16675
+ };
16676
+ /** @description Payload invalid */
16677
+ 400: {
16678
+ headers: {
16679
+ [name: string]: unknown;
16680
+ };
16681
+ content?: never;
16682
+ };
16683
+ /** @description Missing or invalid Floor bearer token */
16684
+ 401: {
16685
+ headers: {
16686
+ [name: string]: unknown;
16687
+ };
16688
+ content?: never;
16689
+ };
16690
+ /** @description No such plan */
16691
+ 404: {
16692
+ headers: {
16693
+ [name: string]: unknown;
16694
+ };
16695
+ content?: never;
16696
+ };
16697
+ };
16698
+ };
16699
+ getQualityPlanDecision: {
16700
+ parameters: {
16701
+ query?: never;
16702
+ header?: never;
16703
+ path: {
16704
+ planId: string;
16705
+ };
16706
+ cookie?: never;
16707
+ };
16708
+ requestBody?: never;
16709
+ responses: {
16710
+ /** @description Evaluation state, block reason while blocked, and the decision once decided */
16711
+ 200: {
16712
+ headers: {
16713
+ [name: string]: unknown;
16714
+ };
16715
+ content: {
16716
+ "application/json": components["schemas"]["QualityPlanDecisionResponse"];
16717
+ };
16718
+ };
16719
+ /** @description Payload invalid */
16720
+ 400: {
16721
+ headers: {
16722
+ [name: string]: unknown;
16723
+ };
16724
+ content?: never;
16725
+ };
16726
+ /** @description Missing or invalid Floor bearer token */
16727
+ 401: {
16728
+ headers: {
16729
+ [name: string]: unknown;
16730
+ };
16731
+ content?: never;
16732
+ };
16733
+ /** @description No such plan */
16734
+ 404: {
16735
+ headers: {
16736
+ [name: string]: unknown;
16737
+ };
16738
+ content?: never;
16739
+ };
16740
+ };
16741
+ };
16742
+ recordQualityDeployment: {
16743
+ parameters: {
16744
+ query?: never;
16745
+ header?: never;
16746
+ path?: never;
16747
+ cookie?: never;
16748
+ };
16749
+ requestBody: {
16750
+ content: {
16751
+ "application/json": components["schemas"]["RecordQualityDeploymentRequest"];
16752
+ };
16753
+ };
16754
+ responses: {
16755
+ /** @description The change that verifies the deployment, its plan, and the release references pinned */
16756
+ 200: {
16757
+ headers: {
16758
+ [name: string]: unknown;
16759
+ };
16760
+ content: {
16761
+ "application/json": components["schemas"]["RecordQualityDeploymentResponse"];
16762
+ };
16763
+ };
16764
+ /** @description Not an applied backend deployment: nothing was recorded */
16765
+ 202: {
16766
+ headers: {
16767
+ [name: string]: unknown;
16768
+ };
16769
+ content: {
16770
+ "application/json": components["schemas"]["QualityDeploymentIgnored"];
16771
+ };
16772
+ };
16773
+ /** @description Payload invalid */
16774
+ 400: {
16775
+ headers: {
16776
+ [name: string]: unknown;
16777
+ };
16778
+ content?: never;
16779
+ };
16780
+ /** @description Missing or invalid Floor bearer token */
16781
+ 401: {
16782
+ headers: {
16783
+ [name: string]: unknown;
16784
+ };
16785
+ content?: never;
16786
+ };
16787
+ };
16788
+ };
16426
16789
  startImpersonation: {
16427
16790
  parameters: {
16428
16791
  query?: never;
@@ -69,6 +69,11 @@ decision-effect-is-a-domain-parameter.
69
69
  effect and no apply-protocol address; `questionId` is its only id. Its `kind`
70
70
  is a bounded open string because each domain has its own kinds. Promoted
71
71
  because the backend renders it and the app consumes it.
72
+ - **A matrix keeps its state on rows.** A presentation with `rows` is a matrix:
73
+ each row is one subject answered with one of `choices` (the columns). Columns
74
+ carry no `selected`/`recommended`; each row holds its own, within its
75
+ `allowedChoiceIds`. One answer per row, no free text. Enforced by
76
+ `matrixPresentationDefect` inside the schema (ADR-CONTRACTS-213).
72
77
 
73
78
  ## Measured evidence is optional
74
79
 
@@ -11,6 +11,7 @@ import { describe, expect, it } from "vitest";
11
11
  import {
12
12
  DecisionChoicePresentationSchema,
13
13
  DecisionQuestionPresentationSchema,
14
+ matrixPresentationDefect,
14
15
  } from "../index";
15
16
 
16
17
  function presentedQuestion(overrides: Record<string, unknown> = {}) {
@@ -110,3 +111,155 @@ describe("DecisionQuestionPresentationSchema", () => {
110
111
  );
111
112
  });
112
113
  });
114
+
115
+ /**
116
+ * A MATRIX question: one subject per row, each answered with one of the
117
+ * question's choices. Rows carry the state; the columns carry none.
118
+ */
119
+ function matrixQuestion(overrides: Record<string, unknown> = {}) {
120
+ return presentedQuestion({
121
+ questionId: "treatment_matrix:s1",
122
+ kind: "treatment_matrix",
123
+ header: "Strategy memo",
124
+ question:
125
+ "Plan was found relevant to 2 places. How should it be used in each?",
126
+ choices: [
127
+ {
128
+ choiceId: "attached",
129
+ label: "Attach",
130
+ recommended: false,
131
+ selected: false,
132
+ },
133
+ {
134
+ choiceId: "direction_only",
135
+ label: "Direction only",
136
+ recommended: false,
137
+ selected: false,
138
+ },
139
+ {
140
+ choiceId: "excluded",
141
+ label: "Leave out",
142
+ recommended: false,
143
+ selected: false,
144
+ },
145
+ ],
146
+ multiSelect: false,
147
+ freeFormAllowed: false,
148
+ recommendation: { strength: "confident" },
149
+ rows: [
150
+ {
151
+ rowId: "item-1",
152
+ label: "Support",
153
+ note: "Shared more narrowly than this doc",
154
+ evidence: [],
155
+ selectedChoiceId: null,
156
+ recommendedChoiceId: "direction_only",
157
+ allowedChoiceIds: ["attached", "direction_only", "excluded"],
158
+ required: true,
159
+ },
160
+ {
161
+ rowId: "item-2",
162
+ label: "Engineering",
163
+ evidence: [
164
+ { label: "Why it was found", value: "Title search", withheld: false },
165
+ ],
166
+ selectedChoiceId: "attached",
167
+ recommendedChoiceId: null,
168
+ allowedChoiceIds: ["attached", "direction_only", "excluded"],
169
+ required: false,
170
+ },
171
+ ],
172
+ ...overrides,
173
+ });
174
+ }
175
+
176
+ describe("DecisionQuestionPresentationSchema — matrix", () => {
177
+ it("accepts a matrix whose rows hold their own selection and recommendation", () => {
178
+ const parsed =
179
+ DecisionQuestionPresentationSchema.safeParse(matrixQuestion());
180
+ expect(parsed.success).toBe(true);
181
+ expect(parsed.data?.rows).toHaveLength(2);
182
+ });
183
+
184
+ it("leaves a question without rows untouched", () => {
185
+ expect(
186
+ matrixPresentationDefect(
187
+ DecisionQuestionPresentationSchema.parse(presentedQuestion()),
188
+ ),
189
+ ).toBeNull();
190
+ });
191
+
192
+ it.each([
193
+ ["matrix_multi_select", { multiSelect: true }],
194
+ ["matrix_free_form", { freeFormAllowed: true }],
195
+ ["matrix_recommendation_mismatch", { recommendation: null }],
196
+ ])("refuses %s", (defect, overrides) => {
197
+ const parsed = DecisionQuestionPresentationSchema.safeParse(
198
+ matrixQuestion(overrides),
199
+ );
200
+ expect(parsed.success).toBe(false);
201
+ expect(parsed.error?.issues[0]?.message).toBe(defect);
202
+ expect(parsed.error?.issues[0]?.path).toEqual(["rows"]);
203
+ });
204
+
205
+ it("refuses state on a column", () => {
206
+ const base = matrixQuestion();
207
+ const choices = base.choices.map((c, i) =>
208
+ i === 0 ? { ...c, selected: true } : c,
209
+ );
210
+ const parsed = DecisionQuestionPresentationSchema.safeParse({
211
+ ...base,
212
+ choices,
213
+ });
214
+ expect(parsed.error?.issues[0]?.message).toBe("matrix_column_state");
215
+ });
216
+
217
+ it("refuses two rows with one id", () => {
218
+ const base = matrixQuestion();
219
+ const rows = [base.rows[0], { ...base.rows[1], rowId: "item-1" }];
220
+ const parsed = DecisionQuestionPresentationSchema.safeParse({
221
+ ...base,
222
+ rows,
223
+ });
224
+ expect(parsed.error?.issues[0]?.message).toBe("matrix_duplicate_row");
225
+ });
226
+
227
+ it("refuses a row allowing a choice that is not a column", () => {
228
+ const base = matrixQuestion();
229
+ const rows = [
230
+ { ...base.rows[0], allowedChoiceIds: ["attached", "archived"] },
231
+ base.rows[1],
232
+ ];
233
+ const parsed = DecisionQuestionPresentationSchema.safeParse({
234
+ ...base,
235
+ rows,
236
+ });
237
+ expect(parsed.error?.issues[0]?.message).toBe("matrix_unknown_choice");
238
+ });
239
+
240
+ it("refuses a selection or recommendation the row does not allow", () => {
241
+ const base = matrixQuestion();
242
+ const narrowed = { ...base.rows[1], allowedChoiceIds: ["excluded"] };
243
+ const selected = DecisionQuestionPresentationSchema.safeParse({
244
+ ...base,
245
+ rows: [base.rows[0], narrowed],
246
+ });
247
+ expect(selected.error?.issues[0]?.message).toBe(
248
+ "matrix_selection_not_allowed",
249
+ );
250
+ const recommended = DecisionQuestionPresentationSchema.safeParse({
251
+ ...base,
252
+ rows: [{ ...base.rows[0], allowedChoiceIds: ["excluded"] }, base.rows[1]],
253
+ });
254
+ expect(recommended.error?.issues[0]?.message).toBe(
255
+ "matrix_recommendation_not_allowed",
256
+ );
257
+ });
258
+
259
+ it("refuses an empty row list", () => {
260
+ const parsed = DecisionQuestionPresentationSchema.safeParse(
261
+ matrixQuestion({ rows: [] }),
262
+ );
263
+ expect(parsed.success).toBe(false);
264
+ });
265
+ });
@@ -47,10 +47,15 @@ export {
47
47
  decisionAnswerSchemaOf,
48
48
  } from "./schemas";
49
49
 
50
- export type { DecisionQuestionPresentation } from "./presentation";
50
+ export type {
51
+ DecisionMatrixRowPresentation,
52
+ DecisionQuestionPresentation,
53
+ } from "./presentation";
51
54
 
52
55
  export {
53
56
  DecisionChoicePresentationSchema,
54
57
  DecisionEvidenceRowSchema,
58
+ DecisionMatrixRowPresentationSchema,
55
59
  DecisionQuestionPresentationSchema,
60
+ matrixPresentationDefect,
56
61
  } from "./presentation";