bikky 0.4.3 → 0.4.4

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.
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Backend aggregation for memory quality telemetry.
3
+ */
4
+ import { createHash } from "node:crypto";
5
+ import { contentHash, computeEffectiveConfidence } from "../mcp/helpers.js";
6
+ import { categoryForMemorySubtype, layerForMemorySubtype } from "../mcp/taxonomy.js";
7
+ import { buildOperationOrigin } from "../provenance/origin.js";
8
+ import { readMaintenanceState, recordMaintenanceRun, shouldRunMaintenance, } from "./maintenance-state.js";
9
+ import * as qdrant from "./qdrant.js";
10
+ const SCROLL_LIMIT = 256;
11
+ const ROLLUP_TYPE = "latest";
12
+ const JOB_NAME = "memory_quality_rollups";
13
+ let logFn = () => { };
14
+ const defaultDeps = {
15
+ isReady: qdrant.isReady,
16
+ activeDestinations: qdrant.activeDestinations,
17
+ qdrantRequest: qdrant.qdrantRequest,
18
+ embed: qdrant.embed,
19
+ };
20
+ export const setLogger = (fn) => {
21
+ logFn = fn;
22
+ };
23
+ const nonEmptyString = (value) => {
24
+ if (typeof value !== "string")
25
+ return null;
26
+ const trimmed = value.trim();
27
+ return trimmed.length > 0 ? trimmed : null;
28
+ };
29
+ const numberValue = (value) => (typeof value === "number" && Number.isFinite(value) ? value : 0);
30
+ const stringArray = (value) => (Array.isArray(value)
31
+ ? value.map((item) => nonEmptyString(item)).filter((item) => item !== null)
32
+ : []);
33
+ const stableUuid = (input) => {
34
+ const hash = createHash("sha256").update(input).digest("hex");
35
+ return [
36
+ hash.slice(0, 8),
37
+ hash.slice(8, 12),
38
+ hash.slice(12, 16),
39
+ hash.slice(16, 20),
40
+ hash.slice(20, 32),
41
+ ].join("-");
42
+ };
43
+ const completePayload = (payload) => ({
44
+ ...payload,
45
+ content: payload.content ?? "",
46
+ category: payload.category ?? "engineering",
47
+ domain: payload.domain ?? "software_engineering",
48
+ kind: payload.kind ?? "fact",
49
+ memory_subtype: payload.memory_subtype ?? null,
50
+ entities: payload.entities ?? [],
51
+ confidence: typeof payload.confidence === "number" ? payload.confidence : 0.7,
52
+ content_hash: payload.content_hash ?? "",
53
+ reinforcement_count: payload.reinforcement_count ?? 1,
54
+ last_reinforced_at: payload.last_reinforced_at ?? payload.created_at ?? "",
55
+ superseded_by: payload.superseded_by ?? null,
56
+ superseded_at: payload.superseded_at ?? null,
57
+ created_at: payload.created_at ?? "",
58
+ updated_at: payload.updated_at ?? payload.created_at ?? "",
59
+ });
60
+ const daysBetween = (now, iso) => {
61
+ if (!iso)
62
+ return 0;
63
+ const timestamp = Date.parse(iso);
64
+ if (!Number.isFinite(timestamp))
65
+ return 0;
66
+ return Math.max(0, (now.getTime() - timestamp) / 86_400_000);
67
+ };
68
+ const isFactStale = (payload, now, thresholdDays) => {
69
+ const activity = payload.last_verified_at ?? payload.last_reinforced_at ?? payload.created_at;
70
+ if (!activity)
71
+ return false;
72
+ return daysBetween(now, activity) > thresholdDays;
73
+ };
74
+ const scopesForFact = (fact) => {
75
+ const scopes = [{ type: "destination", value: fact.destination }];
76
+ const add = (type, value) => {
77
+ const normalized = nonEmptyString(value);
78
+ if (normalized)
79
+ scopes.push({ type, value: normalized });
80
+ };
81
+ add("repo", fact.payload.repo);
82
+ add("workstream_key", fact.payload.workstream_key);
83
+ add("task_key", fact.payload.task_key);
84
+ for (const entity of fact.payload.entities ?? [])
85
+ add("entity", entity);
86
+ add("origin_user", fact.payload.origin?.user?.id);
87
+ add("origin_agent", fact.payload.origin?.agent?.id);
88
+ const seen = new Set();
89
+ return scopes.filter((scope) => {
90
+ const key = `${scope.type}\0${scope.value}`;
91
+ if (seen.has(key))
92
+ return false;
93
+ seen.add(key);
94
+ return true;
95
+ });
96
+ };
97
+ const rollupKey = (destination, scope) => `${destination}\0${scope.type}\0${scope.value}`;
98
+ const getRollup = (rollups, destination, scope, generatedAt) => {
99
+ const key = rollupKey(destination, scope);
100
+ const existing = rollups.get(key);
101
+ if (existing)
102
+ return existing;
103
+ const created = {
104
+ destination,
105
+ scope_type: scope.type,
106
+ scope_value: scope.value,
107
+ active_fact_count: 0,
108
+ recall_count: 0,
109
+ useful_count: 0,
110
+ misleading_count: 0,
111
+ wrong_count: 0,
112
+ stale_count: 0,
113
+ low_confidence_count: 0,
114
+ generated_at: generatedAt,
115
+ sourceFactIds: new Set(),
116
+ sourceEventIds: new Set(),
117
+ };
118
+ rollups.set(key, created);
119
+ return created;
120
+ };
121
+ const addEventSignal = (rollups, scopes, destination, generatedAt, eventId, update) => {
122
+ for (const scope of scopes) {
123
+ const rollup = getRollup(rollups, destination, scope, generatedAt);
124
+ update(rollup);
125
+ rollup.sourceEventIds.add(eventId);
126
+ }
127
+ };
128
+ export const buildQualityRollups = (input) => {
129
+ const now = input.generatedAt ?? new Date();
130
+ const generatedAt = now.toISOString();
131
+ const staleThresholdDays = input.staleThresholdDays ?? 30;
132
+ const lowConfidenceThreshold = input.lowConfidenceThreshold ?? 0.6;
133
+ const factsById = new Map(input.facts.map((fact) => [fact.id, fact]));
134
+ const scopesByFactId = new Map(input.facts.map((fact) => [fact.id, scopesForFact(fact)]));
135
+ const rollups = new Map();
136
+ for (const fact of input.facts) {
137
+ const payload = completePayload(fact.payload);
138
+ const scopes = scopesByFactId.get(fact.id) ?? [];
139
+ for (const scope of scopes) {
140
+ const rollup = getRollup(rollups, fact.destination, scope, generatedAt);
141
+ rollup.active_fact_count++;
142
+ rollup.recall_count += numberValue(payload.recall_count);
143
+ rollup.useful_count += numberValue(payload.useful_count ?? payload.useful_feedback_count);
144
+ if (isFactStale(payload, now, staleThresholdDays))
145
+ rollup.stale_count++;
146
+ if (computeEffectiveConfidence(payload) < lowConfidenceThreshold)
147
+ rollup.low_confidence_count++;
148
+ rollup.sourceFactIds.add(fact.id);
149
+ }
150
+ }
151
+ for (const event of input.events ?? []) {
152
+ const subtype = event.payload.memory_subtype;
153
+ if (subtype === "recall_event") {
154
+ for (const factId of stringArray(event.payload.returned_fact_ids)) {
155
+ const fact = factsById.get(factId);
156
+ if (!fact || typeof fact.payload.recall_count === "number")
157
+ continue;
158
+ addEventSignal(rollups, scopesByFactId.get(factId) ?? [], fact.destination, generatedAt, event.id, (rollup) => { rollup.recall_count++; });
159
+ }
160
+ continue;
161
+ }
162
+ const targetFactId = nonEmptyString(event.payload.target_fact_id);
163
+ if (!targetFactId)
164
+ continue;
165
+ const targetFact = factsById.get(targetFactId);
166
+ if (!targetFact)
167
+ continue;
168
+ const targetScopes = scopesByFactId.get(targetFactId) ?? [];
169
+ if (subtype === "feedback_event" && event.payload.feedback_kind === "useful") {
170
+ if (typeof targetFact.payload.useful_count === "number" || typeof targetFact.payload.useful_feedback_count === "number") {
171
+ continue;
172
+ }
173
+ addEventSignal(rollups, targetScopes, targetFact.destination, generatedAt, event.id, (rollup) => { rollup.useful_count++; });
174
+ continue;
175
+ }
176
+ if (subtype === "outcome_event" && (event.payload.outcome === "misleading" || event.payload.outcome === "wrong")) {
177
+ addEventSignal(rollups, targetScopes, targetFact.destination, generatedAt, event.id, (rollup) => {
178
+ if (event.payload.outcome === "misleading")
179
+ rollup.misleading_count++;
180
+ if (event.payload.outcome === "wrong")
181
+ rollup.wrong_count++;
182
+ });
183
+ }
184
+ }
185
+ return [...rollups.values()]
186
+ .map((rollup) => ({
187
+ destination: rollup.destination,
188
+ scope_type: rollup.scope_type,
189
+ scope_value: rollup.scope_value,
190
+ active_fact_count: rollup.active_fact_count,
191
+ recall_count: rollup.recall_count,
192
+ useful_count: rollup.useful_count,
193
+ misleading_count: rollup.misleading_count,
194
+ wrong_count: rollup.wrong_count,
195
+ stale_count: rollup.stale_count,
196
+ low_confidence_count: rollup.low_confidence_count,
197
+ generated_at: rollup.generated_at,
198
+ source_fact_ids: [...rollup.sourceFactIds].sort().slice(0, 100),
199
+ source_event_ids: [...rollup.sourceEventIds].sort().slice(0, 100),
200
+ }))
201
+ .sort((a, b) => `${a.scope_type}:${a.scope_value}`.localeCompare(`${b.scope_type}:${b.scope_value}`));
202
+ };
203
+ const scrollAllPoints = async (deps, destination, filter) => {
204
+ const points = [];
205
+ let offset;
206
+ do {
207
+ const body = {
208
+ filter,
209
+ limit: SCROLL_LIMIT,
210
+ with_payload: true,
211
+ ...(offset !== undefined && offset !== null ? { offset } : {}),
212
+ };
213
+ const response = await deps.qdrantRequest("POST", `/collections/${destination.collection}/points/scroll`, body, destination);
214
+ for (const point of response.result?.points ?? []) {
215
+ points.push({
216
+ id: point.id,
217
+ destination: destination.name,
218
+ payload: (point.payload ?? {}),
219
+ });
220
+ }
221
+ offset = response.result?.next_page_offset;
222
+ } while (offset !== undefined && offset !== null);
223
+ return points;
224
+ };
225
+ const fetchQualityInputs = async (deps, destination) => {
226
+ const facts = await scrollAllPoints(deps, destination, {
227
+ must: [{ is_null: { key: "superseded_by" } }],
228
+ must_not: [{ key: "kind", match: { any: ["telemetry", "entity_type"] } }],
229
+ });
230
+ const events = await scrollAllPoints(deps, destination, {
231
+ must: [
232
+ { key: "kind", match: { value: "telemetry" } },
233
+ { key: "memory_subtype", match: { any: ["feedback_event", "outcome_event", "recall_event"] } },
234
+ ],
235
+ });
236
+ return { facts, events };
237
+ };
238
+ const rollupContent = (rollup) => (`Memory quality rollup for ${rollup.scope_type}:${rollup.scope_value}: ` +
239
+ `${rollup.active_fact_count} active facts, ${rollup.recall_count} recalls, ` +
240
+ `${rollup.useful_count} useful, ${rollup.misleading_count} misleading, ` +
241
+ `${rollup.wrong_count} wrong, ${rollup.stale_count} stale, ` +
242
+ `${rollup.low_confidence_count} low-confidence.`);
243
+ const rollupId = (rollup) => stableUuid(`aggregate_rollup:${ROLLUP_TYPE}:${rollup.destination}:${rollup.scope_type}:${rollup.scope_value}`);
244
+ const rollupPayload = (config, rollup) => {
245
+ const content = rollupContent(rollup);
246
+ return {
247
+ content,
248
+ category: categoryForMemorySubtype("aggregate_rollup") ?? "system",
249
+ domain: "software_engineering",
250
+ kind: "telemetry",
251
+ memory_subtype: "aggregate_rollup",
252
+ layer: layerForMemorySubtype("aggregate_rollup") ?? "workspace",
253
+ entities: rollup.scope_type === "entity" ? [rollup.scope_value.toLowerCase()] : [],
254
+ origin: buildOperationOrigin({
255
+ config,
256
+ interface: "daemon",
257
+ action: "aggregate",
258
+ subsystem: "memory_quality_rollups",
259
+ metadata: {
260
+ destination: rollup.destination,
261
+ scope_type: rollup.scope_type,
262
+ scope_value: rollup.scope_value,
263
+ },
264
+ }),
265
+ confidence: 1.0,
266
+ importance: 0.4,
267
+ content_hash: contentHash("aggregate_rollup", `${ROLLUP_TYPE}:${rollup.destination}:${rollup.scope_type}:${rollup.scope_value}`),
268
+ reinforcement_count: 1,
269
+ last_reinforced_at: rollup.generated_at,
270
+ superseded_by: null,
271
+ superseded_at: null,
272
+ created_at: rollup.generated_at,
273
+ updated_at: rollup.generated_at,
274
+ rollup_type: ROLLUP_TYPE,
275
+ rollup_generated_at: rollup.generated_at,
276
+ rollup_window_end: rollup.generated_at,
277
+ scope_type: rollup.scope_type,
278
+ scope_value: rollup.scope_value,
279
+ active_fact_count: rollup.active_fact_count,
280
+ recall_count: rollup.recall_count,
281
+ useful_count: rollup.useful_count,
282
+ misleading_count: rollup.misleading_count,
283
+ wrong_count: rollup.wrong_count,
284
+ stale_count: rollup.stale_count,
285
+ low_confidence_count: rollup.low_confidence_count,
286
+ source_fact_ids: rollup.source_fact_ids,
287
+ source_event_ids: rollup.source_event_ids,
288
+ metadata: {
289
+ generated_by: "memory_quality_rollups",
290
+ rollup_type: ROLLUP_TYPE,
291
+ },
292
+ };
293
+ };
294
+ const upsertRollup = async (config, deps, destination, rollup) => {
295
+ const payload = rollupPayload(config, rollup);
296
+ const vector = await deps.embed(String(payload.content));
297
+ await deps.qdrantRequest("PUT", `/collections/${destination.collection}/points`, {
298
+ points: [{ id: rollupId(rollup), vector, payload }],
299
+ }, destination);
300
+ };
301
+ export const aggregateMemoryQualitySignals = async (config, deps = defaultDeps) => {
302
+ const destinations = deps.activeDestinations();
303
+ const generatedAt = new Date();
304
+ const maxScopes = config.daemon.memory_quality_rollups_max_scopes_per_run ?? 100;
305
+ let factsSeen = 0;
306
+ let eventsSeen = 0;
307
+ let rollupsUpserted = 0;
308
+ let scopesCapped = false;
309
+ for (const destination of destinations) {
310
+ const { facts, events } = await fetchQualityInputs(deps, destination);
311
+ factsSeen += facts.length;
312
+ eventsSeen += events.length;
313
+ const rollups = buildQualityRollups({
314
+ facts,
315
+ events,
316
+ generatedAt,
317
+ staleThresholdDays: config.daemon.staleness_threshold_days,
318
+ lowConfidenceThreshold: config.daemon.memory_quality_rollups_low_confidence_threshold,
319
+ });
320
+ const selectedRollups = rollups.slice(0, maxScopes);
321
+ scopesCapped ||= rollups.length > selectedRollups.length;
322
+ for (const rollup of selectedRollups) {
323
+ await upsertRollup(config, deps, destination, rollup);
324
+ rollupsUpserted++;
325
+ }
326
+ }
327
+ return {
328
+ destinations_seen: destinations.length,
329
+ facts_seen: factsSeen,
330
+ events_seen: eventsSeen,
331
+ rollups_upserted: rollupsUpserted,
332
+ scopes_capped: scopesCapped,
333
+ };
334
+ };
335
+ export const tick = async (config, deps = defaultDeps) => {
336
+ if (!config.daemon.memory_quality_rollups_enabled)
337
+ return;
338
+ if (!deps.isReady())
339
+ return;
340
+ const now = new Date();
341
+ const nowIso = now.toISOString();
342
+ const state = readMaintenanceState(logFn);
343
+ const job = state.jobs.memory_quality_rollups;
344
+ const intervalSec = config.daemon.memory_quality_rollups_interval_sec ?? 3600;
345
+ if (!shouldRunMaintenance(now, job.last_run_at, intervalSec))
346
+ return;
347
+ try {
348
+ const result = await aggregateMemoryQualitySignals(config, deps);
349
+ recordMaintenanceRun(JOB_NAME, {
350
+ job: JOB_NAME,
351
+ ran_at: nowIso,
352
+ status: result.facts_seen === 0 ? "skipped" : "success",
353
+ candidates_seen: result.facts_seen + result.events_seen,
354
+ llm_calls: 0,
355
+ accepted: result.rollups_upserted,
356
+ deterministic: result.rollups_upserted,
357
+ skipped_reason: result.facts_seen === 0
358
+ ? "no_active_facts"
359
+ : result.scopes_capped
360
+ ? "max_scopes_per_run_reached"
361
+ : undefined,
362
+ }, { cursorUpdatedAt: nowIso }, logFn);
363
+ }
364
+ catch (e) {
365
+ const message = e instanceof Error ? e.message : String(e);
366
+ logFn("ERROR", `Memory quality rollups failed: ${message}`);
367
+ recordMaintenanceRun(JOB_NAME, {
368
+ job: JOB_NAME,
369
+ ran_at: nowIso,
370
+ status: "error",
371
+ candidates_seen: 0,
372
+ llm_calls: 0,
373
+ accepted: 0,
374
+ error: message,
375
+ }, {}, logFn);
376
+ }
377
+ };
378
+ //# sourceMappingURL=quality-rollups.js.map
@@ -255,7 +255,7 @@ const storeRelation = async (fromEntity, toEntity, relationType, content, candid
255
255
  }
