@company-semantics/contracts 63.1.0 → 63.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "63.1.0",
3
+ "version": "63.2.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -118,7 +118,7 @@
118
118
  "anchor-corpus:sync": "tsx scripts/anchor-corpus.ts --sync",
119
119
  "generate:spec-hash": "tsx scripts/generate-spec-hash.ts",
120
120
  "generate:spec-hash:check": "tsx scripts/generate-spec-hash.ts --check",
121
- "generate:api": "pnpm generate:api-types && pnpm generate:spec-hash && pnpm generate:openapi-routes",
121
+ "generate:api": "pnpm generate:api-types && pnpm generate:spec-hash && pnpm generate:openapi-routes && pnpm file-sizes:pin",
122
122
  "generate:api:check": "pnpm generate:api-types:check && pnpm generate:spec-hash:check && pnpm generate:openapi-routes:check",
123
123
  "generate:api-types": "openapi-typescript openapi/backend.yaml -o src/api/generated.ts",
124
124
  "generate:openapi-routes": "tsx scripts/generate-openapi-routes.ts",
@@ -86,6 +86,8 @@ function makeThread(over: Record<string, unknown> = {}) {
86
86
  canReopen: false,
87
87
  canDecideSuggestion: false,
88
88
  canWithdraw: false,
89
+ canRevertSuggestion: false,
90
+ canDeleteThread: false,
89
91
  },
90
92
  ...over,
91
93
  };
@@ -34,10 +34,36 @@ describe("the vocabulary tuples", () => {
34
34
  expect([...SUGGESTION_OPS]).toEqual(["insert", "delete", "replace"]);
35
35
  });
36
36
 
37
- it("SUGGESTION_STATUSES is exactly open, accepted and rejected", () => {
37
+ it("SUGGESTION_STATUSES is exactly open, accepted, rejected and reverted", () => {
38
38
  // The SECOND status level. The thread-level enum stays open|resolved —
39
39
  // `./schemas.test.ts` pins that separately — and the decision lives here.
40
- expect([...SUGGESTION_STATUSES]).toEqual(["open", "accepted", "rejected"]);
40
+ //
41
+ // PINNED AS A LIST, NOT A LENGTH, because widening it is the one change on
42
+ // this surface that cannot be walked back: a published `z.enum` refuses a
43
+ // member it has not shipped, and this one is nested inside the thread
44
+ // summary, so an unfamiliar status costs a client the whole document's
45
+ // comments. Failing here is the reminder that adding a member is a
46
+ // coordinated release — readers first, emitter second.
47
+ expect([...SUGGESTION_STATUSES]).toEqual([
48
+ "open",
49
+ "accepted",
50
+ "rejected",
51
+ "reverted",
52
+ ]);
53
+ });
54
+
55
+ it("accepts a reverted payload", () => {
56
+ // The state a taken-back decision lands in. Parsed here so the widening is
57
+ // proven at the schema, not just in the tuple above.
58
+ expect(
59
+ SuggestionSchema.safeParse(
60
+ makeSuggestion({
61
+ op: "replace",
62
+ insertedText: "x",
63
+ status: "reverted",
64
+ }),
65
+ ).success,
66
+ ).toBe(true);
41
67
  });
42
68
  });
43
69
 
@@ -355,6 +355,32 @@ export const ThreadCapabilitiesSchema = z.object({
355
355
  * their own suggestion.
356
356
  */
357
357
  canWithdraw: z.boolean(),
358
+ /**
359
+ * Take back a decision on a DECIDED suggestion — the complement of
360
+ * `canDecideSuggestion`, exactly as `canReopen` is the complement of
361
+ * `canResolve` one kind over.
362
+ *
363
+ * The edit band, like deciding: undoing an acceptance puts the replaced
364
+ * passage back into the document, which is the same authority as putting the
365
+ * proposal in. A reverted suggestion offers it no longer — `reverted` is
366
+ * terminal, so this is false there as well as on an open one.
367
+ */
368
+ canRevertSuggestion: z.boolean(),
369
+ /**
370
+ * Delete the whole thread — the conversation, not one comment in it.
371
+ *
372
+ * TRUE FOR BOTH KINDS, and it is the only capability here that does not
373
+ * mention one. A comment thread could previously only be dismantled by
374
+ * deleting its first comment, which cascaded; a suggestion thread could not be
375
+ * deleted at all, because its proposal is not a comment and there was nothing
376
+ * to aim the cascade at. One flag answers both.
377
+ *
378
+ * IT SAYS NOTHING ABOUT THE DOCUMENT. Deleting a thread whose suggestion was
379
+ * accepted leaves the accepted words where they are: they are the document's
380
+ * now, and taking them back is `canRevertSuggestion`'s act, deliberately kept
381
+ * separate so neither one silently performs the other.
382
+ */
383
+ canDeleteThread: z.boolean(),
358
384
  });
