@company-semantics/contracts 51.0.0 → 51.2.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 +3 -3
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +67 -4
- package/src/comments/README.md +175 -32
- package/src/comments/__tests__/README.md +60 -1
- package/src/comments/__tests__/anchor-constants.test.ts +161 -0
- package/src/comments/__tests__/create.test.ts +300 -0
- package/src/comments/__tests__/receipt.test.ts +143 -0
- package/src/comments/__tests__/resolve.test.ts +391 -0
- package/src/comments/__tests__/schemas.test.ts +29 -1
- package/src/comments/__tests__/verify.test.ts +262 -0
- package/src/comments/anchor-constants.ts +61 -0
- package/src/comments/anchor.ts +20 -11
- package/src/comments/create.ts +128 -0
- package/src/comments/index.ts +36 -0
- package/src/comments/receipt.ts +146 -0
- package/src/comments/resolve.ts +261 -0
- package/src/comments/schemas.ts +27 -8
- package/src/comments/verify.ts +128 -0
- package/src/execution/__tests__/registry.test.ts +89 -0
- package/src/execution/kinds.ts +21 -1
- package/src/execution/registry.ts +73 -0
- package/src/generated/openapi-routes.ts +1 -0
- package/src/index.ts +73 -0
- package/src/message-parts/__tests__/confirmation.test.ts +3 -0
- package/src/message-parts/confirmation.ts +3 -0
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
getExecutionKindDefinition,
|
|
5
5
|
isValidExecutionKind,
|
|
6
6
|
} from "../registry.js";
|
|
7
|
+
import type { ExecutionKind } from "../kinds.js";
|
|
8
|
+
import { CONFIRMATION_LABELS } from "../../message-parts/confirmation.js";
|
|
7
9
|
|
|
8
10
|
describe("EXECUTION_KINDS golden snapshot", () => {
|
|
9
11
|
it("exact values are frozen", () => {
|
|
@@ -199,10 +201,97 @@ describe("EXECUTION_KINDS golden snapshot", () => {
|
|
|
199
201
|
templateId: "companyMd.ingest",
|
|
200
202
|
},
|
|
201
203
|
},
|
|
204
|
+
"companyMd.commentPass": {
|
|
205
|
+
kind: "companyMd.commentPass",
|
|
206
|
+
domain: "data",
|
|
207
|
+
display: {
|
|
208
|
+
label: "Apply Comment Pass",
|
|
209
|
+
pastTenseLabel: "Comment pass applied",
|
|
210
|
+
icon: "pencil",
|
|
211
|
+
},
|
|
212
|
+
governance: {
|
|
213
|
+
visibility: "user",
|
|
214
|
+
requiresAdmin: false,
|
|
215
|
+
},
|
|
216
|
+
ui: {
|
|
217
|
+
showInAdmin: false,
|
|
218
|
+
showInTimeline: true,
|
|
219
|
+
confirmBeforeRun: true,
|
|
220
|
+
},
|
|
221
|
+
explanation: {
|
|
222
|
+
templateId: "companyMd.commentPass",
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
"semantic.transform": {
|
|
226
|
+
kind: "semantic.transform",
|
|
227
|
+
domain: "organization",
|
|
228
|
+
display: {
|
|
229
|
+
label: "Apply Reporting Transformation",
|
|
230
|
+
pastTenseLabel: "Reporting transformation applied",
|
|
231
|
+
icon: "pencil",
|
|
232
|
+
},
|
|
233
|
+
governance: {
|
|
234
|
+
visibility: "user",
|
|
235
|
+
requiresAdmin: false,
|
|
236
|
+
},
|
|
237
|
+
ui: {
|
|
238
|
+
showInAdmin: false,
|
|
239
|
+
showInTimeline: true,
|
|
240
|
+
confirmBeforeRun: false,
|
|
241
|
+
},
|
|
242
|
+
explanation: {
|
|
243
|
+
templateId: "semantic.transform",
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
"companyMd.importContextDoc": {
|
|
247
|
+
kind: "companyMd.importContextDoc",
|
|
248
|
+
domain: "data",
|
|
249
|
+
display: {
|
|
250
|
+
label: "Import Context Document",
|
|
251
|
+
pastTenseLabel: "Context document imported",
|
|
252
|
+
icon: "pencil",
|
|
253
|
+
},
|
|
254
|
+
governance: {
|
|
255
|
+
visibility: "user",
|
|
256
|
+
requiresAdmin: false,
|
|
257
|
+
},
|
|
258
|
+
ui: {
|
|
259
|
+
showInAdmin: false,
|
|
260
|
+
showInTimeline: true,
|
|
261
|
+
confirmBeforeRun: false,
|
|
262
|
+
},
|
|
263
|
+
explanation: {
|
|
264
|
+
templateId: "companyMd.importContextDoc",
|
|
265
|
+
},
|
|
266
|
+
},
|
|
202
267
|
});
|
|
203
268
|
});
|
|
204
269
|
});
|
|
205
270
|
|
|
271
|
+
describe("ExecutionKind exhaustiveness", () => {
|
|
272
|
+
// `satisfies Record<ExecutionKind, ...>` catches a MISSING key at compile
|
|
273
|
+
// time, but only a runtime set comparison catches an EXTRA one that no union
|
|
274
|
+
// member claims — a Record with a surplus key still satisfies the type.
|
|
275
|
+
it("the registry and the confirmation labels cover exactly the same kinds", () => {
|
|
276
|
+
expect(Object.keys(EXECUTION_KINDS).sort()).toStrictEqual(
|
|
277
|
+
Object.keys(CONFIRMATION_LABELS).sort(),
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("every kind the backend dispatches is a union member, not a call-site cast", () => {
|
|
282
|
+
// These three previously ran while cast to ExecutionKind at their call
|
|
283
|
+
// sites, which left the registry incomplete over what actually executes.
|
|
284
|
+
for (const kind of [
|
|
285
|
+
"companyMd.commentPass",
|
|
286
|
+
"semantic.transform",
|
|
287
|
+
"companyMd.importContextDoc",
|
|
288
|
+
]) {
|
|
289
|
+
expect(isValidExecutionKind(kind)).toBe(true);
|
|
290
|
+
expect(CONFIRMATION_LABELS[kind as ExecutionKind]).toBeTruthy();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
206
295
|
describe("registry structural invariants", () => {
|
|
207
296
|
it("every registry key matches its definition.kind field", () => {
|
|
208
297
|
for (const [key, def] of Object.entries(EXECUTION_KINDS)) {
|
package/src/execution/kinds.ts
CHANGED
|
@@ -18,9 +18,20 @@
|
|
|
18
18
|
* - domain: integration, policy, data, system
|
|
19
19
|
* - verb: action being performed (connect, disconnect, etc.)
|
|
20
20
|
*
|
|
21
|
+
* This union is the authority over what may run: every kind that a backend
|
|
22
|
+
* executor dispatches MUST be a member here. A kind cast in at its call site
|
|
23
|
+
* is invisible to every reader of the registry — ExecutionSummaryService drops
|
|
24
|
+
* such executions from timelines, and the query enums cannot filter for them.
|
|
25
|
+
*
|
|
21
26
|
* New kinds MUST be added to:
|
|
22
27
|
* 1. This union type
|
|
23
28
|
* 2. EXECUTION_KINDS registry in registry.ts
|
|
29
|
+
* 3. CONFIRMATION_LABELS in message-parts/confirmation.ts
|
|
30
|
+
*
|
|
31
|
+
* Both (2) and (3) are exhaustive Records over this union, so omitting either
|
|
32
|
+
* is a typecheck failure rather than a checklist item.
|
|
33
|
+
*
|
|
34
|
+
* @see decisions/ADR-CONTRACTS-128-execution-kind-union-is-exhaustive.md
|
|
24
35
|
*/
|
|
25
36
|
export type ExecutionKind =
|
|
26
37
|
| "integration.connect"
|
|
@@ -31,4 +42,13 @@ export type ExecutionKind =
|
|
|
31
42
|
| "data.scope"
|
|
32
43
|
| "system.cleanup"
|
|
33
44
|
| "member.changeManager"
|
|
34
|
-
| "companyMd.ingest"
|
|
45
|
+
| "companyMd.ingest"
|
|
46
|
+
/** A reviewed comment pass over one company.md doc: validated range edits plus
|
|
47
|
+
* thread dispositions, applied in one transaction after one confirmation. */
|
|
48
|
+
| "companyMd.commentPass"
|
|
49
|
+
/** Already RUNS today, previously cast at the call site. Applies a frozen
|
|
50
|
+
* bundle of fact mutations to the org chart's reporting edges. */
|
|
51
|
+
| "semantic.transform"
|
|
52
|
+
/** Already RUNS today, previously cast at the call site. Creates a context
|
|
53
|
+
* doc from submitted text and links it under a destination doc. */
|
|
54
|
+
| "companyMd.importContextDoc";
|
|
@@ -225,6 +225,79 @@ export const EXECUTION_KINDS = {
|
|
|
225
225
|
templateId: "companyMd.ingest",
|
|
226
226
|
},
|
|
227
227
|
},
|
|
228
|
+
"companyMd.commentPass": {
|
|
229
|
+
kind: "companyMd.commentPass",
|
|
230
|
+
domain: "data",
|
|
231
|
+
display: {
|
|
232
|
+
label: "Apply Comment Pass",
|
|
233
|
+
pastTenseLabel: "Comment pass applied",
|
|
234
|
+
icon: "pencil",
|
|
235
|
+
},
|
|
236
|
+
governance: {
|
|
237
|
+
visibility: "user",
|
|
238
|
+
requiresAdmin: false,
|
|
239
|
+
},
|
|
240
|
+
ui: {
|
|
241
|
+
showInAdmin: false,
|
|
242
|
+
showInTimeline: true,
|
|
243
|
+
// The pass mutates the document body; the reviewer confirms once and the
|
|
244
|
+
// whole bundle of range edits and dispositions lands in one transaction.
|
|
245
|
+
confirmBeforeRun: true,
|
|
246
|
+
},
|
|
247
|
+
explanation: {
|
|
248
|
+
templateId: "companyMd.commentPass",
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
"semantic.transform": {
|
|
252
|
+
kind: "semantic.transform",
|
|
253
|
+
domain: "organization",
|
|
254
|
+
display: {
|
|
255
|
+
label: "Apply Reporting Transformation",
|
|
256
|
+
pastTenseLabel: "Reporting transformation applied",
|
|
257
|
+
icon: "pencil",
|
|
258
|
+
},
|
|
259
|
+
governance: {
|
|
260
|
+
// Authority is decided in-operation by the reporting write seam, not by
|
|
261
|
+
// this flag — the same shape member.changeManager already has.
|
|
262
|
+
visibility: "user",
|
|
263
|
+
requiresAdmin: false,
|
|
264
|
+
// The frozen bundle is applied atomically; reversal is not a single-edge
|
|
265
|
+
// restore, so no kind reverses this one.
|
|
266
|
+
},
|
|
267
|
+
ui: {
|
|
268
|
+
showInAdmin: false,
|
|
269
|
+
showInTimeline: true,
|
|
270
|
+
// The confirmed preview IS the deliberate action — no second confirmation.
|
|
271
|
+
confirmBeforeRun: false,
|
|
272
|
+
},
|
|
273
|
+
explanation: {
|
|
274
|
+
templateId: "semantic.transform",
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
"companyMd.importContextDoc": {
|
|
278
|
+
kind: "companyMd.importContextDoc",
|
|
279
|
+
domain: "data",
|
|
280
|
+
display: {
|
|
281
|
+
label: "Import Context Document",
|
|
282
|
+
pastTenseLabel: "Context document imported",
|
|
283
|
+
icon: "pencil",
|
|
284
|
+
},
|
|
285
|
+
governance: {
|
|
286
|
+
visibility: "user",
|
|
287
|
+
requiresAdmin: false,
|
|
288
|
+
// Reversal is an executor undo window over the created doc, not a
|
|
289
|
+
// separately governed ExecutionKind, so reversibleBy stays unset.
|
|
290
|
+
},
|
|
291
|
+
ui: {
|
|
292
|
+
showInAdmin: false,
|
|
293
|
+
showInTimeline: true,
|
|
294
|
+
// The filled interactive-task form IS the deliberate action.
|
|
295
|
+
confirmBeforeRun: false,
|
|
296
|
+
},
|
|
297
|
+
explanation: {
|
|
298
|
+
templateId: "companyMd.importContextDoc",
|
|
299
|
+
},
|
|
300
|
+
},
|
|
228
301
|
} as const satisfies Record<ExecutionKind, ExecutionKindDefinition>;
|
|
229
302
|
|
|
230
303
|
// =============================================================================
|
|
@@ -26,6 +26,7 @@ export const openApiRoutes = {
|
|
|
26
26
|
'/api/comments/threads/{threadId}/accept-complete': ['POST'],
|
|
27
27
|
'/api/comments/threads/{threadId}/accept-intent': ['POST'],
|
|
28
28
|
'/api/comments/threads/{threadId}/accept-release': ['POST'],
|
|
29
|
+
'/api/comments/threads/{threadId}/anchor-trace': ['POST'],
|
|
29
30
|
'/api/comments/threads/{threadId}/comments': ['POST'],
|
|
30
31
|
'/api/comments/threads/{threadId}/reject': ['POST'],
|
|
31
32
|
'/api/comments/threads/{threadId}/reopen': ['POST'],
|
package/src/index.ts
CHANGED
|
@@ -329,16 +329,89 @@ export type {
|
|
|
329
329
|
// contracts drift on it has NO server-side tripwire and mis-places comments
|
|
330
330
|
// silently. Response vocabulary only — request bodies stay backend-side.
|
|
331
331
|
// See src/comments/README.md.
|
|
332
|
+
//
|
|
333
|
+
// The anchor SIZE BOUNDS are published alongside the shape, because the client
|
|
334
|
+
// that composes an anchor and the schema that accepts it are the two halves of
|
|
335
|
+
// that same driftable agreement: a constructor capturing more context than the
|
|
336
|
+
// boundary accepts loses a comment after it was written. Note ANCHOR_CONTEXT_CHARS
|
|
337
|
+
// (capture) and ANCHOR_AFFIX_MAX_CHARS (accept) differ ON PURPOSE.
|
|
332
338
|
// @see ADR-CONTRACTS-116, ADR-CONT-029
|
|
333
339
|
export {
|
|
340
|
+
ANCHOR_AFFIX_MAX_CHARS,
|
|
341
|
+
ANCHOR_CONTEXT_CHARS,
|
|
334
342
|
COMMENT_ANCHOR_TYPES,
|
|
335
343
|
COMMENT_SUBJECT_TYPES,
|
|
336
344
|
COMMENT_THREAD_KINDS,
|
|
337
345
|
COMMENT_THREAD_STATUSES,
|
|
346
|
+
QUOTE_MAX_CHARS,
|
|
347
|
+
RELATIVE_POSITION_MAX_CHARS,
|
|
338
348
|
SUGGESTION_OPS,
|
|
339
349
|
SUGGESTION_STATUSES,
|
|
340
350
|
} from "./comments/index";
|
|
341
351
|
|
|
352
|
+
// The anchor's INTERPRETATION, published beside its shape (ADR-CONTRACTS-127).
|
|
353
|
+
// The schema said what an anchor looks like; these say what it MEANS. Both
|
|
354
|
+
// halves have to be published for the same reason: the writing client and the
|
|
355
|
+
// resolving client agree with no server-side arbiter between them, so a second
|
|
356
|
+
// description of "where does this anchor land" mis-places comments with nothing
|
|
357
|
+
// anywhere throwing. `resolveAnchorFromText` is total, pure and text-only — the
|
|
358
|
+
// Yjs relative-position rung stays in consumers, which compose it on top.
|
|
359
|
+
export {
|
|
360
|
+
contextAfter,
|
|
361
|
+
contextBefore,
|
|
362
|
+
contextsMatchAt,
|
|
363
|
+
findSoleOccurrence,
|
|
364
|
+
resolveAnchorFromText,
|
|
365
|
+
} from "./comments/index";
|
|
366
|
+
|
|
367
|
+
export type { AnchorResolution, Occurrence } from "./comments/index";
|
|
368
|
+
|
|
369
|
+
// The two questions asked BEFORE the body is written (ADR-CONTRACTS-127).
|
|
370
|
+
// `anchorStillReads` is a SECOND assertion, independent of the ladder — the
|
|
371
|
+
// applier must not trust the finder — and `plannedEdit` derives the mutation
|
|
372
|
+
// from the canonical payload rather than a caller's restatement of it. Both are
|
|
373
|
+
// total functions of their arguments, which is exactly what makes them liftable
|
|
374
|
+
// out of a browser applier: no Y.Text, no clock, no lease.
|
|
375
|
+
export { anchorStillReads, plannedEdit } from "./comments/index";
|
|
376
|
+
|
|
377
|
+
export type { PlannedEdit } from "./comments/index";
|
|
378
|
+
|
|
379
|
+
// The CONSTRUCTION half of the same contract (ADR-CONTRACTS-127). Publishing
|
|
380
|
+
// anchor → text while leaving text → anchor browser-only would be half an
|
|
381
|
+
// extraction: the side that COMPOSES an anchor decides how much context to
|
|
382
|
+
// capture and what counts as a quote, and a second description of that is the
|
|
383
|
+
// same silent-drift hazard the resolver was published to end. Both constructors
|
|
384
|
+
// validate through `CommentAnchorSchema` before returning and refuse — `null` —
|
|
385
|
+
// rather than truncate, because an anchor the route boundary rejects is a
|
|
386
|
+
// comment lost after it was composed. The relative-position pair is NOT
|
|
387
|
+
// produced here; a consumer holding a `Y.Text` merges it onto the result.
|
|
388
|
+
export {
|
|
389
|
+
createTextAnchorFromRange,
|
|
390
|
+
createTextInsertionAnchorAt,
|
|
391
|
+
} from "./comments/index";
|
|
392
|
+
|
|
393
|
+
export type { TextRange } from "./comments/index";
|
|
394
|
+
|
|
395
|
+
// The application receipt — the durable proof that an accepted suggestion's
|
|
396
|
+
// edit ALREADY LANDED IN THE BODY (ADR-CONTRACTS-127). It travels in the same
|
|
397
|
+
// CRDT transaction as the edit, so either both survived or neither did, which
|
|
398
|
+
// is what closes the window between the write and the acknowledgement. The MAP
|
|
399
|
+
// KEY is the load-bearing part: a receipt written under any other name lands in
|
|
400
|
+
// a map nobody reads while the reader watches a map nobody writes, and nothing
|
|
401
|
+
// anywhere throws — the same no-arbiter hazard as the anchor, now that a second
|
|
402
|
+
// implementation is about to write receipts too. Published with a PARSER,
|
|
403
|
+
// because these values arrive from a peer through a CRDT that has no schema of
|
|
404
|
+
// its own: a row that is not three well-typed fields is dropped, never
|
|
405
|
+
// surfaced. The receipt lives BESIDE the body and must never reach the markdown
|
|
406
|
+
// source, an export, or the content hash.
|
|
407
|
+
export {
|
|
408
|
+
SUGGESTION_RECEIPTS_MAP,
|
|
409
|
+
SuggestionReceiptSchema,
|
|
410
|
+
readSuggestionReceipt,
|
|
411
|
+
} from "./comments/index";
|
|
412
|
+
|
|
413
|
+
export type { SuggestionReceipt } from "./comments/index";
|
|
414
|
+
|
|
342
415
|
export {
|
|
343
416
|
CommentAnchorSchema,
|
|
344
417
|
CommentAnchorTypeSchema,
|
|
@@ -121,6 +121,9 @@ describe("CONFIRMATION_LABELS", () => {
|
|
|
121
121
|
"system.cleanup": "Cleanup Connections",
|
|
122
122
|
"member.changeManager": "Change Reporting Manager",
|
|
123
123
|
"companyMd.ingest": "Ingest Company Knowledge",
|
|
124
|
+
"companyMd.commentPass": "Apply Comment Pass",
|
|
125
|
+
"semantic.transform": "Apply Reporting Transformation",
|
|
126
|
+
"companyMd.importContextDoc": "Import Context Document",
|
|
124
127
|
});
|
|
125
128
|
});
|
|
126
129
|
});
|
|
@@ -46,6 +46,9 @@ export const CONFIRMATION_LABELS: Record<ExecutionKind, string> = {
|
|
|
46
46
|
"system.cleanup": "Cleanup Connections",
|
|
47
47
|
"member.changeManager": "Change Reporting Manager",
|
|
48
48
|
"companyMd.ingest": "Ingest Company Knowledge",
|
|
49
|
+
"companyMd.commentPass": "Apply Comment Pass",
|
|
50
|
+
"semantic.transform": "Apply Reporting Transformation",
|
|
51
|
+
"companyMd.importContextDoc": "Import Context Document",
|
|
49
52
|
};
|
|
50
53
|
|
|
51
54
|
/**
|