@principal-ai/subsystems-core 0.48.1 → 0.48.2

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.
@@ -3,11 +3,11 @@
3
3
  * agrees on. Browser-safe (no `node:*`); the file-per-topic store that reads and
4
4
  * writes these lives in `./node` (see `topicStore.ts`).
5
5
  *
6
- * A **topic** is a curated bundle of trails on one subject. Two distinct named
7
- * shapes, split by id-namespace / ownership, with an explicit translation
8
- * between them — NOT one shared shape. They share most field names, but `id`
9
- * and `trailIds` live in different namespaces (local vs server), so passing one
10
- * where the other is expected corrupts the trail foreign-keys.
6
+ * A **topic** is a locally authored subject brief that may be published. Two
7
+ * distinct named shapes, split by id-namespace / ownership, with an explicit
8
+ * translation between them — NOT one shared shape. Their `id` fields live in
9
+ * different namespaces (local vs server), so passing one where the other is
10
+ * expected corrupts topic identity.
11
11
  *
12
12
  * - {@link DraftTopic} — locally-authored, what desktop persists + edits.
13
13
  * - {@link PublishedTopic} — server-authoritative, what the server returns and
@@ -15,10 +15,8 @@
15
15
  * - {@link publishedFromDraft} — the Draft -> Published projection (one-way;
16
16
  * there is no reverse hydration).
17
17
  *
18
- * The shapes mirror the over-the-wire form used by the sharing API (web-ade) so
19
- * a published topic and a locally stored one stay interchangeable on read. All
20
- * timestamps are ISO 8601 strings because this contract crosses the desktop/web
21
- * boundary.
18
+ * All timestamps are ISO 8601 strings because this contract crosses the
19
+ * desktop/web boundary.
22
20
  */
23
21
  /**
24
22
  * Status of a topic — a small structured axis plus optional human nuance.
@@ -49,7 +47,7 @@ export interface TopicStatus {
49
47
  until?: string;
50
48
  /** Structured pointer to the thing being waited on, for automations. */