359
385
  export type ThreadCapabilities = z.infer<typeof ThreadCapabilitiesSchema>;
360
386
 
@@ -37,14 +37,43 @@ export const SUGGESTION_OPS = ["insert", "delete", "replace"] as const;
37
37
  export type SuggestionOp = (typeof SUGGESTION_OPS)[number];
38
38
 
39
39
  /**
40
- * open -> accepted | rejected. Terminal states are terminal: there is no
41
- * reopen for a decided suggestion (unlike comment resolve/reopen). This is
42
- * the SECOND status level — the thread-level `status` enum stays exactly
43
- * `open | resolved`, and a decided suggestion arrives as thread status
44
- * `resolved` with this field carrying which way it went. Widening the closed
45
- * thread enum instead would be a parse outage for every deployed client.
40
+ * `open -> accepted | rejected -> reverted`. This is the SECOND status level —
41
+ * the thread-level `status` enum stays exactly `open | resolved`, and a decided
42
+ * suggestion arrives as thread status `resolved` with this field carrying which
43
+ * way it went. Widening the closed thread enum instead would be a parse outage
44
+ * for every deployed client.
45
+ *
46
+ * A DECISION IS NO LONGER THE LAST WORD, BUT IT IS STILL ONE-WAY. This tuple
47
+ * read `open | accepted | rejected` with "terminal states are terminal: there is
48
+ * no reopen for a decided suggestion" written beside it, and that is now false:
49
+ * people are allowed to change their minds, and undoing an acceptance restores
50
+ * the passage the edit replaced. What has NOT changed is that the arrow never
51
+ * points back to `open`. `reverted` is its own terminal state rather than a
52
+ * return to the start, for two reasons — a reverted proposal is a decision
53
+ * somebody took and then took back, which is a different fact from one nobody
54
+ * has answered yet; and an `open` suggestion whose document still carries an
55
+ * application receipt is exactly the shape the server's reconciler treats as
56
+ * proof of an unacknowledged accept, so returning one to `open` would have it
57
+ * silently re-accepted by background maintenance.
58
+ *
59
+ * WIDENING THIS TUPLE IS ORDERED, NOT FREE. `SuggestionSchema` below is a
60
+ * `z.strictObject` nested inside `CommentThreadSummarySchema`, so for anyone who
61
+ * PARSES a thread list, one row carrying a status they do not know fails the
62
+ * whole list — every comment on that document, not just the odd suggestion — and
63
+ * it fails the first time somebody reverts rather than at deploy. No consumer in
64
+ * this workspace parses these at runtime today (the app is typed off the
65
+ * generated spec; the backend validates against its own local copies), which was
66
+ * checked rather than assumed, so this package leads the release and the emitter
67
+ * follows. The next person to add a member owes that check again for their own
68
+ * consumers — which is why `__tests__/suggestion.test.ts` pins this list
69
+ * exactly. See the ADR with slug `suggestion-revert-vocabulary`.
46
70
  */
47
- export const SUGGESTION_STATUSES = ["open", "accepted", "rejected"] as const;
71
+ export const SUGGESTION_STATUSES = [
72
+ "open",
73
+ "accepted",
74
+ "rejected",
75
+ "reverted",
76
+ ] as const;
48
77
  export type SuggestionStatus = (typeof SUGGESTION_STATUSES)[number];
49
78
 
50
79
  /**