@company-semantics/contracts 44.3.0 → 44.5.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": "44.3.0",
3
+ "version": "44.5.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -80,8 +80,8 @@
80
80
  },
81
81
  "scripts": {
82
82
  "build": "tsc -b --noEmit",
83
- "typecheck": "tsc -b --noEmit",
84
- "typecheck:ci": "tsc -p scripts/ci/tsconfig.json",
83
+ "typecheck": "NODE_OPTIONS='--max-old-space-size=4096' tsc -b --noEmit",
84
+ "typecheck:ci": "NODE_OPTIONS='--max-old-space-size=4096' tsc -p scripts/ci/tsconfig.json",
85
85
  "lint:md": "markdownlint-cli2 '**/*.md' '#node_modules' '#.ralph/worktrees' '#.claude/worktrees'",
86
86
  "format": "prettier --write src/",
87
87
  "format:check": "prettier --check src/",
@@ -99,11 +99,11 @@
99
99
  "guard:quick": "GUARD_DEPENDENCY_ROOT=../ tsx ../company-semantics-ci/run-guards.ts --config scripts/ci/guard-entries.ts --quick",
100
100
  "ci:local": "pnpm typecheck && pnpm lint:md && pnpm guard:test && GUARD_DEPENDENCY_ROOT=../ pnpm guard",
101
101
  "guard": "GUARD_DEPENDENCY_ROOT=../ tsx ../company-semantics-ci/run-guards.ts --config scripts/ci/guard-entries.ts",
102
- "guard:test": "vitest run scripts/ci/__tests__",
102
+ "guard:test": "NODE_OPTIONS='--max-old-space-size=4096' vitest run scripts/ci/__tests__",
103
103
  "release": "npx tsx scripts/release.ts",
104
104
  "prepublishOnly": "echo 'ERROR: Publishing is CI-only via tag push. Use pnpm release instead.' && exit 1",
105
- "test": "bash scripts/vitest-run.sh run",
106
- "test:run": "bash scripts/vitest-run.sh run",
105
+ "test": "NODE_OPTIONS='--max-old-space-size=4096' bash scripts/vitest-run.sh run",
106
+ "test:run": "NODE_OPTIONS='--max-old-space-size=4096' bash scripts/vitest-run.sh run",
107
107
  "color": "tsx scripts/color.ts",
108
108
  "generate:spec-hash": "tsx scripts/generate-spec-hash.ts",
109
109
  "generate:spec-hash:check": "tsx scripts/generate-spec-hash.ts --check",
@@ -98,6 +98,37 @@ describe("resource-keys: system-scoped internalAdmin* types", () => {
98
98
  });
99
99
  });
100
100
 