51
49
  ref?: {
52
- kind: 'url' | 'pr' | 'issue' | 'topic' | 'trail';
50
+ kind: 'url' | 'pr' | 'issue' | 'topic';
53
51
  /** The address/id of the referenced thing. */
54
52
  value: string;
55
53
  /** Optional human label for display. */
@@ -114,8 +112,6 @@ export interface DraftTopic {
114
112
  title: string;
115
113
  /** Optional markdown body. */
116
114
  description?: string;
117
- /** Ordered list of LOCAL trail ids — foreign keys into the local trail store. */
118
- trailIds: string[];
119
115
  /**
120
116
  * Repositories this topic is about, as PURL strings (e.g.
121
117
  * `pkg:github/owner/repo`). The topic's own source of truth for "which repos"
@@ -151,8 +147,6 @@ export interface PublishedTopic {
151
147
  title: string;
152
148
  /** Markdown body — required on the server. */
153
149
  description: string;
154
- /** Ordered list of REMOTE trail ids. */
155
- trailIds: string[];
156
150
  /**
157
151
  * Repositories this topic is about, as PURL strings (e.g.
158
152
  * `pkg:github/owner/repo`). Travels with the topic across the wire so a
@@ -174,13 +168,10 @@ export interface PublishedTopic {
174
168
  * Inputs the {@link DraftTopic} cannot supply on its own — the namespace
175
169
  * crossing and the fields publish must enforce. The caller resolves these
176
170
  * before projecting:
177
- * - `trailIds`: LOCAL trail ids remapped to REMOTE ids (web-ade references
178
- * trails by their server id; see desktop's `resolveRemoteTrailIds`).
179
171
  * - `createdBy`: required on the server; supplied here if the draft lacks it.
180
172
  * - `visibility`: a published concept the draft never carries.
181
173
  */
182
174
  export interface PublishProjection {
183
- trailIds: string[];
184
175
  createdBy: TopicCreator;
185
176
  visibility: 'private' | 'public';
186
177
  }
@@ -188,8 +179,8 @@ export interface PublishProjection {
188
179
  * Project a {@link DraftTopic} into a {@link PublishedTopic} — the one-way
189
180
  * Draft -> Published translation. It does NOT perform any I/O (no id minting,
190
181
  * no asset upload, no network): the caller resolves the namespace-crossing and
191
- * required fields into {@link PublishProjection} first (remapping trail ids,
192
- * uploading assets, choosing visibility), and this enforces the shape.
182
+ * required fields into {@link PublishProjection} first (uploading assets,
183
+ * choosing visibility), and this enforces the shape.
193
184
  *
194
185
  * One-way by design: there is no reverse `PublishedTopic -> DraftTopic`
195
186
  * hydration. A published topic's edits write through to the server; the local
@@ -1 +1 @@
1
- {"version":3,"file":"topic-types.d.ts","sourceRoot":"","sources":["../../src/storage/topic-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"topic-types.d.ts","sourceRoot":"","sources":["../../src/storage/topic-types.ts"],"names":[],"mappings":"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"}
@@ -4,11 +4,11 @@
4
4
  * agrees on. Browser-safe (no `node:*`); the file-per-topic store that reads and
5
5
  * writes these lives in `./node` (see `topicStore.ts`).
6
6
  *
7
- * A **topic** is a curated bundle of trails on one subject. Two distinct named
8
- * shapes, split by id-namespace / ownership, with an explicit translation
9
- * between them — NOT one shared shape. They share most field names, but `id`
10
- * and `trailIds` live in different namespaces (local vs server), so passing one
11
- * where the other is expected corrupts the trail foreign-keys.
7
+ * A **topic** is a locally authored subject brief that may be published. Two
8
+ * distinct named shapes, split by id-namespace / ownership, with an explicit
9
+ * translation between them — NOT one shared shape. Their `id` fields live in
10
+ * different namespaces (local vs server), so passing one where the other is
11
+ * expected corrupts topic identity.
12
12
  *
13
13
  * - {@link DraftTopic} — locally-authored, what desktop persists + edits.
14
14
  * - {@link PublishedTopic} — server-authoritative, what the server returns and
@@ -16,10 +16,8 @@
16
16
  * - {@link publishedFromDraft} — the Draft -> Published projection (one-way;
17
17
  * there is no reverse hydration).
18
18
  *
19
- * The shapes mirror the over-the-wire form used by the sharing API (web-ade) so
20
- * a published topic and a locally stored one stay interchangeable on read. All
21
- * timestamps are ISO 8601 strings because this contract crosses the desktop/web
22
- * boundary.
19
+ * All timestamps are ISO 8601 strings because this contract crosses the
20
+ * desktop/web boundary.
23
21
  */
24
22
  Object.defineProperty(exports, "__esModule", { value: true });
25
23
  exports.publishedFromDraft = void 0;
@@ -27,8 +25,8 @@ exports.publishedFromDraft = void 0;
27
25
  * Project a {@link DraftTopic} into a {@link PublishedTopic} — the one-way
28
26
  * Draft -> Published translation. It does NOT perform any I/O (no id minting,
29
27
  * no asset upload, no network): the caller resolves the namespace-crossing and
30
- * required fields into {@link PublishProjection} first (remapping trail ids,
31
- * uploading assets, choosing visibility), and this enforces the shape.
28
+ * required fields into {@link PublishProjection} first (uploading assets,
29
+ * choosing visibility), and this enforces the shape.
32
30
  *
33
31
  * One-way by design: there is no reverse `PublishedTopic -> DraftTopic`
34
32
  * hydration. A published topic's edits write through to the server; the local
@@ -47,7 +45,6 @@ function publishedFromDraft(draft, projection) {
47
45
  id: draft.id,
48
46
  title: draft.title,
49
47
  description,
50
- trailIds: projection.trailIds,
51
48
  ...(draft.repos !== undefined ? { repos: draft.repos } : {}),
52
49
  createdAt: draft.createdAt,
53
50
  updatedAt: draft.updatedAt,
@@ -1 +1 @@
1
- {"version":3,"file":"topic-types.js","sourceRoot":"","sources":["../../src/storage/topic-types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAgLH;;;;;;;;;;;;;;GAcG;AACH,SAAgB,kBAAkB,CAChC,KAAiB,EACjB,UAA6B;IAE7B,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;QAC5B,MAAM,IAAI,KAAK,CACb,SAAS,KAAK,CAAC,EAAE,4DAA4D,CAC9E,CAAC;KACH;IACD,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,WAAW;QACX,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,UAAU,EAAE,UAAU,CAAC,UAAU;KAClC,CAAC;AACJ,CAAC;AAtBD,gDAsBC"}
1
+ {"version":3,"file":"topic-types.js","sourceRoot":"","sources":["../../src/storage/topic-types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAmKH;;;;;;;;;;;;;;GAcG;AACH,SAAgB,kBAAkB,CAChC,KAAiB,EACjB,UAA6B;IAE7B,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;QAC5B,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,CAAC,EAAE,4DAA4D,CAAC,CAAC;KAChG;IACD,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,WAAW;QACX,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,UAAU,EAAE,UAAU,CAAC,UAAU;KAClC,CAAC;AACJ,CAAC;AAnBD,gDAmBC"}
@@ -39,7 +39,6 @@ export interface TopicIndexEntry {
39
39
  id: string;
40
40
  title: string;
41
41
  descriptionPreview: string;
42
- trailCount: number;
43
42
  /** `status.state` lifted out for filtering/sorting without a file read. */
44
43
  state?: TopicStatus['state'];
45
44
  createdAt: string;
@@ -64,10 +63,8 @@ export interface MigrationResult {
64
63
  noLegacyBlob: boolean;
65
64
  }
66
65
  /**
67
- * Fields a caller may set when updating a topic. `id`, `createdAt`, and
68
- * `trailIds` are immutable here — `id`/`createdAt` are set once at creation, and
69
- * `trailIds` is foreign-keyed membership owned by the desktop's trail store, so
70
- * a topic update here can never rewrite it.
66
+ * Fields a caller may set when updating a topic. `id` and `createdAt` are set
67
+ * once at creation and cannot be changed by an update.
71
68
  */
72
69
  export type TopicUpdate = Partial<Pick<DraftTopic, 'title' | 'description' | 'status' | 'createdBy' | 'assets' | 'repos'>>;
73
70
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"topicStore.d.ts","sourceRoot":"","sources":["../../src/storage/topicStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAaH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE7D,gEAAgE;AAChE,eAAO,MAAM,aAAa,QAAgC,CAAC;AAC3D,gCAAgC;AAChC,eAAO,MAAM,UAAU,QAAgC,CAAC;AACxD,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,QAAgD,CAAC;AAKhF;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;CAClB;AA6CD,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,CAC/B,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC,CACxF,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,CAC5B,UAAU,EACV,IAAI,GAAG,WAAW,GAAG,WAAW,CACjC,GAAG;IACF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,UAAU,CAAoC;IAEtD;;;;OAIG;gBACS,OAAO,GAAE,MAAmB,EAAE,cAAc,GAAE,MAA2B;IAQ/E,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAUlC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAOtD,wEAAwE;IAClE,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAOlC,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAmBpD,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAelE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAa/C;;;;;;;;;;OAUG;IACG,qBAAqB,IAAI,OAAO,CAAC,eAAe,CAAC;YA8DzC,QAAQ;YAQR,YAAY;IAQ1B,4EAA4E;YAC9D,UAAU;YAaV,SAAS;YAUT,cAAc;YAWd,UAAU;YASV,SAAS;IAkBvB,2EAA2E;YAC7D,YAAY;IA4B1B,OAAO,CAAC,YAAY;CAWrB"}
1
+ {"version":3,"file":"topicStore.d.ts","sourceRoot":"","sources":["../../src/storage/topicStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE7D,gEAAgE;AAChE,eAAO,MAAM,aAAa,QAAgC,CAAC;AAC3D,gCAAgC;AAChC,eAAO,MAAM,UAAU,QAAgC,CAAC;AACxD,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,QAAgD,CAAC;AAKhF;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiDD,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,CAC/B,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC,CACxF,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG;IAC7E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,UAAU,CAAoC;IAEtD;;;;OAIG;gBACS,OAAO,GAAE,MAAmB,EAAE,cAAc,GAAE,MAA2B;IAQ/E,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAUlC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAOtD,wEAAwE;IAClE,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAKlC,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAkBpD,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAclE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAa/C;;;;;;;;;;OAUG;IACG,qBAAqB,IAAI,OAAO,CAAC,eAAe,CAAC;YAmEzC,QAAQ;YAQR,YAAY;IAQ1B,4EAA4E;YAC9D,UAAU;YAcV,SAAS;YAmBT,cAAc;YAWd,UAAU;YASV,SAAS;IA+BvB,2EAA2E;YAC7D,YAAY;IA2B1B,OAAO,CAAC,YAAY;CAWrB"}
@@ -50,11 +50,15 @@ const descriptionPreview = (description) => {
50
50
  return trimmed;
51
51
  return `${trimmed.slice(0, DESCRIPTION_PREVIEW_MAX - 1)}…`;
52
52
  };
53
+ /** Strip the removed topic-to-trail field from data written by older versions. */
54
+ const withoutLegacyTrailIds = (topic) => {
55
+ const { trailIds: _trailIds, ...canonical } = topic;
56
+ return canonical;
57
+ };
53
58
  const buildEntry = (topic, sizeBytes) => ({
54
59
  id: topic.id,
55
60
  title: topic.title || 'Untitled topic',
56
61
  descriptionPreview: descriptionPreview(topic.description),
57
- trailCount: topic.trailIds.length,
58
62
  state: topic.status?.state,
59
63
  createdAt: topic.createdAt,
60
64
  updatedAt: topic.updatedAt,
@@ -97,9 +101,7 @@ class TopicStore {
97
101
  /** Cheap listing straight off the manifest — no per-topic file read. */
98
102
  async list() {
99
103
  const idx = await this.getIndex();
100
- return idx.entries
101
- .slice()
102
- .sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
104
+ return idx.entries.slice().sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
103
105
  }
104
106
  async createTopic(input) {
105
107
  const idx = await this.getIndex();
@@ -112,7 +114,6 @@ class TopicStore {
112
114
  ...input,
113
115
  id,
114
116
  title: input.title || 'Untitled topic',
115
- trailIds: input.trailIds ?? [],
116
117
  createdAt: input.createdAt ?? now,
117
118
  updatedAt: input.updatedAt ?? now,
118
119
  };
@@ -126,7 +127,6 @@ class TopicStore {
126
127
  ...existing,
127
128
  ...updates,
128
129
  id: existing.id,
129
- trailIds: existing.trailIds,
130
130
  createdAt: existing.createdAt,
131
131
  updatedAt: nowIso(),
132
132
  };
@@ -174,18 +174,23 @@ class TopicStore {
174
174
  const idx = await this.getIndex();
175
175
  let migrated = 0;
176
176
  let skipped = 0;
177
- for (const legacy of blob.topics ?? []) {
178
- if (!legacy?.id) {
177
+ const legacyTopics = Array.isArray(blob.topics) ? blob.topics : [];
178
+ for (const legacyValue of legacyTopics) {
179
+ if (!legacyValue || typeof legacyValue !== 'object' || Array.isArray(legacyValue)) {
180
+ skipped++;
181
+ continue;
182
+ }
183
+ const legacy = legacyValue;
184
+ if (typeof legacy.id !== 'string' || !legacy.id) {
179
185
  skipped++;
180
186
  continue;
181
187
  }
182
188
  const now = nowIso();
183
189
  const topic = {
184
190
  ...legacy,
185
- title: legacy.title || 'Untitled topic',
186
- trailIds: legacy.trailIds ?? [],
187
- createdAt: legacy.createdAt ?? now,
188
- updatedAt: legacy.updatedAt ?? now,
191
+ title: typeof legacy.title === 'string' && legacy.title ? legacy.title : 'Untitled topic',
192
+ createdAt: typeof legacy.createdAt === 'string' ? legacy.createdAt : now,
193
+ updatedAt: typeof legacy.updatedAt === 'string' ? legacy.updatedAt : now,
189
194
  };
190
195
  try {
191
196
  await this.writeTopic(topic, idx);
@@ -232,13 +237,14 @@ class TopicStore {
232
237
  }
233
238
  /** Write a topic file and upsert its index entry. Does not generate ids. */
234
239
  async writeTopic(topic, idx) {
235
- const fileName = `${sanitizeSegment(topic.id)}.json`;
240
+ const canonicalTopic = withoutLegacyTrailIds(topic);
241
+ const fileName = `${sanitizeSegment(canonicalTopic.id)}.json`;
236
242
  const file = (0, node_path_1.join)(this.baseDir, fileName);
237
243
  await (0, promises_1.mkdir)((0, node_path_1.dirname)(file), { recursive: true });
238
- const serialized = JSON.stringify(topic, null, 2);
244
+ const serialized = JSON.stringify(canonicalTopic, null, 2);
239
245
  await (0, promises_1.writeFile)(file, serialized, 'utf8');
240
- const entry = buildEntry(topic, Buffer.byteLength(serialized, 'utf8'));
241
- const at = idx.entries.findIndex((e) => e.id === topic.id);
246
+ const entry = buildEntry(canonicalTopic, Buffer.byteLength(serialized, 'utf8'));
247
+ const at = idx.entries.findIndex((e) => e.id === canonicalTopic.id);
242
248
  if (at >= 0)
243
249
  idx.entries[at] = entry;
244
250
  else
@@ -248,7 +254,17 @@ class TopicStore {
248
254
  async readTopic(fileName) {
249
255
  try {
250
256
  const raw = await (0, promises_1.readFile)((0, node_path_1.join)(this.baseDir, fileName), 'utf8');
251
- return JSON.parse(raw);
257
+ const parsed = JSON.parse(raw);
258
+ const topic = withoutLegacyTrailIds(parsed);
259
+ if ('trailIds' in parsed) {
260
+ try {
261
+ await (0, promises_1.writeFile)((0, node_path_1.join)(this.baseDir, fileName), JSON.stringify(topic, null, 2), 'utf8');
262
+ }
263
+ catch (err) {
264
+ console.warn('[TopicStore] Failed to remove legacy trailIds from', fileName, err);
265
+ }
266
+ }
267
+ return topic;
252
268
  }
253
269
  catch (err) {
254
270
  console.error('[TopicStore] Failed to read topic', fileName, err);
@@ -280,7 +296,16 @@ class TopicStore {
280
296
  const raw = await (0, promises_1.readFile)(this.indexPath, 'utf8');
281
297
  const parsed = JSON.parse(raw);
282
298
  if (parsed?.version === 1 && Array.isArray(parsed.entries)) {
283
- return { version: 1, entries: parsed.entries };
299
+ let hadLegacyTrailCount = false;
300
+ const entries = parsed.entries.map((entry) => {
301
+ hadLegacyTrailCount || (hadLegacyTrailCount = Boolean(entry && typeof entry === 'object' && 'trailCount' in entry));
302
+ const { trailCount: _trailCount, ...canonical } = entry;
303
+ return canonical;
304
+ });
305
+ if (hadLegacyTrailCount) {
306
+ await (0, promises_1.writeFile)(this.indexPath, JSON.stringify({ version: 1, entries }, null, 2), 'utf8');
307
+ }
308
+ return { version: 1, entries };
284
309
  }
285
310
  }
286
311
  catch (err) {
@@ -315,7 +340,6 @@ class TopicStore {
315
340
  createdAt,
316
341
  updatedAt: topic.updatedAt ?? createdAt,
317
342
  title: topic.title || 'Untitled topic',
318
- trailIds: topic.trailIds ?? [],
319
343
  };
320
344
  const raw = JSON.stringify(stamped, null, 2);
321
345
  idx.entries.push(buildEntry(stamped, Buffer.byteLength(raw, 'utf8')));
@@ -1 +1 @@
1
- {"version":3,"file":"topicStore.js","sourceRoot":"","sources":["../../src/storage/topicStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"topicStore.js","sourceRoot":"","sources":["../../src/storage/topicStore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AAEH,qCAAkC;AAClC,yCAA0C;AAC1C,+CAA+F;AAG/F,gEAAgE;AACnD,QAAA,aAAa,GAAG,IAAA,gBAAI,EAAC,IAAA,iBAAO,GAAE,EAAE,YAAY,CAAC,CAAC;AAC3D,gCAAgC;AACnB,QAAA,UAAU,GAAG,IAAA,gBAAI,EAAC,qBAAa,EAAE,QAAQ,CAAC,CAAC;AACxD,wDAAwD;AAC3C,QAAA,kBAAkB,GAAG,IAAA,gBAAI,EAAC,IAAA,iBAAO,GAAE,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;AAEhF,MAAM,cAAc,GAAG,aAAa,CAAC;AACrC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AA2BpC,MAAM,UAAU,GAAG,GAAgB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;AAEpE,MAAM,eAAe,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;AAE1F,MAAM,MAAM,GAAG,GAAW,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAEtD,8EAA8E;AAC9E,MAAM,eAAe,GAAG,GAAW,EAAE,CACnC,SAAS,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAEnE,MAAM,kBAAkB,GAAG,CAAC,WAAoB,EAAU,EAAE;IAC1D,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,OAAO,CAAC,MAAM,IAAI,uBAAuB;QAAE,OAAO,OAAO,CAAC;IAC9D,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,uBAAuB,GAAG,CAAC,CAAC,GAAG,CAAC;AAC7D,CAAC,CAAC;AAEF,kFAAkF;AAClF,MAAM,qBAAqB,GAAG,CAAC,KAAa,EAA2B,EAAE;IACvE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,GAAG,KAAgC,CAAC;IAC/E,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,KAAiB,EAAE,SAAiB,EAAmB,EAAE,CAAC,CAAC;IAC7E,EAAE,EAAE,KAAK,CAAC,EAAE;IACZ,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,gBAAgB;IACtC,kBAAkB,EAAE,kBAAkB,CAAC,KAAK,CAAC,WAAW,CAAC;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK;IAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;IAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;IAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;IAC1B,SAAS,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;IAC1C,SAAS;IACT,QAAQ,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO;CAC9C,CAAC,CAAC;AA2CH,MAAa,UAAU;IAOrB;;;;OAIG;IACH,YAAY,UAAkB,kBAAU,EAAE,iBAAyB,0BAAkB;QAR7E,UAAK,GAAuB,IAAI,CAAC;QACjC,eAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;QAQpD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,IAAA,gBAAI,EAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED,yBAAyB;IAEzB,KAAK,CAAC,SAAS;QACb,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE;YAC/B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACnD,IAAI,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SAC/B;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,wEAAwE;IACxE,KAAK,CAAC,IAAI;QACR,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAkB;QAClC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,eAAe,EAAE,CAAC;QACjD,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;YACxC,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;SACzD;QACD,MAAM,KAAK,GAAG;YACZ,GAAG,KAAK;YACR,EAAE;YACF,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,gBAAgB;YACtC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,GAAG;YACjC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,GAAG;SACpB,CAAC;QAChB,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,OAAoB;QAChD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClD,MAAM,IAAI,GAAe;YACvB,GAAG,QAAQ;YACX,GAAG,OAAO;YACV,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,SAAS,EAAE,MAAM,EAAE;SACpB,CAAC;QACF,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU;QAC1B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,IAAI,QAAQ,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC/B,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC3C,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAChC,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wBAAwB;IAExB;;;;;;;;;;OAUG;IACH,KAAK,CAAC,qBAAqB;QACzB,IAAI,GAAW,CAAC;QAChB,IAAI;YACF,GAAG,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;SACnD;QAAC,MAAM;YACN,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;SACxD;QAED,IAAI,IAAsB,CAAC;QAC3B,IAAI;YACF,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;SAC5C;QAAC,MAAM;YACN,MAAM,IAAI,KAAK,CACb,yBAAyB,IAAI,CAAC,cAAc,oCAAoC,CACjF,CAAC;SACH;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE;YACtC,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;gBACjF,OAAO,EAAE,CAAC;gBACV,SAAS;aACV;YACD,MAAM,MAAM,GAAG,WAAsC,CAAC;YACtD,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE;gBAC/C,OAAO,EAAE,CAAC;gBACV,SAAS;aACV;YACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG;gBACZ,GAAG,MAAM;gBACT,KAAK,EAAE,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB;gBACzF,SAAS,EAAE,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;gBACxE,SAAS,EAAE,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;aAC3D,CAAC;YAChB,IAAI;gBACF,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBAClC,QAAQ,EAAE,CAAC;aACZ;YAAC,MAAM;gBACN,OAAO,EAAE,CAAC;aACX;SACF;QAED,MAAM,UAAU,GAAG,GAAG,IAAI,CAAC,cAAc,MAAM,CAAC;QAChD,IAAI,QAA4B,CAAC;QACjC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE;YACxC,IAAI;gBACF,MAAM,IAAA,iBAAM,EAAC,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;gBAC9C,QAAQ,GAAG,UAAU,CAAC;aACvB;YAAC,MAAM;gBACN,gEAAgE;aACjE;SACF;QAED,OAAO;YACL,QAAQ;YACR,OAAO;YACP,YAAY,EAAE,KAAK;YACnB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9C,CAAC;IACJ,CAAC;IAED,wBAAwB;IAEhB,KAAK,CAAC,QAAQ;QACpB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;YACf,MAAM,IAAA,gBAAK,EAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;SACrC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,GAAgB,EAAE,EAAU;QACrD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;QAC/D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,4EAA4E;IACpE,KAAK,CAAC,UAAU,CAAC,KAAiB,EAAE,GAAgB;QAC1D,MAAM,cAAc,GAAG,qBAAqB,CAAC,KAAK,CAA0B,CAAC;QAC7E,MAAM,QAAQ,GAAG,GAAG,eAAe,CAAC,cAAc,CAAC,EAAE,CAAC,OAAO,CAAC;QAC9D,MAAM,IAAI,GAAG,IAAA,gBAAI,EAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,IAAA,gBAAK,EAAC,IAAA,mBAAO,EAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAA,oBAAS,EAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;QAChF,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,cAAc,CAAC,EAAE,CAAC,CAAC;QACpE,IAAI,EAAE,IAAI,CAAC;YAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;;YAChC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,QAAgB;QACtC,IAAI;YACF,MAAM,GAAG,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAA,gBAAI,EAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;YAC1D,MAAM,KAAK,GAAG,qBAAqB,CAAC,MAAM,CAA0B,CAAC;YACrE,IAAI,UAAU,IAAI,MAAM,EAAE;gBACxB,IAAI;oBACF,MAAM,IAAA,oBAAS,EAAC,IAAA,gBAAI,EAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;iBACvF;gBAAC,OAAO,GAAG,EAAE;oBACZ,OAAO,CAAC,IAAI,CAAC,oDAAoD,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;iBACnF;aACF;YACD,OAAO,KAAK,CAAC;SACd;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;SACb;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,QAAgB;QAC3C,IAAI;YACF,MAAM,IAAA,iBAAM,EAAC,IAAA,gBAAI,EAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;SAC5C;QAAC,OAAO,GAAG,EAAE;YACZ,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;YACjD,IAAI,IAAI,KAAK,QAAQ,EAAE;gBACrB,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;aAC/D;SACF;IACH,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,IAAY;QACnC,IAAI;YACF,MAAM,IAAA,iBAAM,EAAC,IAAI,CAAC,CAAC;YACnB,OAAO,IAAI,CAAC;SACb;QAAC,MAAM;YACN,OAAO,KAAK,CAAC;SACd;IACH,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,IAAI;YACF,MAAM,GAAG,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;YACvD,IAAI,MAAM,EAAE,OAAO,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;gBAC1D,IAAI,mBAAmB,GAAG,KAAK,CAAC;gBAChC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC3C,mBAAmB,KAAnB,mBAAmB,GAAK,OAAO,CAC7B,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,YAAY,IAAI,KAAK,CAC5D,EAAC;oBACF,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,SAAS,EAAE,GAAG,KAEjD,CAAC;oBACF,OAAO,SAAS,CAAC;gBACnB,CAAC,CAAC,CAAC;gBACH,IAAI,mBAAmB,EAAE;oBACvB,MAAM,IAAA,oBAAS,EAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;iBAC3F;gBACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;aAChC;SACF;QAAC,OAAO,GAAG,EAAE;YACZ,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;YACjD,IAAI,IAAI,KAAK,QAAQ,EAAE;gBACrB,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,GAAG,CAAC,CAAC;aACtE;SACF;QACD,mEAAmE;QACnE,oDAAoD;QACpD,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;IAC7B,CAAC;IAED,2EAA2E;IACnE,KAAK,CAAC,YAAY;QACxB,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,IAAI,KAAe,CAAC;QACpB,IAAI;YACF,KAAK,GAAG,MAAM,IAAA,kBAAO,EAAC,IAAI,CAAC,OAAO,CAAC,CAAC;SACrC;QAAC,MAAM;YACN,OAAO,GAAG,CAAC;SACZ;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,IAAI,IAAI,KAAK,cAAc,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,SAAS;YACjE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,KAAK,EAAE,EAAE;gBAAE,SAAS;YACzB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;YAC9C,MAAM,OAAO,GAAG;gBACd,GAAG,KAAK;gBACR,SAAS;gBACT,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,SAAS;gBACvC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,gBAAgB;aACzB,CAAC;YAChB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7C,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;SACvE;QACD,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;QACjB,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU;aAC9B,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aACtB,IAAI,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,IAAA,gBAAK,EAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,MAAM,IAAA,oBAAS,EAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QACL,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF;AAlTD,gCAkTC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/subsystems-core",
3
- "version": "0.48.1",
3
+ "version": "0.48.2",
4
4
  "description": "Subsystem model types and agent-session tooling for Principal AI",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -3,11 +3,11 @@
3
3
  * agrees on. Browser-safe (no `node:*`); the file-per-topic store that reads and
4
4
  * writes these lives in `./node` (see `topicStore.ts`).
5
5
  *
6
- * A **topic** is a curated bundle of trails on one subject. Two distinct named
7
- * shapes, split by id-namespace / ownership, with an explicit translation
8
- * between them — NOT one shared shape. They share most field names, but `id`
9
- * and `trailIds` live in different namespaces (local vs server), so passing one
10
- * where the other is expected corrupts the trail foreign-keys.
6
+ * A **topic** is a locally authored subject brief that may be published. Two
7
+ * distinct named shapes, split by id-namespace / ownership, with an explicit
8
+ * translation between them — NOT one shared shape. Their `id` fields live in
9
+ * different namespaces (local vs server), so passing one where the other is
10
+ * expected corrupts topic identity.
11
11
  *
12
12
  * - {@link DraftTopic} — locally-authored, what desktop persists + edits.
13
13
  * - {@link PublishedTopic} — server-authoritative, what the server returns and
@@ -15,10 +15,8 @@
15
15
  * - {@link publishedFromDraft} — the Draft -> Published projection (one-way;
16
16
  * there is no reverse hydration).
17
17
  *
18
- * The shapes mirror the over-the-wire form used by the sharing API (web-ade) so
19
- * a published topic and a locally stored one stay interchangeable on read. All
20
- * timestamps are ISO 8601 strings because this contract crosses the desktop/web
21
- * boundary.
18
+ * All timestamps are ISO 8601 strings because this contract crosses the
19
+ * desktop/web boundary.
22
20
  */
23
21
 
24
22
  /**
@@ -40,13 +38,7 @@ export interface TopicStatus {
40
38
  * `new-thought`, so legacy topics and renamed values need no migration.
41
39
  */
42
40
  state:
43
- | 'new-thought'
44
- | 'working'
45
- | 'paused'
46
- | 'waiting'
47
- | 'done-for-now'
48
- | 'deprecated'
49
- | 'abandoned';
41
+ 'new-thought' | 'working' | 'paused' | 'waiting' | 'done-for-now' | 'deprecated' | 'abandoned';
50
42
  /** Free-form text shown in place of the default label for the state. */
51
43
  label?: string;
52
44
  /** External blocker description. Meaningful when `state` is `waiting`. */
@@ -57,7 +49,7 @@ export interface TopicStatus {
57
49
  until?: string;
58
50
  /** Structured pointer to the thing being waited on, for automations. */
59
51
  ref?: {
60
- kind: 'url' | 'pr' | 'issue' | 'topic' | 'trail';
52
+ kind: 'url' | 'pr' | 'issue' | 'topic';
61
53
  /** The address/id of the referenced thing. */
62
54
  value: string;
63
55
  /** Optional human label for display. */
@@ -122,8 +114,6 @@ export interface DraftTopic {
122
114
  title: string;
123
115
  /** Optional markdown body. */
124
116
  description?: string;
125
- /** Ordered list of LOCAL trail ids — foreign keys into the local trail store. */
126
- trailIds: string[];
127
117
  /**
128
118
  * Repositories this topic is about, as PURL strings (e.g.
129
119
  * `pkg:github/owner/repo`). The topic's own source of truth for "which repos"
@@ -160,8 +150,6 @@ export interface PublishedTopic {
160
150
  title: string;
161
151
  /** Markdown body — required on the server. */
162
152
  description: string;
163
- /** Ordered list of REMOTE trail ids. */
164
- trailIds: string[];
165
153
  /**
166
154
  * Repositories this topic is about, as PURL strings (e.g.
167
155
  * `pkg:github/owner/repo`). Travels with the topic across the wire so a
@@ -184,13 +172,10 @@ export interface PublishedTopic {
184
172
  * Inputs the {@link DraftTopic} cannot supply on its own — the namespace
185
173
  * crossing and the fields publish must enforce. The caller resolves these
186
174
  * before projecting:
187
- * - `trailIds`: LOCAL trail ids remapped to REMOTE ids (web-ade references
188
- * trails by their server id; see desktop's `resolveRemoteTrailIds`).
189
175
  * - `createdBy`: required on the server; supplied here if the draft lacks it.
190
176
  * - `visibility`: a published concept the draft never carries.
191
177
  */
192
178
  export interface PublishProjection {
193
- trailIds: string[];
194
179
  createdBy: TopicCreator;
195
180
  visibility: 'private' | 'public';
196
181
  }
@@ -199,8 +184,8 @@ export interface PublishProjection {
199
184
  * Project a {@link DraftTopic} into a {@link PublishedTopic} — the one-way
200
185
  * Draft -> Published translation. It does NOT perform any I/O (no id minting,
201
186
  * no asset upload, no network): the caller resolves the namespace-crossing and
202
- * required fields into {@link PublishProjection} first (remapping trail ids,
203
- * uploading assets, choosing visibility), and this enforces the shape.
187
+ * required fields into {@link PublishProjection} first (uploading assets,
188
+ * choosing visibility), and this enforces the shape.
204
189
  *
205
190
  * One-way by design: there is no reverse `PublishedTopic -> DraftTopic`
206
191
  * hydration. A published topic's edits write through to the server; the local
@@ -212,19 +197,16 @@ export interface PublishProjection {
212
197
  */
213
198
  export function publishedFromDraft(
214
199
  draft: DraftTopic,
215
- projection: PublishProjection,
200
+ projection: PublishProjection
216
201
  ): PublishedTopic {
217
202
  const description = (draft.description ?? '').trim();
218
203
  if (description.length === 0) {
219
- throw new Error(
220
- `Topic ${draft.id} has no description; a description is required to publish.`,
221
- );
204
+ throw new Error(`Topic ${draft.id} has no description; a description is required to publish.`);
222
205
  }
223
206
  return {
224
207
  id: draft.id,
225
208
  title: draft.title,
226
209
  description,
227
- trailIds: projection.trailIds,
228
210
  ...(draft.repos !== undefined ? { repos: draft.repos } : {}),
229
211
  createdAt: draft.createdAt,
230
212
  updatedAt: draft.updatedAt,
@@ -28,7 +28,7 @@ afterEach(() => {
28
28
  describe('CRUD', () => {
29
29
  test('create generates an id, persists a file, and reads back', async () => {
30
30
  const store = makeStore();
31
- const created = await store.createTopic({ title: 'Auth flow', trailIds: [] });
31
+ const created = await store.createTopic({ title: 'Auth flow' });
32
32
  expect(created.id).toMatch(/^topic-/);
33
33
  expect(existsSync(join(topicsDir, `${created.id}.json`))).toBe(true);
34
34
 
@@ -38,20 +38,22 @@ describe('CRUD', () => {
38
38
 
39
39
  test('create honors an explicit id and rejects duplicates', async () => {
40
40
  const store = makeStore();
41
- await store.createTopic({ id: 'topic-x', title: 'A', trailIds: [] });
42
- await expect(
43
- store.createTopic({ id: 'topic-x', title: 'B', trailIds: [] }),
44
- ).rejects.toThrow(/already exists/);
41
+ await store.createTopic({ id: 'topic-x', title: 'A' });
42
+ await expect(store.createTopic({ id: 'topic-x', title: 'B' })).rejects.toThrow(
43
+ /already exists/
44
+ );
45
45
  });
46
46
 
47
- test('update changes fields but never id/createdAt/trailIds', async () => {
47
+ test('update changes fields but never id/createdAt', async () => {
48
48
  const store = makeStore();
49
- const t = await store.createTopic({ id: 'topic-u', title: 'A', trailIds: ['t1'] });
50
- const updated = await store.updateTopic('topic-u', { title: 'B', description: 'hi' });
49
+ const t = await store.createTopic({ id: 'topic-u', title: 'A' });
50
+ const updated = await store.updateTopic('topic-u', {
51
+ title: 'B',
52
+ description: 'hi',
53
+ });
51
54
  expect(updated.title).toBe('B');
52
55
  expect(updated.description).toBe('hi');
53
56
  expect(updated.createdAt).toBe(t.createdAt);
54
- expect(updated.trailIds).toEqual(['t1']);
55
57
  });
56
58
 
57
59
  test('repos (PURL strings) persist on create and are updatable', async () => {
@@ -59,7 +61,6 @@ describe('CRUD', () => {
59
61
  const created = await store.createTopic({
60
62
  id: 'topic-repos',
61
63
  title: 'Repo-scoped',
62
- trailIds: [],
63
64
  repos: ['pkg:github/acme/web'],
64
65
  });
65
66
  expect(created.repos).toEqual(['pkg:github/acme/web']);
@@ -78,22 +79,21 @@ describe('CRUD', () => {
78
79
  id: 'topic-pub',
79
80
  title: 'Pub',
80
81
  description: 'has a description',
81
- trailIds: ['local-1'],
82
82
  repos: ['pkg:github/acme/web'],
83
83
  createdAt: '2026-01-01T00:00:00.000Z',
84
84
  updatedAt: '2026-01-02T00:00:00.000Z',
85
85
  };
86
86
  const published = publishedFromDraft(draft, {
87
- trailIds: ['remote-1'],
88
87
  createdBy: { githubId: 1, githubLogin: 'u1' },
89
88
  visibility: 'private',
90
89
  });
91
90
  expect(published.repos).toEqual(['pkg:github/acme/web']);
91
+ expect(published).not.toHaveProperty('trailIds');
92
92
  });
93
93
 
94
94
  test('delete removes the file and the index entry', async () => {
95
95
  const store = makeStore();
96
- await store.createTopic({ id: 'topic-d', title: 'D', trailIds: [] });
96
+ await store.createTopic({ id: 'topic-d', title: 'D' });
97
97
  expect(await store.deleteTopic('topic-d')).toBe(true);
98
98
  expect(existsSync(join(topicsDir, 'topic-d.json'))).toBe(false);
99
99
  expect(await store.getTopic('topic-d')).toBeNull();
@@ -103,7 +103,7 @@ describe('CRUD', () => {
103
103
  test('no eviction cap — many topics all survive', async () => {
104
104
  const store = makeStore();
105
105
  for (let i = 0; i < 120; i++) {
106
- await store.createTopic({ id: `topic-${i}`, title: `T${i}`, trailIds: [] });
106
+ await store.createTopic({ id: `topic-${i}`, title: `T${i}` });
107
107
  }
108
108
  expect((await store.getTopics()).length).toBe(120);
109
109
  });
@@ -112,30 +112,60 @@ describe('CRUD', () => {
112
112
  describe('index rebuild', () => {
113
113
  test('a corrupt _index.json is rebuilt by scanning topic files', async () => {
114
114
  const store = makeStore();
115
- await store.createTopic({ id: 'topic-r', title: 'R', trailIds: ['z'] });
115
+ await store.createTopic({ id: 'topic-r', title: 'R' });
116
116
 
117
117
  // Corrupt the manifest and force a fresh store to rebuild from disk.
118
118
  writeFileSync(join(topicsDir, '_index.json'), 'not json', 'utf8');
119
119
  const fresh = makeStore();
120
120
  const list = await fresh.list();
121
121
  expect(list.map((e) => e.id)).toContain('topic-r');
122
- expect(list.find((e) => e.id === 'topic-r')?.trailCount).toBe(1);
122
+ expect(list.find((e) => e.id === 'topic-r')).not.toHaveProperty('trailCount');
123
+ expect(readFileSync(join(topicsDir, '_index.json'), 'utf8')).not.toContain('trailCount');
124
+ });
125
+
126
+ test('legacy topic files drop trailIds but preserve other fields', async () => {
127
+ const store = makeStore();
128
+ await store.createTopic({
129
+ id: 'topic-legacy',
130
+ title: 'Legacy',
131
+ repos: ['pkg:github/a/b'],
132
+ });
133
+ const topicPath = join(topicsDir, 'topic-legacy.json');
134
+ const legacy = {
135
+ ...JSON.parse(readFileSync(topicPath, 'utf8')),
136
+ trailIds: ['old-trail'],
137
+ description: 'Keep this description',
138
+ };
139
+ writeFileSync(topicPath, JSON.stringify(legacy), 'utf8');
140
+
141
+ const loaded = await store.getTopic('topic-legacy');
142
+
143
+ expect(loaded).toMatchObject({
144
+ id: 'topic-legacy',
145
+ title: 'Legacy',
146
+ description: 'Keep this description',
147
+ repos: ['pkg:github/a/b'],
148
+ });
149
+ expect(loaded).not.toHaveProperty('trailIds');
150
+ expect(JSON.parse(readFileSync(topicPath, 'utf8'))).not.toHaveProperty('trailIds');
123
151
  });
124
152
  });
125
153
 
126
154
  describe('migration', () => {
127
- const seedLegacy = (topics: Partial<DraftTopic>[]) => {
128
- writeFileSync(
129
- legacyBlob,
130
- JSON.stringify({ version: '1.0.0', topics }, null, 2),
131
- 'utf8',
132
- );
155
+ const seedLegacy = (topics: Record<string, unknown>[]) => {
156
+ writeFileSync(legacyBlob, JSON.stringify({ version: '1.0.0', topics }, null, 2), 'utf8');
133
157
  };
134
158
 
135
159
  test('fans the blob out to file-per-topic and backs up the blob', async () => {
136
160
  seedLegacy([
137
- { id: 'topic-1', title: 'One', trailIds: ['a'], createdAt: '2026-01-01T00:00:00.000Z', updatedAt: '2026-01-02T00:00:00.000Z' },
138
- { id: 'topic-2', title: 'Two', trailIds: [] },
161
+ {
162
+ id: 'topic-1',
163
+ title: 'One',
164
+ trailIds: ['a'],
165
+ createdAt: '2026-01-01T00:00:00.000Z',
166
+ updatedAt: '2026-01-02T00:00:00.000Z',
167
+ },
168
+ { id: 'topic-2', title: 'Two', trailIds: [], repos: ['pkg:github/a/b'] },
139
169
  ]);
140
170
  const store = makeStore();
141
171
  const result = await store.migrateFromLegacyBlob();
@@ -149,6 +179,12 @@ describe('migration', () => {
149
179
  const one = await store.getTopic('topic-1');
150
180
  expect(one?.title).toBe('One');
151
181
  expect(one?.createdAt).toBe('2026-01-01T00:00:00.000Z');
182
+ expect(one).not.toHaveProperty('trailIds');
183
+ expect(await store.getTopic('topic-2')).toMatchObject({
184
+ title: 'Two',
185
+ repos: ['pkg:github/a/b'],
186
+ });
187
+ expect(await store.getTopic('topic-2')).not.toHaveProperty('trailIds');
152
188
  expect(existsSync(join(topicsDir, 'topic-1.json'))).toBe(true);
153
189
  });
154
190
 
@@ -26,15 +26,7 @@
26
26
 
27
27
  import { homedir } from 'node:os';
28
28
  import { dirname, join } from 'node:path';
29
- import {
30
- mkdir,
31
- readFile,
32
- writeFile,
33
- unlink,
34
- readdir,
35
- rename,
36
- access,
37
- } from 'node:fs/promises';
29
+ import { mkdir, readFile, writeFile, unlink, readdir, rename, access } from 'node:fs/promises';
38
30
  import type { DraftTopic, TopicStatus } from './topic-types';
39
31
 
40
32
  /** Root of the global, cross-repo principal narration store. */
@@ -56,7 +48,6 @@ export interface TopicIndexEntry {
56
48
  id: string;
57
49
  title: string;
58
50
  descriptionPreview: string;
59
- trailCount: number;
60
51
  /** `status.state` lifted out for filtering/sorting without a file read. */
61
52
  state?: TopicStatus['state'];
62
53
  createdAt: string;
@@ -75,8 +66,7 @@ interface IndexFileV1 {
75
66
 
76
67
  const emptyIndex = (): IndexFileV1 => ({ version: 1, entries: [] });
77
68
 
78
- const sanitizeSegment = (value: string): string =>
79
- value.replace(/[^A-Za-z0-9._-]/g, '_');
69
+ const sanitizeSegment = (value: string): string => value.replace(/[^A-Za-z0-9._-]/g, '_');
80
70
 
81
71
  const nowIso = (): string => new Date().toISOString();
82
72
 
@@ -91,11 +81,16 @@ const descriptionPreview = (description?: string): string => {
91
81
  return `${trimmed.slice(0, DESCRIPTION_PREVIEW_MAX - 1)}…`;
92
82
  };
93
83
 
84
+ /** Strip the removed topic-to-trail field from data written by older versions. */
85
+ const withoutLegacyTrailIds = (topic: object): Record<string, unknown> => {
86
+ const { trailIds: _trailIds, ...canonical } = topic as Record<string, unknown>;
87
+ return canonical;
88
+ };
89
+
94
90
  const buildEntry = (topic: DraftTopic, sizeBytes: number): TopicIndexEntry => ({
95
91
  id: topic.id,
96
92
  title: topic.title || 'Untitled topic',
97
93
  descriptionPreview: descriptionPreview(topic.description),
98
- trailCount: topic.trailIds.length,
99
94
  state: topic.status?.state,
100
95
  createdAt: topic.createdAt,
101
96
  updatedAt: topic.updatedAt,
@@ -108,7 +103,7 @@ const buildEntry = (topic: DraftTopic, sizeBytes: number): TopicIndexEntry => ({
108
103
  /** Shape the legacy `~/.alexandria/topics.json` blob is parsed as. */
109
104
  interface LegacyTopicsBlob {
110
105
  version?: string;
111
- topics?: DraftTopic[];
106
+ topics?: unknown[];
112
107
  }
113
108
 
114
109
  export interface MigrationResult {
@@ -123,10 +118,8 @@ export interface MigrationResult {
123
118
  }
124
119
 
125
120
  /**
126
- * Fields a caller may set when updating a topic. `id`, `createdAt`, and
127
- * `trailIds` are immutable here — `id`/`createdAt` are set once at creation, and
128
- * `trailIds` is foreign-keyed membership owned by the desktop's trail store, so
129
- * a topic update here can never rewrite it.
121
+ * Fields a caller may set when updating a topic. `id` and `createdAt` are set
122
+ * once at creation and cannot be changed by an update.
130
123
  */
131
124
  export type TopicUpdate = Partial<
132
125
  Pick<DraftTopic, 'title' | 'description' | 'status' | 'createdBy' | 'assets' | 'repos'>
@@ -142,10 +135,7 @@ export type TopicUpdate = Partial<
142
135
  * onto a still-required `id: string` would NOT widen it — an intersection keeps
143
136
  * the stricter member — which would leave `id` wrongly required for callers.
144
137
  */
145
- export type TopicCreate = Omit<
146
- DraftTopic,
147
- 'id' | 'createdAt' | 'updatedAt'
148
- > & {
138
+ export type TopicCreate = Omit<DraftTopic, 'id' | 'createdAt' | 'updatedAt'> & {
149
139
  id?: string;
150
140
  createdAt?: string;
151
141
  updatedAt?: string;
@@ -191,9 +181,7 @@ export class TopicStore {
191
181
  /** Cheap listing straight off the manifest — no per-topic file read. */
192
182
  async list(): Promise<TopicIndexEntry[]> {
193
183
  const idx = await this.getIndex();
194
- return idx.entries
195
- .slice()
196
- .sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
184
+ return idx.entries.slice().sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
197
185
  }
198
186
 
199
187
  async createTopic(input: TopicCreate): Promise<DraftTopic> {
@@ -203,14 +191,13 @@ export class TopicStore {
203
191
  if (idx.entries.some((e) => e.id === id)) {
204
192
  throw new Error(`Topic with id '${id}' already exists`);
205
193
  }
206
- const topic: DraftTopic = {
194
+ const topic = {
207
195
  ...input,
208
196
  id,
209
197
  title: input.title || 'Untitled topic',
210
- trailIds: input.trailIds ?? [],
211
198
  createdAt: input.createdAt ?? now,
212
199
  updatedAt: input.updatedAt ?? now,
213
- };
200
+ } as DraftTopic;
214
201
  await this.writeTopic(topic, idx);
215
202
  return topic;
216
203
  }
@@ -222,7 +209,6 @@ export class TopicStore {
222
209
  ...existing,
223
210
  ...updates,
224
211
  id: existing.id,
225
- trailIds: existing.trailIds,
226
212
  createdAt: existing.createdAt,
227
213
  updatedAt: nowIso(),
228
214
  };
@@ -267,26 +253,31 @@ export class TopicStore {
267
253
  blob = JSON.parse(raw) as LegacyTopicsBlob;
268
254
  } catch {
269
255
  throw new Error(
270
- `Legacy topics blob at ${this.legacyBlobPath} is not valid JSON; not migrating.`,
256
+ `Legacy topics blob at ${this.legacyBlobPath} is not valid JSON; not migrating.`
271
257
  );
272
258
  }
273
259
 
274
260
  const idx = await this.getIndex();
275
261
  let migrated = 0;
276
262
  let skipped = 0;
277
- for (const legacy of blob.topics ?? []) {
278
- if (!legacy?.id) {
263
+ const legacyTopics = Array.isArray(blob.topics) ? blob.topics : [];
264
+ for (const legacyValue of legacyTopics) {
265
+ if (!legacyValue || typeof legacyValue !== 'object' || Array.isArray(legacyValue)) {
266
+ skipped++;
267
+ continue;
268
+ }
269
+ const legacy = legacyValue as Record<string, unknown>;
270
+ if (typeof legacy.id !== 'string' || !legacy.id) {
279
271
  skipped++;
280
272
  continue;
281
273
  }
282
274
  const now = nowIso();
283
- const topic: DraftTopic = {
275
+ const topic = {
284
276
  ...legacy,
285
- title: legacy.title || 'Untitled topic',
286
- trailIds: legacy.trailIds ?? [],
287
- createdAt: legacy.createdAt ?? now,
288
- updatedAt: legacy.updatedAt ?? now,
289
- };
277
+ title: typeof legacy.title === 'string' && legacy.title ? legacy.title : 'Untitled topic',
278
+ createdAt: typeof legacy.createdAt === 'string' ? legacy.createdAt : now,
279
+ updatedAt: typeof legacy.updatedAt === 'string' ? legacy.updatedAt : now,
280
+ } as DraftTopic;
290
281
  try {
291
282
  await this.writeTopic(topic, idx);
292
283
  migrated++;
@@ -334,13 +325,14 @@ export class TopicStore {
334
325
 
335
326
  /** Write a topic file and upsert its index entry. Does not generate ids. */
336
327
  private async writeTopic(topic: DraftTopic, idx: IndexFileV1): Promise<void> {
337
- const fileName = `${sanitizeSegment(topic.id)}.json`;
328
+ const canonicalTopic = withoutLegacyTrailIds(topic) as unknown as DraftTopic;
329
+ const fileName = `${sanitizeSegment(canonicalTopic.id)}.json`;
338
330
  const file = join(this.baseDir, fileName);
339
331
  await mkdir(dirname(file), { recursive: true });
340
- const serialized = JSON.stringify(topic, null, 2);
332
+ const serialized = JSON.stringify(canonicalTopic, null, 2);
341
333
  await writeFile(file, serialized, 'utf8');
342
- const entry = buildEntry(topic, Buffer.byteLength(serialized, 'utf8'));
343
- const at = idx.entries.findIndex((e) => e.id === topic.id);
334
+ const entry = buildEntry(canonicalTopic, Buffer.byteLength(serialized, 'utf8'));
335
+ const at = idx.entries.findIndex((e) => e.id === canonicalTopic.id);
344
336
  if (at >= 0) idx.entries[at] = entry;
345
337
  else idx.entries.push(entry);
346
338
  await this.persistIndex();
@@ -349,7 +341,16 @@ export class TopicStore {
349
341
  private async readTopic(fileName: string): Promise<DraftTopic | null> {
350
342
  try {
351
343
  const raw = await readFile(join(this.baseDir, fileName), 'utf8');
352
- return JSON.parse(raw) as DraftTopic;
344
+ const parsed = JSON.parse(raw) as Record<string, unknown>;
345
+ const topic = withoutLegacyTrailIds(parsed) as unknown as DraftTopic;
346
+ if ('trailIds' in parsed) {
347
+ try {
348
+ await writeFile(join(this.baseDir, fileName), JSON.stringify(topic, null, 2), 'utf8');
349
+ } catch (err) {
350
+ console.warn('[TopicStore] Failed to remove legacy trailIds from', fileName, err);
351
+ }
352
+ }
353
+ return topic;
353
354
  } catch (err) {
354
355
  console.error('[TopicStore] Failed to read topic', fileName, err);
355
356
  return null;
@@ -381,7 +382,20 @@ export class TopicStore {
381
382
  const raw = await readFile(this.indexPath, 'utf8');
382
383
  const parsed = JSON.parse(raw) as Partial<IndexFileV1>;
383
384
  if (parsed?.version === 1 && Array.isArray(parsed.entries)) {
384
- return { version: 1, entries: parsed.entries as TopicIndexEntry[] };
385
+ let hadLegacyTrailCount = false;
386
+ const entries = parsed.entries.map((entry) => {
387
+ hadLegacyTrailCount ||= Boolean(
388
+ entry && typeof entry === 'object' && 'trailCount' in entry
389
+ );
390
+ const { trailCount: _trailCount, ...canonical } = entry as TopicIndexEntry & {
391
+ trailCount?: number;
392
+ };
393
+ return canonical;
394
+ });
395
+ if (hadLegacyTrailCount) {
396
+ await writeFile(this.indexPath, JSON.stringify({ version: 1, entries }, null, 2), 'utf8');
397
+ }
398
+ return { version: 1, entries };
385
399
  }
386
400
  } catch (err) {
387
401
  const code = (err as NodeJS.ErrnoException).code;
@@ -408,13 +422,12 @@ export class TopicStore {
408
422
  const topic = await this.readTopic(name);
409
423
  if (!topic?.id) continue;
410
424
  const createdAt = topic.createdAt ?? nowIso();
411
- const stamped: DraftTopic = {
425
+ const stamped = {
412
426
  ...topic,
413
427
  createdAt,
414
428
  updatedAt: topic.updatedAt ?? createdAt,
415
429
  title: topic.title || 'Untitled topic',
416
- trailIds: topic.trailIds ?? [],
417
- };
430
+ } as DraftTopic;
418
431
  const raw = JSON.stringify(stamped, null, 2);
419
432
  idx.entries.push(buildEntry(stamped, Buffer.byteLength(raw, 'utf8')));
420
433
  }