256
256
  const id = await qdrant.storeFact({
257
257
  content,
258
- category: "human",
258
+ category: "engineering",
259
259
  domain: DEFAULT_CAPTURE_CONTEXT.domain,
260
260
  kind: "relation",
261
261
  entities: [fromEntity, toEntity],
@@ -20,7 +20,7 @@ const defaultDeps = {
20
20
  */
21
21
  export const scanStaleFacts = async (config, deps = defaultDeps) => {
22
22
  const threshold = config.daemon.staleness_threshold_days || 30;
23
- const categories = ["engineering", "product", "human", "system"];
23
+ const categories = ["engineering", "product", "system"];
24
24
  const limit = 3;
25
25
  if (!deps.isReady()) {
26
26
  logFn("DEBUG", "Staleness scan: Qdrant client not ready, skipping");
package/dist/lifecycle.js CHANGED
@@ -7,13 +7,14 @@
7
7
  */
8
8
  import fs from "node:fs";
9
9
  import { spawn } from "node:child_process";
10
- import { PID_PATH, STATE_DIR } from "./config.js";
10
+ import { getPidPath, getStateDir } from "./config.js";
11
11
  // ---------------------------------------------------------------------------
12
12
  // PID file helpers
13
13
  // ---------------------------------------------------------------------------
14
14
  function readPid() {
15
+ const pidPath = getPidPath();
15
16
  try {
16
- const raw = fs.readFileSync(PID_PATH, "utf-8").trim();
17
+ const raw = fs.readFileSync(pidPath, "utf-8").trim();
17
18
  const pid = parseInt(raw, 10);
18
19
  return Number.isFinite(pid) ? pid : null;
19
20
  }
@@ -22,12 +23,13 @@ function readPid() {
22
23
  }
23
24
  }
24
25
  function writePid(pid) {
25
- fs.mkdirSync(STATE_DIR, { recursive: true });
26
- fs.writeFileSync(PID_PATH, String(pid) + "\n");
26
+ fs.mkdirSync(getStateDir(), { recursive: true });
27
+ fs.writeFileSync(getPidPath(), String(pid) + "\n");
27
28
  }
28
29
  function removePid() {
30
+ const pidPath = getPidPath();
29
31
  try {
30
- fs.unlinkSync(PID_PATH);
32
+ fs.unlinkSync(pidPath);
31
33
  }
32
34
  catch {
33
35
  // already gone
@@ -26,6 +26,9 @@ export interface StructuredFact {
26
26
  verification_count?: number;
27
27
  useful_count?: number;
28
28
  not_useful_count?: number;
29
+ misleading_count?: number;
30
+ wrong_count?: number;
31
+ irrelevant_count?: number;
29
32
  redaction?: FactPayload["redaction"];
30
33
  metadata?: FactPayload["metadata"];
31
34
  workstream_key?: string | null;
@@ -217,6 +217,12 @@ export function formatFact(point) {
217
217
  parts.push(`useful: ${p.useful_count}x`);
218
218
  if ((p.not_useful_count ?? 0) > 0)
219
219
  parts.push(`not useful: ${p.not_useful_count}x`);
220
+ if ((p.misleading_count ?? 0) > 0)
221
+ parts.push(`misleading: ${p.misleading_count}x`);
222
+ if ((p.wrong_count ?? 0) > 0)
223
+ parts.push(`wrong: ${p.wrong_count}x`);
224
+ if ((p.irrelevant_count ?? 0) > 0)
225
+ parts.push(`irrelevant: ${p.irrelevant_count}x`);
220
226
  if (p.redaction?.redacted)
221
227
  parts.push(`redacted: ${p.redaction.summary}`);
222
228
  if (p.metadata && Object.keys(p.metadata).length > 0) {
@@ -268,6 +274,9 @@ export function structuredFact(point) {
268
274
  ...(p.verification_count !== undefined ? { verification_count: p.verification_count } : {}),
269
275
  ...(p.useful_count !== undefined ? { useful_count: p.useful_count } : {}),
270
276
  ...(p.not_useful_count !== undefined ? { not_useful_count: p.not_useful_count } : {}),
277
+ ...(p.misleading_count !== undefined ? { misleading_count: p.misleading_count } : {}),
278
+ ...(p.wrong_count !== undefined ? { wrong_count: p.wrong_count } : {}),
279
+ ...(p.irrelevant_count !== undefined ? { irrelevant_count: p.irrelevant_count } : {}),
271
280
  ...(p.redaction ? { redaction: p.redaction } : {}),
272
281
  ...(p.metadata && Object.keys(p.metadata).length > 0 ? { metadata: p.metadata } : {}),
273
282
  ...(p.workstream_key ? { workstream_key: p.workstream_key } : {}),
@@ -13,10 +13,6 @@ export declare const CATEGORIES: {
13
13
  readonly description: "Product context: domain rules, product decisions, requirements, user workflows, roadmap, success metrics, and market or community insight.";
14
14
  readonly examples: readonly ["Bikky should show categories and concrete subtype chips directly, with no extra sub-tab layer.", "Activation should be measured by whether agents successfully reuse recalled memory."];
15
15
  };
16
- readonly human: {
17
- readonly description: "Human context: explicit preferences, people and roles, ownership, working agreements, and durable actor-action activity events.";
18
- readonly examples: readonly ["Saber prefers concise implementation plans before code changes.", "Alex approved PR #85 for merge."];
19
- };
20
16
  readonly system: {
21
17
  readonly description: "System context: Bikky-owned lifecycle memory, session indexes, episodes, workstreams, recall/feedback/outcome telemetry, and aggregate rollups.";
22
18
  readonly examples: readonly ["Session indexes are routing/audit artifacts rather than durable project boundaries.", "Recall telemetry is excluded from normal semantic recall."];
@@ -27,15 +23,15 @@ export declare const DEFAULT_CATEGORY: Category;
27
23
  export declare const DOMAINS: {
28
24
  readonly software_engineering: {
29
25
  readonly description: "Coding-agent work: repositories, code changes, architecture, infrastructure, debugging, tests, CI, and developer workflow.";
30
- readonly defaultCategories: readonly ["engineering", "product", "human", "system"];
26
+ readonly defaultCategories: readonly ["engineering", "product", "system"];
31
27
  };
32
28
  readonly product_strategy: {
33
29
  readonly description: "Product direction, positioning, roadmap tradeoffs, customer problems, metrics, and market learning.";
34
- readonly defaultCategories: readonly ["product", "human", "system"];
30
+ readonly defaultCategories: readonly ["product", "engineering", "system"];
35
31
  };
36
32
  readonly business_operations: {
37
33
  readonly description: "Business process, vendors, finance, legal/admin operations, recurring procedures, and ownership.";
38
- readonly defaultCategories: readonly ["product", "human", "system"];
34
+ readonly defaultCategories: readonly ["product", "engineering", "system"];
39
35
  };
40
36
  readonly research: {
41
37
  readonly description: "Research questions, sources, hypotheses, experiment findings, synthesis, and reusable insights.";
@@ -43,7 +39,7 @@ export declare const DOMAINS: {
43
39
  };
44
40
  readonly personal_productivity: {
45
41
  readonly description: "Individual productivity, habits, planning preferences, reminders, and personal operating context.";
46
- readonly defaultCategories: readonly ["human", "product", "system"];
42
+ readonly defaultCategories: readonly ["engineering", "product", "system"];
47
43
  };
48
44
  };
49
45
  export type Domain = keyof typeof DOMAINS;
@@ -121,11 +117,11 @@ export declare const MEMORY_SUBTYPE_DEFAULT_CATEGORY: {
121
117
  readonly success_metric: "product";
122
118
  readonly market_insight: "product";
123
119
  readonly troubleshooting_gotcha: "engineering";
124
- readonly preference: "human";
125
- readonly person_profile: "human";
126
- readonly ownership_note: "human";
127
- readonly working_agreement: "human";
128
- readonly activity_event: "human";
120
+ readonly preference: "engineering";
121
+ readonly person_profile: "engineering";
122
+ readonly ownership_note: "engineering";
123
+ readonly working_agreement: "engineering";
124
+ readonly activity_event: "engineering";
129
125
  readonly session_index: "system";
130
126
  readonly episode: "system";
131
127
  readonly workstream: "system";