101
+ describe("resource-keys: userMd (user-scoped singleton)", () => {
102
+ const USER_ID = "55555555-5555-4555-8555-555555555555";
103
+ const key: ResourceKey = { type: "userMd", userId: USER_ID };
104
+
105
+ it("toQueryKey produces [type, userId] with no doc identity", () => {
106
+ const qk = toQueryKey(key);
107
+ expect(qk).toEqual(["userMd", USER_ID]);
108
+ // A per-user singleton: the user IS the identity, so there is no third
109
+ // segment even though the payload is a work item (ADR-BE-487).
110
+ expect(qk).toHaveLength(2);
111
+ });
112
+
113
+ it("roundtrips through fromQueryKey", () => {
114
+ const parsed = fromQueryKey(toQueryKey(key));
115
+ expect(parsed).toEqual(key);
116
+ expect("orgId" in parsed).toBe(false);
117
+ });
118
+
119
+ it("matchesResourceKey is user-scoped: rejects another user's doc", () => {
120
+ const otherUser = "66666666-6666-4666-8666-666666666666";
121
+ expect(matchesResourceKey(toQueryKey(key), key)).toBe(true);
122
+ expect(matchesResourceKey(["userMd", otherUser], key)).toBe(false);
123
+ });
124
+
125
+ it("does not collide with the other user-scoped keys", () => {
126
+ const viewerKey: ResourceKey = { type: "viewer", userId: USER_ID };
127
+ expect(matchesResourceKey(toQueryKey(viewerKey), key)).toBe(false);
128
+ expect(matchesResourceKey(toQueryKey(key), viewerKey)).toBe(false);
129
+ });
130
+ });
131
+
101
132
  describe("resource-keys: companyMdAccessRequests (per-doc identity)", () => {
102
133
  const DOC_ID = "22222222-2222-4222-8222-222222222222";
103
134
  const key: ResourceKey = {
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '44c4c6792ad6' as const;
3
- export const SPEC_HASH_FULL = '44c4c6792ad691901fe7f76cdf1b487664a2569532944b5a2fd57b69e599f334' as const;
2
+ export const SPEC_HASH = '9bcb0e0bc2cc' as const;
3
+ export const SPEC_HASH_FULL = '9bcb0e0bc2cce27742a97dd5f7ef4cadcff268e8700f40554258301cee4e183e' as const;
@@ -2879,6 +2879,23 @@ export interface paths {
2879
2879
  patch?: never;
2880
2880
  trace?: never;
2881
2881
  };
2882
+ "/api/me/md": {
2883
+ parameters: {
2884
+ query?: never;
2885
+ header?: never;
2886
+ path?: never;
2887
+ cookie?: never;
2888
+ };
2889
+ /** Get (and lazily materialize) my personal doc */
2890
+ get: operations["getMyMd"];
2891
+ put?: never;
2892
+ post?: never;
2893
+ delete?: never;
2894
+ options?: never;
2895
+ head?: never;
2896
+ patch?: never;
2897
+ trace?: never;
2898
+ };
2882
2899
  "/api/me/action-items": {
2883
2900
  parameters: {
2884
2901
  query?: never;
@@ -5550,7 +5567,7 @@ export interface components {
5550
5567
  WorkItemDetailResponse: {
5551
5568
  id: string;
5552
5569
  /** @enum {string} */
5553
- kind: "meeting" | "comm" | "doc";
5570
+ kind: "meeting" | "comm" | "doc" | "personal";
5554
5571
  title: string;
5555
5572
  content: string;
5556
5573
  contentRedacted?: boolean;
@@ -10911,6 +10928,26 @@ export interface operations {
10911
10928
  };
10912
10929
  };
10913
10930
  };
10931
+ getMyMd: {
10932
+ parameters: {
10933
+ query?: never;
10934
+ header?: never;
10935
+ path?: never;
10936
+ cookie?: never;
10937
+ };
10938
+ requestBody?: never;
10939
+ responses: {
10940
+ /** @description The caller-owned personal doc (kind personal), created on first access with an empty body */
10941
+ 200: {
10942
+ headers: {
10943
+ [name: string]: unknown;
10944
+ };
10945
+ content: {
10946
+ "application/json": components["schemas"]["WorkItemDetailResponse"];
10947
+ };
10948
+ };
10949
+ };
10950
+ };
10914
10951
  listMyActionItems: {
10915
10952
  parameters: {
10916
10953
  query?: never;
@@ -217,7 +217,9 @@ export const CompanyMdDocResponseSchema = z.object({
217
217
  */
218
218
  export const WorkItemDetailResponseSchema = z.object({
219
219
  id: z.string(),
220
- kind: z.enum(["meeting", "comm", "doc"]),
220
+ // `personal` is the /me/md page (ADR-BE-487): a per-(org, user) singleton
221
+ // riding the work_items substrate, served by getMyMd and this detail route.
222
+ kind: z.enum(["meeting", "comm", "doc", "personal"]),
221
223
  title: z.string(),
222
224
  content: z.string(),
223
225
  /**
@@ -79,6 +79,7 @@ export const openApiRoutes = {
79
79
  '/api/me/feed': ['GET'],
80
80
  '/api/me/feed/read': ['POST'],
81
81
  '/api/me/feed/read-all': ['POST'],
82
+ '/api/me/md': ['GET'],
82
83
  '/api/me/meetings/recordings/start': ['POST'],
83
84
  '/api/me/meetings/recordings/{id}': ['DELETE'],
84
85
  '/api/me/meetings/recordings/{id}/complete': ['POST'],
@@ -113,9 +113,12 @@ export const AclListResponseSchema = z.object({
113
113
  * Who knows this entity EXISTS — a SIBLING of `visibility`, not a modifier
114
114
  * on it (ADR-CONTRACTS-093).
115
115
  *
116
- * ABSENT => this entity type has no discoverability axis (work_item,
117
- * meeting_recording). Deliberately not defaulted: `org|owners` has no honest
118
- * cross-entity value, unlike `general_access_role`'s `viewer`.
116
+ * ABSENT => the share dialog has no discoverability control for this
117
+ * entity: meeting_recording (no axis at all) and company_md (the policy is
118
+ * DERIVED server-side — 'org' for structural docs, ADR-BE-482 never
119
+ * share-governed). PRESENT only for work_item, the one stored,
120
+ * share-governed axis (ADR-BE-486). Deliberately not defaulted: `org|owners`
121
+ * has no honest cross-entity value, unlike `general_access_role`'s `viewer`.
119
122
  *
120
123
  * The policy's domain is NON-READERS only. When `visibility` is `org` every
121
124
  * member is a reader, so the stored value affects no one — it is not erased,
@@ -84,6 +84,12 @@ export type ResourceKey =
84
84
  | { type: "dismissedBanners"; userId: string }
85
85
  | { type: "userOrgs"; userId: string }
86
86
  | { type: "sessions"; userId: string }
87
+ // The viewer's personal doc (`/api/me/md`) — a per-user singleton, so it is
88
+ // keyed by userId alone with no doc identity segment. Its PAYLOAD is a work
89
+ // item, lazily materialized on first read (ADR-BE-487), but the resource is
90
+ // "this user's personal doc", not "work item <id>": a future generic
91
+ // `workItem` key would be a different resource with a different identity.
92
+ | { type: "userMd"; userId: string }
87
93
  | { type: "viewer"; userId: string };
88
94
 
89
95
  /**
@@ -131,6 +137,7 @@ const USER_SCOPED_TYPES = [
131
137
  "dismissedBanners",
132
138
  "userOrgs",
133
139
  "sessions",
140
+ "userMd",
134
141
  "viewer",
135
142
  ] as const;
136
143
 
@@ -186,6 +193,7 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
186
193
  case "dismissedBanners":
187
194
  case "userOrgs":
188
195
  case "sessions":
196
+ case "userMd":
189
197
  case "viewer":
190
198
  return [key.type, key.userId] as const;
191
199