@company-semantics/contracts 49.0.0 → 51.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/README.md +6 -1
- package/package.json +15 -15
- package/src/__tests__/resource-keys.test.ts +8 -8
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +346 -13
- package/src/generated/openapi-routes.ts +5 -1
- package/src/resource-key-types.ts +9 -6
- package/src/resource-keys.ts +2 -2
- package/src/resource-registry.ts +5 -4
package/README.md
CHANGED
|
@@ -11,7 +11,10 @@ until they are proven stable.
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
IntentCategory,
|
|
16
|
+
InsightConfidence,
|
|
17
|
+
} from "@company-semantics/contracts";
|
|
15
18
|
```
|
|
16
19
|
|
|
17
20
|
## What belongs here
|
|
@@ -33,6 +36,7 @@ It does not emit `.d.ts` files. Consumers are expected to compile TS.
|
|
|
33
36
|
## Versioning
|
|
34
37
|
|
|
35
38
|
Follows semver:
|
|
39
|
+
|
|
36
40
|
- Additive vocabulary → minor bump
|
|
37
41
|
- Renaming or removal → major bump
|
|
38
42
|
|
|
@@ -41,6 +45,7 @@ Follows semver:
|
|
|
41
45
|
Releases are published automatically via GitHub Actions.
|
|
42
46
|
|
|
43
47
|
Steps:
|
|
48
|
+
|
|
44
49
|
1. `npm version patch`
|
|
45
50
|
2. `git push origin main --tags`
|
|
46
51
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@company-semantics/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "51.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"typecheck:ci": "NODE_OPTIONS='--max-old-space-size=4096' tsc -p scripts/ci/tsconfig.json",
|
|
89
89
|
"lint": "eslint src/ scripts/ tests/ __tests__/ vitest.config.ts",
|
|
90
90
|
"lint:md": "markdownlint-cli2 '**/*.md' '#node_modules' '#.ralph/worktrees' '#.claude/worktrees'",
|
|
91
|
-
"format": "prettier --write
|
|
92
|
-
"format:check": "prettier --check
|
|
91
|
+
"format": "prettier --write .",
|
|
92
|
+
"format:check": "prettier --check .",
|
|
93
93
|
"lint:json": "node -e \"JSON.parse(require('fs').readFileSync('package.json'))\"",
|
|
94
94
|
"prepare": "husky",
|
|
95
95
|
"guard:export": "npx tsx scripts/ci/export-guard.ts",
|
|
@@ -101,9 +101,9 @@
|
|
|
101
101
|
"guard:version-tag:json": "npx tsx scripts/ci/version-tag-guard.ts --json",
|
|
102
102
|
"guard:decisions-deprecation": "npx tsx scripts/ci/decisions-deprecation-guard.ts",
|
|
103
103
|
"guard:decisions-deprecation:json": "npx tsx scripts/ci/decisions-deprecation-guard.ts --json",
|
|
104
|
-
"guard:quick": "
|
|
105
|
-
"ci:local": "pnpm typecheck && pnpm lint:md && pnpm guard:test &&
|
|
106
|
-
"guard": "
|
|
104
|
+
"guard:quick": "sh scripts/ci/run-guard.sh --quick",
|
|
105
|
+
"ci:local": "pnpm typecheck && pnpm lint:md && pnpm guard:test && pnpm guard",
|
|
106
|
+
"guard": "sh scripts/ci/run-guard.sh",
|
|
107
107
|
"guard:test": "NODE_OPTIONS='--max-old-space-size=4096' vitest run scripts/ci/__tests__",
|
|
108
108
|
"release": "npx tsx scripts/release.ts",
|
|
109
109
|
"prepublishOnly": "echo 'ERROR: Publishing is CI-only via tag push. Use pnpm release instead.' && exit 1",
|
|
@@ -120,13 +120,13 @@
|
|
|
120
120
|
"generate:openapi-routes": "tsx scripts/generate-openapi-routes.ts",
|
|
121
121
|
"generate:openapi-routes:check": "tsx scripts/generate-openapi-routes.ts --check",
|
|
122
122
|
"generate:api-types:check": "openapi-typescript openapi/backend.yaml -o /tmp/cs-api-types-check.ts && diff -q src/api/generated.ts /tmp/cs-api-types-check.ts",
|
|
123
|
-
"generate:current": "tsx
|
|
124
|
-
"adr:new": "tsx
|
|
125
|
-
"repo-map": "tsx
|
|
126
|
-
"sync:workflow-pins": "tsx
|
|
127
|
-
"repo-map:check": "tsx
|
|
128
|
-
"readme-api": "tsx
|
|
129
|
-
"readme-api:check": "tsx
|
|
123
|
+
"generate:current": "tsx \"$(git rev-parse --path-format=absolute --git-common-dir)/../../company-semantics-ci/scripts/generate-current.ts\"",
|
|
124
|
+
"adr:new": "tsx \"$(git rev-parse --path-format=absolute --git-common-dir)/../../company-semantics-control/scripts/adr/adr-new-cli.ts\"",
|
|
125
|
+
"repo-map": "tsx \"$(git rev-parse --path-format=absolute --git-common-dir)/../../company-semantics-ci/scripts/generate-repo-map.ts\" --roots src --name company-semantics-contracts --write",
|
|
126
|
+
"sync:workflow-pins": "tsx \"$(git rev-parse --path-format=absolute --git-common-dir)/../../company-semantics-ci/scripts/sync-workflow-pins.ts\" --write",
|
|
127
|
+
"repo-map:check": "tsx \"$(git rev-parse --path-format=absolute --git-common-dir)/../../company-semantics-ci/scripts/generate-repo-map.ts\" --roots src --name company-semantics-contracts --check",
|
|
128
|
+
"readme-api": "tsx \"$(git rev-parse --path-format=absolute --git-common-dir)/../../company-semantics-ci/scripts/generate-readme-api.ts\" --roots src --write --jsdoc",
|
|
129
|
+
"readme-api:check": "tsx \"$(git rev-parse --path-format=absolute --git-common-dir)/../../company-semantics-ci/scripts/generate-readme-api.ts\" --roots src --check --jsdoc",
|
|
130
130
|
"validate:amp": "tsx scripts/validate-amp.ts"
|
|
131
131
|
},
|
|
132
132
|
"packageManager": "pnpm@10.25.0",
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
"overrides": {
|
|
160
160
|
"brace-expansion@<5.0.9": ">=5.0.9",
|
|
161
161
|
"minimatch@<10.2.3": ">=10.2.3",
|
|
162
|
-
"nanoid@<3.3.
|
|
162
|
+
"nanoid@<3.3.18": ">=3.3.18",
|
|
163
163
|
"js-yaml@>=4.0.0 <4.3.1": ">=4.3.1 <5.0.0",
|
|
164
164
|
"js-yaml@>=5.0.0 <5.2.2": ">=5.2.2 <6.0.0",
|
|
165
165
|
"ajv@<6.14.0": ">=6.14.0",
|
|
@@ -176,5 +176,5 @@
|
|
|
176
176
|
"*.md": "markdownlint-cli2",
|
|
177
177
|
"package.json": "node -e \"JSON.parse(require('fs').readFileSync('package.json'))\""
|
|
178
178
|
},
|
|
179
|
-
"securityRequirementsVersion": "
|
|
179
|
+
"securityRequirementsVersion": "2dcea336317c1136c8d94798b1cadc9c115febf574d041a173974a5f7959de0b"
|
|
180
180
|
}
|
|
@@ -234,16 +234,16 @@ describe("resource-keys: companyMdAccessRequests (per-doc identity)", () => {
|
|
|
234
234
|
});
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
-
describe("resource-keys:
|
|
237
|
+
describe("resource-keys: companyMdDocHistory (per-doc history list)", () => {
|
|
238
238
|
const DOC_ID = "44444444-4444-4444-8444-444444444444";
|
|
239
239
|
const key: ResourceKey = {
|
|
240
|
-
type: "
|
|
240
|
+
type: "companyMdDocHistory",
|
|
241
241
|
orgId: ORG_ID,
|
|
242
242
|
docId: DOC_ID,
|
|
243
243
|
};
|
|
244
244
|
|
|
245
245
|
it("serialises to [type, orgId, docId]", () => {
|
|
246
|
-
expect(toQueryKey(key)).toEqual(["
|
|
246
|
+
expect(toQueryKey(key)).toEqual(["companyMdDocHistory", ORG_ID, DOC_ID]);
|
|
247
247
|
});
|
|
248
248
|
|
|
249
249
|
it("round-trips through fromQueryKey", () => {
|
|
@@ -261,7 +261,7 @@ describe("resource-keys: companyMdDocVersions (per-doc version list)", () => {
|
|
|
261
261
|
|
|
262
262
|
it("does NOT collide with companyMdAccessRequests, which shares its shape", () => {
|
|
263
263
|
// Same segments, same field name, same document — only the type tag tells
|
|
264
|
-
// the two apart. An owner's access-request inbox and a
|
|
264
|
+
// the two apart. An owner's access-request inbox and a history list must
|
|
265
265
|
// never invalidate each other.
|
|
266
266
|
const requestsKey: ResourceKey = {
|
|
267
267
|
type: "companyMdAccessRequests",
|
|
@@ -277,7 +277,7 @@ describe("resource-keys: companyMdDocVersion (composite version identity)", () =
|
|
|
277
277
|
// The second composite member. Its identity is (docId, versionId), so it
|
|
278
278
|
// exercises the same four-segment path `commentThreads` opened — and, unlike
|
|
279
279
|
// that key, it has a SIBLING of the same doc scope one segment shorter
|
|
280
|
-
// (
|
|
280
|
+
// (companyMdDocHistory), which is the collision the split exists to avoid.
|
|
281
281
|
const DOC_ID = "44444444-4444-4444-8444-444444444444";
|
|
282
282
|
const VERSION_ID = "66666666-6666-4666-8666-666666666666";
|
|
283
283
|
const key: ResourceKey = {
|
|
@@ -319,12 +319,12 @@ describe("resource-keys: companyMdDocVersion (composite version identity)", () =
|
|
|
319
319
|
expect(matchesResourceKey(toQueryKey(key), other)).toBe(false);
|
|
320
320
|
});
|
|
321
321
|
|
|
322
|
-
it("is NOT invalidated by a sweep of the document's
|
|
322
|
+
it("is NOT invalidated by a sweep of the document's history LIST", () => {
|
|
323
323
|
// The whole reason the two members are distinct. A restore appends to the
|
|
324
|
-
// history and so invalidates `
|
|
324
|
+
// history and so invalidates `companyMdDocHistory`; the sealed bodies
|
|
325
325
|
// already fetched are immutable and must survive it.
|
|
326
326
|
const listKey: ResourceKey = {
|
|
327
|
-
type: "
|
|
327
|
+
type: "companyMdDocHistory",
|
|
328
328
|
orgId: ORG_ID,
|
|
329
329
|
docId: DOC_ID,
|
|
330
330
|
};
|
|
@@ -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 = '6dcd82441a54' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '6dcd82441a54409faaa94d94f36a43cd72894c7e655fb49f5eb758a0963c9a43' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -1548,15 +1548,15 @@ export interface paths {
|
|
|
1548
1548
|
patch?: never;
|
|
1549
1549
|
trace?: never;
|
|
1550
1550
|
};
|
|
1551
|
-
"/api/company-md/docs/{id}/
|
|
1551
|
+
"/api/company-md/docs/{id}/history": {
|
|
1552
1552
|
parameters: {
|
|
1553
1553
|
query?: never;
|
|
1554
1554
|
header?: never;
|
|
1555
1555
|
path?: never;
|
|
1556
1556
|
cookie?: never;
|
|
1557
1557
|
};
|
|
1558
|
-
/** List a company.md document’s
|
|
1559
|
-
get: operations["
|
|
1558
|
+
/** List a company.md document’s merged history (versions and lifecycle events) */
|
|
1559
|
+
get: operations["listCompanyMdDocHistory"];
|
|
1560
1560
|
put?: never;
|
|
1561
1561
|
post?: never;
|
|
1562
1562
|
delete?: never;
|
|
@@ -3565,6 +3565,74 @@ export interface paths {
|
|
|
3565
3565
|
patch?: never;
|
|
3566
3566
|
trace?: never;
|
|
3567
3567
|
};
|
|
3568
|
+
"/api/comments/threads/{threadId}/accept-intent": {
|
|
3569
|
+
parameters: {
|
|
3570
|
+
query?: never;
|
|
3571
|
+
header?: never;
|
|
3572
|
+
path?: never;
|
|
3573
|
+
cookie?: never;
|
|
3574
|
+
};
|
|
3575
|
+
get?: never;
|
|
3576
|
+
put?: never;
|
|
3577
|
+
/** Claim the acceptance lease on an open suggestion */
|
|
3578
|
+
post: operations["acceptSuggestionIntent"];
|
|
3579
|
+
delete?: never;
|
|
3580
|
+
options?: never;
|
|
3581
|
+
head?: never;
|
|
3582
|
+
patch?: never;
|
|
3583
|
+
trace?: never;
|
|
3584
|
+
};
|
|
3585
|
+
"/api/comments/threads/{threadId}/accept-complete": {
|
|
3586
|
+
parameters: {
|
|
3587
|
+
query?: never;
|
|
3588
|
+
header?: never;
|
|
3589
|
+
path?: never;
|
|
3590
|
+
cookie?: never;
|
|
3591
|
+
};
|
|
3592
|
+
get?: never;
|
|
3593
|
+
put?: never;
|
|
3594
|
+
/** Record the accept decision by claim token */
|
|
3595
|
+
post: operations["acceptSuggestionComplete"];
|
|
3596
|
+
delete?: never;
|
|
3597
|
+
options?: never;
|
|
3598
|
+
head?: never;
|
|
3599
|
+
patch?: never;
|
|
3600
|
+
trace?: never;
|
|
3601
|
+
};
|
|
3602
|
+
"/api/comments/threads/{threadId}/accept-release": {
|
|
3603
|
+
parameters: {
|
|
3604
|
+
query?: never;
|
|
3605
|
+
header?: never;
|
|
3606
|
+
path?: never;
|
|
3607
|
+
cookie?: never;
|
|
3608
|
+
};
|
|
3609
|
+
get?: never;
|
|
3610
|
+
put?: never;
|
|
3611
|
+
/** Release a held acceptance lease without deciding */
|
|
3612
|
+
post: operations["releaseSuggestionClaim"];
|
|
3613
|
+
delete?: never;
|
|
3614
|
+
options?: never;
|
|
3615
|
+
head?: never;
|
|
3616
|
+
patch?: never;
|
|
3617
|
+
trace?: never;
|
|
3618
|
+
};
|
|
3619
|
+
"/api/comments/threads/{threadId}/reject": {
|
|
3620
|
+
parameters: {
|
|
3621
|
+
query?: never;
|
|
3622
|
+
header?: never;
|
|
3623
|
+
path?: never;
|
|
3624
|
+
cookie?: never;
|
|
3625
|
+
};
|
|
3626
|
+
get?: never;
|
|
3627
|
+
put?: never;
|
|
3628
|
+
/** Reject an open suggestion (or withdraw as its author) */
|
|
3629
|
+
post: operations["rejectSuggestion"];
|
|
3630
|
+
delete?: never;
|
|
3631
|
+
options?: never;
|
|
3632
|
+
head?: never;
|
|
3633
|
+
patch?: never;
|
|
3634
|
+
trace?: never;
|
|
3635
|
+
};
|
|
3568
3636
|
}
|
|
3569
3637
|
export type webhooks = Record<string, never>;
|
|
3570
3638
|
export interface components {
|
|
@@ -4966,10 +5034,11 @@ export interface components {
|
|
|
4966
5034
|
CompanyMdMentionableResponse: {
|
|
4967
5035
|
items: components["schemas"]["CompanyMdMentionableCandidate"][];
|
|
4968
5036
|
};
|
|
4969
|
-
/** @description A page of a company.md document’s
|
|
4970
|
-
|
|
5037
|
+
/** @description A page of a company.md document’s merged History — versions and lifecycle events under one cursor — plus the opaque token a restore is gated on. */
|
|
5038
|
+
CompanyMdDocHistoryResponse: {
|
|
4971
5039
|
currentRevision: string;
|
|
4972
|
-
|
|
5040
|
+
items: components["schemas"]["CompanyMdDocHistoryItem"][];
|
|
5041
|
+
withheldCategories: ("version" | "source" | "lifecycle" | "access" | "sync")[];
|
|
4973
5042
|
nextCursor: string | null;
|
|
4974
5043
|
};
|
|
4975
5044
|
/** @description One sealed company.md version — its metadata and the body it sealed. */
|
|
@@ -6397,7 +6466,16 @@ export interface components {
|
|
|
6397
6466
|
/** Format: uuid */
|
|
6398
6467
|
subjectId: string;
|
|
6399
6468
|
anchor: components["schemas"]["CommentAnchor"];
|
|
6400
|
-
|
|
6469
|
+
/** @enum {string} */
|
|
6470
|
+
kind?: "comment" | "suggestion";
|
|
6471
|
+
suggestion?: {
|
|
6472
|
+
/** @constant */
|
|
6473
|
+
v: 1;
|
|
6474
|
+
/** @enum {string} */
|
|
6475
|
+
op: "insert" | "delete" | "replace";
|
|
6476
|
+
insertedText?: string;
|
|
6477
|
+
};
|
|
6478
|
+
body?: string;
|
|
6401
6479
|
mentions?: {
|
|
6402
6480
|
/** Format: uuid */
|
|
6403
6481
|
userId: string;
|
|
@@ -6436,6 +6514,14 @@ export interface components {
|
|
|
6436
6514
|
CommentThreadSummaryResponse: {
|
|
6437
6515
|
thread: components["schemas"]["CommentThreadSummary"];
|
|
6438
6516
|
};
|
|
6517
|
+
/** @description The acceptance lease: the opaque claim token that alone can complete the accept, and how long the lease lives. */
|
|
6518
|
+
SuggestionClaimResponse: {
|
|
6519
|
+
claimToken: string;
|
|
6520
|
+
leaseDurationMs: number;
|
|
6521
|
+
};
|
|
6522
|
+
SuggestionClaimRequest: {
|
|
6523
|
+
claimToken: string;
|
|
6524
|
+
};
|
|
6439
6525
|
/** @description Server-enforced collaboration size limits, published so clients can pre-check. */
|
|
6440
6526
|
CollabSyncLimits: {
|
|
6441
6527
|
maxUpdateBytes: number;
|
|
@@ -6447,12 +6533,55 @@ export interface components {
|
|
|
6447
6533
|
displayName: string;
|
|
6448
6534
|
avatarUrl: string | null;
|
|
6449
6535
|
};
|
|
6536
|
+
/** @description One item in a document’s merged History: a sealed version (or the in-progress session) nested as `version`, or a lifecycle event. */
|
|
6537
|
+
CompanyMdDocHistoryItem: {
|
|
6538
|
+
/** @constant */
|
|
6539
|
+
kind: "version";
|
|
6540
|
+
mergeId: string | null;
|
|
6541
|
+
occurredAt: string;
|
|
6542
|
+
/** @constant */
|
|
6543
|
+
category: "version";
|
|
6544
|
+
version: components["schemas"]["CompanyMdDocVersionEntry"];
|
|
6545
|
+
} | {
|
|
6546
|
+
/** @constant */
|
|
6547
|
+
kind: "event";
|
|
6548
|
+
mergeId: string;
|
|
6549
|
+
occurredAt: string;
|
|
6550
|
+
/** @enum {string} */
|
|
6551
|
+
category: "version" | "source" | "lifecycle" | "access" | "sync";
|
|
6552
|
+
/** @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";
|
|
6554
|
+
actor: components["schemas"]["CompanyMdDocVersionActor"] | null;
|
|
6555
|
+
subject: string | null;
|
|
6556
|
+
target: components["schemas"]["CompanyMdDocHistoryTarget"] | null;
|
|
6557
|
+
previousValue: string | null;
|
|
6558
|
+
newValue: string | null;
|
|
6559
|
+
};
|
|
6450
6560
|
/** @description The singular actor a version is attributed to, in product vocabulary. */
|
|
6451
6561
|
CompanyMdDocVersionActor: {
|
|
6452
6562
|
/** @enum {string} */
|
|
6453
6563
|
type: "member" | "agent" | "system";
|
|
6454
6564
|
userId: string;
|
|
6455
6565
|
};
|
|
6566
|
+
/** @description What a history event acted on: an ACL principal (member, unit, or the whole org) or the extracted source artifact. */
|
|
6567
|
+
CompanyMdDocHistoryTarget: {
|
|
6568
|
+
/** @constant */
|
|
6569
|
+
type: "member";
|
|
6570
|
+
userId: string;
|
|
6571
|
+
} | {
|
|
6572
|
+
/** @constant */
|
|
6573
|
+
type: "unit";
|
|
6574
|
+
unitId: string;
|
|
6575
|
+
} | {
|
|
6576
|
+
/** @constant */
|
|
6577
|
+
type: "org";
|
|
6578
|
+
} | {
|
|
6579
|
+
/** @constant */
|
|
6580
|
+
type: "source";
|
|
6581
|
+
filename: string | null;
|
|
6582
|
+
mime: string;
|
|
6583
|
+
byteSize: number;
|
|
6584
|
+
};
|
|
6456
6585
|
/** @description An ACL-admitted context-doc discovery hit. */
|
|
6457
6586
|
ContextDocDiscoveryHit: {
|
|
6458
6587
|
id: string;
|
|
@@ -6473,10 +6602,13 @@ export interface components {
|
|
|
6473
6602
|
subjectType: "company_md" | "strategy_doc" | "work_item" | "meeting_recording";
|
|
6474
6603
|
subjectId: string;
|
|
6475
6604
|
/** @enum {string} */
|
|
6476
|
-
anchorType: "document" | "text";
|
|
6605
|
+
anchorType: "document" | "text" | "text-insertion";
|
|
6477
6606
|
anchor: components["schemas"]["CommentAnchor"];
|
|
6478
6607
|
/** @enum {string} */
|
|
6479
6608
|
status: "open" | "resolved";
|
|
6609
|
+
/** @enum {string} */
|
|
6610
|
+
kind: "comment" | "suggestion";
|
|
6611
|
+
suggestion: components["schemas"]["Suggestion"] | null;
|
|
6480
6612
|
createdByUserId: string | null;
|
|
6481
6613
|
resolvedByUserId: string | null;
|
|
6482
6614
|
resolvedAt: string | null;
|
|
@@ -6484,7 +6616,7 @@ export interface components {
|
|
|
6484
6616
|
updatedAt: string;
|
|
6485
6617
|
comments: components["schemas"]["Comment"][];
|
|
6486
6618
|
};
|
|
6487
|
-
/** @description Opaque, versioned comment anchor: document-level,
|
|
6619
|
+
/** @description Opaque, versioned comment anchor: document-level, a text anchor with a required quote and optional relative positions, or a zero-width text-insertion point for insert suggestions. */
|
|
6488
6620
|
CommentAnchor: {
|
|
6489
6621
|
/** @constant */
|
|
6490
6622
|
type: "document";
|
|
@@ -6500,6 +6632,24 @@ export interface components {
|
|
|
6500
6632
|
quote: string;
|
|
6501
6633
|
prefix: string;
|
|
6502
6634
|
suffix: string;
|
|
6635
|
+
} | {
|
|
6636
|
+
/** @constant */
|
|
6637
|
+
type: "text-insertion";
|
|
6638
|
+
/** @constant */
|
|
6639
|
+
v: 1;
|
|
6640
|
+
relPos?: string;
|
|
6641
|
+
leftContext: string;
|
|
6642
|
+
rightContext: string;
|
|
6643
|
+
};
|
|
6644
|
+
/** @description A suggested edit as proposed: the operation, its insertedText (insert/replace only), and the decision status composed from the thread row. */
|
|
6645
|
+
Suggestion: {
|
|
6646
|
+
/** @constant */
|
|
6647
|
+
v: 1;
|
|
6648
|
+
/** @enum {string} */
|
|
6649
|
+
op: "insert" | "delete" | "replace";
|
|
6650
|
+
insertedText?: string;
|
|
6651
|
+
/** @enum {string} */
|
|
6652
|
+
status: "open" | "accepted" | "rejected";
|
|
6503
6653
|
};
|
|
6504
6654
|
/** @description One comment; body is null iff soft-deleted (redacted projection with empty mentions). */
|
|
6505
6655
|
Comment: {
|
|
@@ -6526,10 +6676,13 @@ export interface components {
|
|
|
6526
6676
|
subjectType: "company_md" | "strategy_doc" | "work_item" | "meeting_recording";
|
|
6527
6677
|
subjectId: string;
|
|
6528
6678
|
/** @enum {string} */
|
|
6529
|
-
anchorType: "document" | "text";
|
|
6679
|
+
anchorType: "document" | "text" | "text-insertion";
|
|
6530
6680
|
anchor: components["schemas"]["CommentAnchor"];
|
|
6531
6681
|
/** @enum {string} */
|
|
6532
6682
|
status: "open" | "resolved";
|
|
6683
|
+
/** @enum {string} */
|
|
6684
|
+
kind: "comment" | "suggestion";
|
|
6685
|
+
suggestion: components["schemas"]["Suggestion"] | null;
|
|
6533
6686
|
createdByUserId: string | null;
|
|
6534
6687
|
resolvedByUserId: string | null;
|
|
6535
6688
|
resolvedAt: string | null;
|
|
@@ -8970,7 +9123,7 @@ export interface operations {
|
|
|
8970
9123
|
};
|
|
8971
9124
|
};
|
|
8972
9125
|
};
|
|
8973
|
-
|
|
9126
|
+
listCompanyMdDocHistory: {
|
|
8974
9127
|
parameters: {
|
|
8975
9128
|
query?: {
|
|
8976
9129
|
cursor?: string;
|
|
@@ -8984,13 +9137,13 @@ export interface operations {
|
|
|
8984
9137
|
};
|
|
8985
9138
|
requestBody?: never;
|
|
8986
9139
|
responses: {
|
|
8987
|
-
/** @description A page of
|
|
9140
|
+
/** @description A page of merged history items, plus the document’s live revision token and the categories withheld for this reader */
|
|
8988
9141
|
200: {
|
|
8989
9142
|
headers: {
|
|
8990
9143
|
[name: string]: unknown;
|
|
8991
9144
|
};
|
|
8992
9145
|
content: {
|
|
8993
|
-
"application/json": components["schemas"]["
|
|
9146
|
+
"application/json": components["schemas"]["CompanyMdDocHistoryResponse"];
|
|
8994
9147
|
};
|
|
8995
9148
|
};
|
|
8996
9149
|
/** @description Invalid cursor or limit */
|
|
@@ -12635,6 +12788,13 @@ export interface operations {
|
|
|
12635
12788
|
};
|
|
12636
12789
|
content?: never;
|
|
12637
12790
|
};
|
|
12791
|
+
/** @description Not a comment thread: a suggestion is decided via accept or reject, never resolved */
|
|
12792
|
+
409: {
|
|
12793
|
+
headers: {
|
|
12794
|
+
[name: string]: unknown;
|
|
12795
|
+
};
|
|
12796
|
+
content?: never;
|
|
12797
|
+
};
|
|
12638
12798
|
};
|
|
12639
12799
|
};
|
|
12640
12800
|
reopenCommentThread: {
|
|
@@ -12664,6 +12824,179 @@ export interface operations {
|
|
|
12664
12824
|
};
|
|
12665
12825
|
content?: never;
|
|
12666
12826
|
};
|
|
12827
|
+
/** @description Not a comment thread: a decided suggestion is terminal and never reopens */
|
|
12828
|
+
409: {
|
|
12829
|
+
headers: {
|
|
12830
|
+
[name: string]: unknown;
|
|
12831
|
+
};
|
|
12832
|
+
content?: never;
|
|
12833
|
+
};
|
|
12834
|
+
};
|
|
12835
|
+
};
|
|
12836
|
+
acceptSuggestionIntent: {
|
|
12837
|
+
parameters: {
|
|
12838
|
+
query?: never;
|
|
12839
|
+
header?: never;
|
|
12840
|
+
path: {
|
|
12841
|
+
threadId: string;
|
|
12842
|
+
};
|
|
12843
|
+
cookie?: never;
|
|
12844
|
+
};
|
|
12845
|
+
requestBody?: never;
|
|
12846
|
+
responses: {
|
|
12847
|
+
/** @description Lease claimed: the opaque claim token (the only artifact that can complete the accept) and its duration */
|
|
12848
|
+
200: {
|
|
12849
|
+
headers: {
|
|
12850
|
+
[name: string]: unknown;
|
|
12851
|
+
};
|
|
12852
|
+
content: {
|
|
12853
|
+
"application/json": components["schemas"]["SuggestionClaimResponse"];
|
|
12854
|
+
};
|
|
12855
|
+
};
|
|
12856
|
+
/** @description Thread not found, or the caller may not decide suggestions on it */
|
|
12857
|
+
404: {
|
|
12858
|
+
headers: {
|
|
12859
|
+
[name: string]: unknown;
|
|
12860
|
+
};
|
|
12861
|
+
content?: never;
|
|
12862
|
+
};
|
|
12863
|
+
/** @description Not a suggestion thread, already claimed by a live lease, or already decided */
|
|
12864
|
+
409: {
|
|
12865
|
+
headers: {
|
|
12866
|
+
[name: string]: unknown;
|
|
12867
|
+
};
|
|
12868
|
+
content?: never;
|
|
12869
|
+
};
|
|
12870
|
+
};
|
|
12871
|
+
};
|
|
12872
|
+
acceptSuggestionComplete: {
|
|
12873
|
+
parameters: {
|
|
12874
|
+
query?: never;
|
|
12875
|
+
header?: never;
|
|
12876
|
+
path: {
|
|
12877
|
+
threadId: string;
|
|
12878
|
+
};
|
|
12879
|
+
cookie?: never;
|
|
12880
|
+
};
|
|
12881
|
+
requestBody: {
|
|
12882
|
+
content: {
|
|
12883
|
+
"application/json": components["schemas"]["SuggestionClaimRequest"];
|
|
12884
|
+
};
|
|
12885
|
+
};
|
|
12886
|
+
responses: {
|
|
12887
|
+
/** @description Suggestion accepted (thread resolved); valid after lease expiry and retry-safe for the same actor */
|
|
12888
|
+
200: {
|
|
12889
|
+
headers: {
|
|
12890
|
+
[name: string]: unknown;
|
|
12891
|
+
};
|
|
12892
|
+
content: {
|
|
12893
|
+
"application/json": components["schemas"]["CommentThreadSummaryResponse"];
|
|
12894
|
+
};
|
|
12895
|
+
};
|
|
12896
|
+
/** @description Malformed claim token */
|
|
12897
|
+
400: {
|
|
12898
|
+
headers: {
|
|
12899
|
+
[name: string]: unknown;
|
|
12900
|
+
};
|
|
12901
|
+
content?: never;
|
|
12902
|
+
};
|
|
12903
|
+
/** @description Thread not found, or the caller may not read its subject */
|
|
12904
|
+
404: {
|
|
12905
|
+
headers: {
|
|
12906
|
+
[name: string]: unknown;
|
|
12907
|
+
};
|
|
12908
|
+
content?: never;
|
|
12909
|
+
};
|
|
12910
|
+
/** @description Not a suggestion thread, or the token does not match a live claim on an open suggestion */
|
|
12911
|
+
409: {
|
|
12912
|
+
headers: {
|
|
12913
|
+
[name: string]: unknown;
|
|
12914
|
+
};
|
|
12915
|
+
content?: never;
|
|
12916
|
+
};
|
|
12917
|
+
};
|
|
12918
|
+
};
|
|
12919
|
+
releaseSuggestionClaim: {
|
|
12920
|
+
parameters: {
|
|
12921
|
+
query?: never;
|
|
12922
|
+
header?: never;
|
|
12923
|
+
path: {
|
|
12924
|
+
threadId: string;
|
|
12925
|
+
};
|
|
12926
|
+
cookie?: never;
|
|
12927
|
+
};
|
|
12928
|
+
requestBody: {
|
|
12929
|
+
content: {
|
|
12930
|
+
"application/json": components["schemas"]["SuggestionClaimRequest"];
|
|
12931
|
+
};
|
|
12932
|
+
};
|
|
12933
|
+
responses: {
|
|
12934
|
+
/** @description Lease released; the suggestion stays open and claimable */
|
|
12935
|
+
200: {
|
|
12936
|
+
headers: {
|
|
12937
|
+
[name: string]: unknown;
|
|
12938
|
+
};
|
|
12939
|
+
content: {
|
|
12940
|
+
"application/json": components["schemas"]["CommentThreadSummaryResponse"];
|
|
12941
|
+
};
|
|
12942
|
+
};
|
|
12943
|
+
/** @description Malformed claim token */
|
|
12944
|
+
400: {
|
|
12945
|
+
headers: {
|
|
12946
|
+
[name: string]: unknown;
|
|
12947
|
+
};
|
|
12948
|
+
content?: never;
|
|
12949
|
+
};
|
|
12950
|
+
/** @description Thread not found, or the caller may not read its subject */
|
|
12951
|
+
404: {
|
|
12952
|
+
headers: {
|
|
12953
|
+
[name: string]: unknown;
|
|
12954
|
+
};
|
|
12955
|
+
content?: never;
|
|
12956
|
+
};
|
|
12957
|
+
/** @description Not a suggestion thread, or the token does not match a live claim on an open suggestion */
|
|
12958
|
+
409: {
|
|
12959
|
+
headers: {
|
|
12960
|
+
[name: string]: unknown;
|
|
12961
|
+
};
|
|
12962
|
+
content?: never;
|
|
12963
|
+
};
|
|
12964
|
+
};
|
|
12965
|
+
};
|
|
12966
|
+
rejectSuggestion: {
|
|
12967
|
+
parameters: {
|
|
12968
|
+
query?: never;
|
|
12969
|
+
header?: never;
|
|
12970
|
+
path: {
|
|
12971
|
+
threadId: string;
|
|
12972
|
+
};
|
|
12973
|
+
cookie?: never;
|
|
12974
|
+
};
|
|
12975
|
+
requestBody?: never;
|
|
12976
|
+
responses: {
|
|
12977
|
+
/** @description Suggestion rejected (thread resolved) */
|
|
12978
|
+
200: {
|
|
12979
|
+
headers: {
|
|
12980
|
+
[name: string]: unknown;
|
|
12981
|
+
};
|
|
12982
|
+
content: {
|
|
12983
|
+
"application/json": components["schemas"]["CommentThreadSummaryResponse"];
|
|
12984
|
+
};
|
|
12985
|
+
};
|
|
12986
|
+
/** @description Thread not found, or the caller may not decide suggestions on it */
|
|
12987
|
+
404: {
|
|
12988
|
+
headers: {
|
|
12989
|
+
[name: string]: unknown;
|
|
12990
|
+
};
|
|
12991
|
+
content?: never;
|
|
12992
|
+
};
|
|
12993
|
+
/** @description Not a suggestion thread, a live acceptance lease is held, or already decided */
|
|
12994
|
+
409: {
|
|
12995
|
+
headers: {
|
|
12996
|
+
[name: string]: unknown;
|
|
12997
|
+
};
|
|
12998
|
+
content?: never;
|
|
12999
|
+
};
|
|
12667
13000
|
};
|
|
12668
13001
|
};
|
|
12669
13002
|
}
|
|
@@ -23,7 +23,11 @@ export const openApiRoutes = {
|
|
|
23
23
|
'/api/chats/{id}/pin': ['DELETE', 'POST'],
|
|
24
24
|
'/api/comments': ['GET'],
|
|
25
25
|
'/api/comments/threads': ['POST'],
|
|
26
|
+
'/api/comments/threads/{threadId}/accept-complete': ['POST'],
|
|
27
|
+
'/api/comments/threads/{threadId}/accept-intent': ['POST'],
|
|
28
|
+
'/api/comments/threads/{threadId}/accept-release': ['POST'],
|
|
26
29
|
'/api/comments/threads/{threadId}/comments': ['POST'],
|
|
30
|
+
'/api/comments/threads/{threadId}/reject': ['POST'],
|
|
27
31
|
'/api/comments/threads/{threadId}/reopen': ['POST'],
|
|
28
32
|
'/api/comments/threads/{threadId}/resolve': ['POST'],
|
|
29
33
|
'/api/comments/{commentId}': ['DELETE', 'PATCH'],
|
|
@@ -46,6 +50,7 @@ export const openApiRoutes = {
|
|
|
46
50
|
'/api/company-md/docs/{id}/context-bank/upload': ['POST'],
|
|
47
51
|
'/api/company-md/docs/{id}/context-bank/{contextDocId}': ['DELETE'],
|
|
48
52
|
'/api/company-md/docs/{id}/context-bank/{contextDocId}/order': ['PATCH'],
|
|
53
|
+
'/api/company-md/docs/{id}/history': ['GET'],
|
|
49
54
|
'/api/company-md/docs/{id}/mentionable': ['GET'],
|
|
50
55
|
'/api/company-md/docs/{id}/sharing': ['GET'],
|
|
51
56
|
'/api/company-md/docs/{id}/sharing/acl': ['POST'],
|
|
@@ -53,7 +58,6 @@ export const openApiRoutes = {
|
|
|
53
58
|
'/api/company-md/docs/{id}/sharing/policy': ['PUT'],
|
|
54
59
|
'/api/company-md/docs/{id}/title': ['PUT'],
|
|
55
60
|
'/api/company-md/docs/{id}/transfer-owner': ['POST'],
|
|
56
|
-
'/api/company-md/docs/{id}/versions': ['GET'],
|
|
57
61
|
'/api/company-md/docs/{id}/versions/{versionId}': ['GET'],
|
|
58
62
|
'/api/company-md/docs/{id}/versions/{versionId}/restore': ['POST'],
|
|
59
63
|
'/api/company-md/extract': ['POST'],
|
|
@@ -38,16 +38,19 @@ export type ResourceKey =
|
|
|
38
38
|
// a bare slug does not identify a document. That field name is a wart to be
|
|
39
39
|
// contained, not propagated — a new key gets the honest name.
|
|
40
40
|
| { type: "companyMdAccessRequests"; orgId: string; docId: string }
|
|
41
|
-
// The
|
|
42
|
-
// list the history panel pages through.
|
|
43
|
-
// `
|
|
44
|
-
//
|
|
45
|
-
|
|
41
|
+
// The merged history feed of ONE document (PRD-00938) — sealed versions plus
|
|
42
|
+
// lifecycle events, the metadata-only list the history panel pages through.
|
|
43
|
+
// Named `companyMdDocHistory`, not `companyMdDocVersions`: the history is the
|
|
44
|
+
// list, a version is a thing in it (see `companyMdDocVersion` below). Keyed by
|
|
45
|
+
// `docId` for the same reason `companyMdAccessRequests` is: the honest name
|
|
46
|
+
// for the stable document id, rather than propagating `companyMdDoc`'s legacy
|
|
47
|
+
// `slug` field name.
|
|
48
|
+
| { type: "companyMdDocHistory"; orgId: string; docId: string }
|
|
46
49
|
// ONE sealed version, body included.
|
|
47
50
|
//
|
|
48
51
|
// A member of its own rather than a `versionId` segment folded into the list
|
|
49
52
|
// key above, and the split is load-bearing. A restore APPENDS to the history,
|
|
50
|
-
// so it must invalidate `
|
|
53
|
+
// so it must invalidate `companyMdDocHistory` — and because a sealed version
|
|
51
54
|
// is immutable, every already-fetched body must SURVIVE that invalidation.
|
|
52
55
|
// Fold the two together and each restore evicts the whole set of historical
|
|
53
56
|
// bodies the reader just paged through, to re-fetch content that cannot have
|
package/src/resource-keys.ts
CHANGED
|
@@ -96,7 +96,7 @@ const IDENTITY_FIELDS = {
|
|
|
96
96
|
companyMdDoc: "slug",
|
|
97
97
|
companyMdContextBank: "slug",
|
|
98
98
|
companyMdAccessRequests: "docId",
|
|
99
|
-
|
|
99
|
+
companyMdDocHistory: "docId",
|
|
100
100
|
orgUnit: "unitId",
|
|
101
101
|
orgUnitChildren: "unitId",
|
|
102
102
|
orgUnitAncestors: "unitId",
|
|
@@ -226,7 +226,7 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
|
|
|
226
226
|
case "companyMdContextBank":
|
|
227
227
|
return [key.type, key.orgId, key.slug] as const;
|
|
228
228
|
case "companyMdAccessRequests":
|
|
229
|
-
case "
|
|
229
|
+
case "companyMdDocHistory":
|
|
230
230
|
return [key.type, key.orgId, key.docId] as const;
|
|
231
231
|
|
|
232
232
|
// Composite identity — subject CLASS then subject id, in that order, so the
|
package/src/resource-registry.ts
CHANGED
|
@@ -60,7 +60,7 @@ export type ResourceEntryInput = z.input<typeof ResourceEntrySchema>;
|
|
|
60
60
|
*
|
|
61
61
|
* TWO VOCABULARY CAVEATS, both deliberate and both for ADR-CONTRACTS-120:
|
|
62
62
|
*
|
|
63
|
-
* 1. `resource` here is the {@link ResourceKey} type TAG (`
|
|
63
|
+
* 1. `resource` here is the {@link ResourceKey} type TAG (`companyMdDocHistory`).
|
|
64
64
|
* The registry that currently holds live rows is the app's copy, and its
|
|
65
65
|
* `resource` values are PATH-shaped (`company-md/docs`, `orgs/:orgId/billing`).
|
|
66
66
|
* The two do not line up, and reconciling them is not this PRD's work —
|
|
@@ -73,8 +73,9 @@ export const COMPANY_MD_VERSION_RESOURCE_ENTRIES = [
|
|
|
73
73
|
{
|
|
74
74
|
// The history panel is opened, never hydrated with the page — hence
|
|
75
75
|
// background/P3. A restore appends to this list, so it is the row that
|
|
76
|
-
// takes the invalidation.
|
|
77
|
-
|
|
76
|
+
// takes the invalidation. The history is the list, a version is a thing
|
|
77
|
+
// in it — hence `companyMdDocHistory` here and `companyMdDocVersion` below.
|
|
78
|
+
resource: "companyMdDocHistory",
|
|
78
79
|
priority: "P3",
|
|
79
80
|
hydrationPhase: "background",
|
|
80
81
|
hydrationDepends: [],
|
|
@@ -96,7 +97,7 @@ export const COMPANY_MD_VERSION_RESOURCE_ENTRIES = [
|
|
|
96
97
|
resource: "companyMdDocVersion",
|
|
97
98
|
priority: "P3",
|
|
98
99
|
hydrationPhase: "background",
|
|
99
|
-
hydrationDepends: ["
|
|
100
|
+
hydrationDepends: ["companyMdDocHistory"],
|
|
100
101
|
mutationBehavior: "serial",
|
|
101
102
|
staleTimeMs: 300_000,
|
|
102
103
|
degradationSection: "core",
|