@company-semantics/contracts 51.0.0 → 51.1.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": "51.
|
|
3
|
+
"version": "51.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -148,9 +148,9 @@
|
|
|
148
148
|
"markdownlint-cli2": "^0.23.2",
|
|
149
149
|
"openapi-typescript": "^7.13.0",
|
|
150
150
|
"prettier": "^3.9.6",
|
|
151
|
-
"tsx": "^4.23.
|
|
151
|
+
"tsx": "^4.23.12",
|
|
152
152
|
"typescript": "^5.8.3",
|
|
153
|
-
"typescript-eslint": "^8.
|
|
153
|
+
"typescript-eslint": "^8.67.0",
|
|
154
154
|
"vite": "^8.2.1",
|
|
155
155
|
"vitest": "^4.1.10",
|
|
156
156
|
"yaml": "^2.9.0"
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '544058ec2d0f' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '544058ec2d0ffc4b77cd02a9121088dadc5618d5bc04ed33707298a8a1ce6114' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -3565,6 +3565,23 @@ export interface paths {
|
|
|
3565
3565
|
patch?: never;
|
|
3566
3566
|
trace?: never;
|
|
3567
3567
|
};
|
|
3568
|
+
"/api/comments/threads/{threadId}/anchor-trace": {
|
|
3569
|
+
parameters: {
|
|
3570
|
+
query?: never;
|
|
3571
|
+
header?: never;
|
|
3572
|
+
path?: never;
|
|
3573
|
+
cookie?: never;
|
|
3574
|
+
};
|
|
3575
|
+
get?: never;
|
|
3576
|
+
put?: never;
|
|
3577
|
+
/** Report where a thread's anchored passage used to be */
|
|
3578
|
+
post: operations["stampCommentAnchorTrace"];
|
|
3579
|
+
delete?: never;
|
|
3580
|
+
options?: never;
|
|
3581
|
+
head?: never;
|
|
3582
|
+
patch?: never;
|
|
3583
|
+
trace?: never;
|
|
3584
|
+
};
|
|
3568
3585
|
"/api/comments/threads/{threadId}/accept-intent": {
|
|
3569
3586
|
parameters: {
|
|
3570
3587
|
query?: never;
|
|
@@ -6514,6 +6531,9 @@ export interface components {
|
|
|
6514
6531
|
CommentThreadSummaryResponse: {
|
|
6515
6532
|
thread: components["schemas"]["CommentThreadSummary"];
|
|
6516
6533
|
};
|
|
6534
|
+
AnchorTraceRequest: {
|
|
6535
|
+
lastAnchoredOffset: number;
|
|
6536
|
+
};
|
|
6517
6537
|
/** @description The acceptance lease: the opaque claim token that alone can complete the accept, and how long the lease lives. */
|
|
6518
6538
|
SuggestionClaimResponse: {
|
|
6519
6539
|
claimToken: string;
|
|
@@ -6550,12 +6570,13 @@ export interface components {
|
|
|
6550
6570
|
/** @enum {string} */
|
|
6551
6571
|
category: "version" | "source" | "lifecycle" | "access" | "sync";
|
|
6552
6572
|
/** @enum {string} */
|
|
6553
|
-
eventType: "doc_created" | "source_added" | "source_removed" | "content_generated" | "external_sync_completed" | "access_granted" | "access_revoked" | "access_updated" | "sharing_policy_changed" | "visibility_changed" | "owner_transferred" | "discoverability_changed";
|
|
6573
|
+
eventType: "doc_created" | "doc_renamed" | "doc_moved" | "source_added" | "source_attached" | "source_detached" | "source_removed" | "source_extraction_started" | "source_extraction_succeeded" | "source_extraction_failed" | "content_generated" | "external_sync_completed" | "access_granted" | "access_revoked" | "access_updated" | "sharing_policy_changed" | "visibility_changed" | "owner_transferred" | "discoverability_changed";
|
|
6554
6574
|
actor: components["schemas"]["CompanyMdDocVersionActor"] | null;
|
|
6555
6575
|
subject: string | null;
|
|
6556
6576
|
target: components["schemas"]["CompanyMdDocHistoryTarget"] | null;
|
|
6557
6577
|
previousValue: string | null;
|
|
6558
6578
|
newValue: string | null;
|
|
6579
|
+
correlationId: string | null;
|
|
6559
6580
|
};
|
|
6560
6581
|
/** @description The singular actor a version is attributed to, in product vocabulary. */
|
|
6561
6582
|
CompanyMdDocVersionActor: {
|
|
@@ -6612,6 +6633,7 @@ export interface components {
|
|
|
6612
6633
|
createdByUserId: string | null;
|
|
6613
6634
|
resolvedByUserId: string | null;
|
|
6614
6635
|
resolvedAt: string | null;
|
|
6636
|
+
lastAnchoredOffset: number | null;
|
|
6615
6637
|
createdAt: string;
|
|
6616
6638
|
updatedAt: string;
|
|
6617
6639
|
comments: components["schemas"]["Comment"][];
|
|
@@ -6686,6 +6708,7 @@ export interface components {
|
|
|
6686
6708
|
createdByUserId: string | null;
|
|
6687
6709
|
resolvedByUserId: string | null;
|
|
6688
6710
|
resolvedAt: string | null;
|
|
6711
|
+
lastAnchoredOffset: number | null;
|
|
6689
6712
|
createdAt: string;
|
|
6690
6713
|
updatedAt: string;
|
|
6691
6714
|
};
|
|
@@ -12833,6 +12856,46 @@ export interface operations {
|
|
|
12833
12856
|
};
|
|
12834
12857
|
};
|
|
12835
12858
|
};
|
|
12859
|
+
stampCommentAnchorTrace: {
|
|
12860
|
+
parameters: {
|
|
12861
|
+
query?: never;
|
|
12862
|
+
header?: never;
|
|
12863
|
+
path: {
|
|
12864
|
+
threadId: string;
|
|
12865
|
+
};
|
|
12866
|
+
cookie?: never;
|
|
12867
|
+
};
|
|
12868
|
+
requestBody: {
|
|
12869
|
+
content: {
|
|
12870
|
+
"application/json": components["schemas"]["AnchorTraceRequest"];
|
|
12871
|
+
};
|
|
12872
|
+
};
|
|
12873
|
+
responses: {
|
|
12874
|
+
/** @description Offset recorded, or the already-recorded thread returned unchanged (first write wins) */
|
|
12875
|
+
200: {
|
|
12876
|
+
headers: {
|
|
12877
|
+
[name: string]: unknown;
|
|
12878
|
+
};
|
|
12879
|
+
content: {
|
|
12880
|
+
"application/json": components["schemas"]["CommentThreadSummaryResponse"];
|
|
12881
|
+
};
|
|
12882
|
+
};
|
|
12883
|
+
/** @description Invalid body */
|
|
12884
|
+
400: {
|
|
12885
|
+
headers: {
|
|
12886
|
+
[name: string]: unknown;
|
|
12887
|
+
};
|
|
12888
|
+
content?: never;
|
|
12889
|
+
};
|
|
12890
|
+
/** @description Thread not found, or the caller may not comment on its subject */
|
|
12891
|
+
404: {
|
|
12892
|
+
headers: {
|
|
12893
|
+
[name: string]: unknown;
|
|
12894
|
+
};
|
|
12895
|
+
content?: never;
|
|
12896
|
+
};
|
|
12897
|
+
};
|
|
12898
|
+
};
|
|
12836
12899
|
acceptSuggestionIntent: {
|
|
12837
12900
|
parameters: {
|
|
12838
12901
|
query?: never;
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
CommentThreadListResponseSchema,
|
|
18
18
|
CommentThreadSchema,
|
|
19
19
|
CommentThreadStatusSchema,
|
|
20
|
+
CommentThreadSummarySchema,
|
|
20
21
|
MentionableResponseSchema,
|
|
21
22
|
} from "../schemas.js";
|
|
22
23
|
|
|
@@ -68,6 +69,7 @@ function makeThread(over: Record<string, unknown> = {}) {
|
|
|
68
69
|
createdByUserId: USER_ID,
|
|
69
70
|
resolvedByUserId: null,
|
|
70
71
|
resolvedAt: null,
|
|
72
|
+
lastAnchoredOffset: null,
|
|
71
73
|
createdAt: "2026-08-01T12:00:00.000Z",
|
|
72
74
|
updatedAt: "2026-08-01T12:00:00.000Z",
|
|
73
75
|
comments: [makeComment()],
|
|
@@ -224,7 +226,13 @@ describe("CommentThreadSchema", () => {
|
|
|
224
226
|
).toBe(false);
|
|
225
227
|
});
|
|
226
228
|
|
|
227
|
-
it("
|
|
229
|
+
it("parses a redacted comment inside a thread — the schema still admits one", () => {
|
|
230
|
+
// This test once asserted that a reader SEES tombstones in a conversation.
|
|
231
|
+
// ADR-BE-545 ended that: the list read omits deleted comments outright. What
|
|
232
|
+
// survives is the SHAPE — the redacted projection is still a legal
|
|
233
|
+
// `CommentProjection`, because it is what the DELETE acknowledgement returns
|
|
234
|
+
// to the caller who performed the delete. So this pins the parse, not a read
|
|
235
|
+
// behaviour the server no longer has.
|
|
228
236
|
const thread = CommentThreadSchema.parse(
|
|
229
237
|
makeThread({
|
|
230
238
|
comments: [
|
|
@@ -243,6 +251,26 @@ describe("CommentThreadSchema", () => {
|
|
|
243
251
|
});
|
|
244
252
|
|
|
245
253
|
describe("CommentThreadSummarySchema kind and suggestion", () => {
|
|
254
|
+
it("carries a lastAnchoredOffset, and null is the ordinary answer", () => {
|
|
255
|
+
// An ORDERING HINT and nothing else (ADR-BE-548): where an orphaned thread's
|
|
256
|
+
// passage used to be, so a client can keep the card in the reader's list
|
|
257
|
+
// rather than piling every orphan at the top. It must never yield a range.
|
|
258
|
+
expect(
|
|
259
|
+
CommentThreadSummarySchema.parse(makeThread()).lastAnchoredOffset,
|
|
260
|
+
"a thread nobody has watched lose its passage carries null",
|
|
261
|
+
).toBeNull();
|
|
262
|
+
expect(
|
|
263
|
+
CommentThreadSummarySchema.parse(makeThread({ lastAnchoredOffset: 412 }))
|
|
264
|
+
.lastAnchoredOffset,
|
|
265
|
+
).toBe(412);
|
|
266
|
+
expect(
|
|
267
|
+
CommentThreadSummarySchema.safeParse(
|
|
268
|
+
makeThread({ lastAnchoredOffset: 3.5 }),
|
|
269
|
+
).success,
|
|
270
|
+
"a fractional offset is not a source position",
|
|
271
|
+
).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
|
|
246
274
|
it("a zero-comment suggestion thread parses through both response shapes", () => {
|
|
247
275
|
// The full suggestion projection: kind `suggestion`, a v1 payload, a
|
|
248
276
|
// zero-width text-insertion anchor, and NO comments. The backend creates a
|
package/src/comments/schemas.ts
CHANGED
|
@@ -110,11 +110,14 @@ export type CommentMention = z.infer<typeof CommentMentionSchema>;
|
|
|
110
110
|
* One comment as the app renders it.
|
|
111
111
|
*
|
|
112
112
|
* THE REDACTION INVARIANT: `body` is null EXACTLY WHEN `deletedAt` is set, and a
|
|
113
|
-
* soft-deleted comment also arrives with `mentions` empty.
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
113
|
+
* soft-deleted comment also arrives with `mentions` empty.
|
|
114
|
+
*
|
|
115
|
+
* WHERE A REDACTED COMMENT ACTUALLY APPEARS is narrower than it once was, and
|
|
116
|
+
* this docblock used to overstate it. The redacted projection is the DELETE
|
|
117
|
+
* acknowledgement — the response to the caller who performed the delete, telling
|
|
118
|
+
* them what they destroyed. It is NOT a row in a thread listing: ADR-BE-545 has
|
|
119
|
+
* the read omit deleted comments outright, and drop a comment-kind thread once
|
|
120
|
+
* nothing live is left on it. A reader never sees a tombstone in a conversation.
|
|
118
121
|
*
|
|
119
122
|
* The invariant is DOCUMENTED here and enforced by the writer, not by a Zod
|
|
120
123
|
* refinement. A refinement would make this package refuse a response the server
|
|
@@ -166,6 +169,18 @@ export type CommentProjection = z.infer<typeof CommentSchema>;
|
|
|
166
169
|
* would make this package refuse a response the server is willing to emit and
|
|
167
170
|
* would be a third wire description no parity guard can check.
|
|
168
171
|
*
|
|
172
|
+
* `lastAnchoredOffset` IS NOT AN ANCHOR. It is a source offset recording where
|
|
173
|
+
* this thread's passage USED TO BE, reported by the client that watched the
|
|
174
|
+
* passage disappear (ADR-BE-548). It exists so a client can keep an ORPHANED
|
|
175
|
+
* thread in the reader's list where the conversation happened, instead of piling
|
|
176
|
+
* every one of them at the top — and it may be used for NOTHING ELSE. It must
|
|
177
|
+
* never yield a range, and nothing may place a highlight, a decoration or a
|
|
178
|
+
* leader line from it: a comment shown against the wrong sentence is strictly
|
|
179
|
+
* worse than one shown as unanchored (ADR-APP-120), and that ordering is what
|
|
180
|
+
* bounds this field. `null` is ordinary — it means no client has watched this
|
|
181
|
+
* thread's passage go — and the value is WRITE-ONCE on the server, so it records
|
|
182
|
+
* the first sighting and never a correction.
|
|
183
|
+
*
|
|
169
184
|
* THE TWO-LEVEL STATUS MODEL: `status` stays exactly `open | resolved`
|
|
170
185
|
* (`COMMENT_THREAD_STATUSES` is NOT widened — widening a closed published enum
|
|
171
186
|
* is a parse outage for every deployed client). A decided suggestion arrives as
|
|
@@ -185,6 +200,7 @@ export const CommentThreadSummarySchema = z.object({
|
|
|
185
200
|
createdByUserId: z.string().nullable(),
|
|
186
201
|
resolvedByUserId: z.string().nullable(),
|
|
187
202
|
resolvedAt: z.string().nullable(),
|
|
203
|
+
lastAnchoredOffset: z.number().int().nullable(),
|
|
188
204
|
createdAt: z.string(),
|
|
189
205
|
updatedAt: z.string(),
|
|
190
206
|
});
|
|
@@ -193,9 +209,12 @@ export type CommentThreadSummary = z.infer<typeof CommentThreadSummarySchema>;
|
|
|
193
209
|
/**
|
|
194
210
|
* A thread with ALL its comments, oldest-first by (createdAt ASC, id ASC).
|
|
195
211
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* comment
|
|
212
|
+
* DELETED COMMENTS ARE OMITTED, NOT REDACTED. This once said the opposite, and
|
|
213
|
+
* the prose outlived the rule: ADR-BE-545 made the read drop deleted comments
|
|
214
|
+
* entirely, and drop a comment-kind thread once nothing live is left on it —
|
|
215
|
+
* deleting the comment that IS a thread deletes the thread with it, so the reply
|
|
216
|
+
* order a redacted row used to hold open no longer has anything to hold. The
|
|
217
|
+
* wire SHAPE is unchanged either way; what changed is which rows arrive.
|
|
199
218
|
*/
|
|
200
219
|
export const CommentThreadSchema = CommentThreadSummarySchema.extend({
|
|
201
220
|
comments: z.array(CommentSchema),
|
|
@@ -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'],
|