@etiquekit/etq 1.0.11 → 1.0.13

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.
Files changed (149) hide show
  1. package/AGENTS.md +19 -26
  2. package/LICENSE +2 -3
  3. package/NOTICE +6 -4
  4. package/QuickStart.md +35 -47
  5. package/README.md +54 -56
  6. package/bin/etiquette +1 -5
  7. package/bin/etiquette-core +1 -5
  8. package/docs/ARCHITECTURE.md +5 -5
  9. package/docs/CODEX_CLIENT_COMPATIBILITY.md +1 -1
  10. package/docs/CONCEPTS.md +7 -23
  11. package/docs/CONCEPT_STATUS.md +66 -0
  12. package/docs/CORE_PROFILE.md +3 -5
  13. package/docs/LANE_PROVISIONING.md +7 -2
  14. package/docs/README.md +4 -4
  15. package/docs/RELEASE_SURFACE_AUDIT.md +7 -6
  16. package/docs/SEAT_DISCIPLINE.md +55 -91
  17. package/docs/SEAT_PROVISIONING.md +19 -22
  18. package/docs/TEAM_HANDOFF.md +21 -21
  19. package/docs/WORKTREE_QOL.md +7 -6
  20. package/docs/contracts/ledger-entry/README.md +11 -11
  21. package/docs/contracts/ledger-entry/ledger-entry.v0.2.md +3 -3
  22. package/docs/contracts/ledger-entry/ledger-entry.v0.md +8 -8
  23. package/package.json +11 -11
  24. package/packages/control/src/authority/lease.ts +261 -0
  25. package/packages/control/src/authority/node-delegation.ts +257 -0
  26. package/packages/control/src/authority/rig-conductor.ts +632 -0
  27. package/packages/control/src/cli/argv.ts +155 -0
  28. package/packages/control/src/cli/commands/console.ts +200 -0
  29. package/packages/control/src/cli/commands/dispatch-core.ts +89 -0
  30. package/packages/control/src/cli/commands/dispatch.ts +279 -0
  31. package/packages/control/src/cli/commands/harness.ts +89 -0
  32. package/packages/control/src/cli/commands/hook.ts +50 -0
  33. package/packages/control/src/cli/commands/ledger.ts +91 -0
  34. package/packages/control/src/cli/commands/local-workflow.ts +4686 -0
  35. package/packages/control/src/cli/commands/memory.ts +445 -0
  36. package/packages/control/src/cli/commands/release.ts +108 -0
  37. package/packages/control/src/cli/commands/rubric.ts +103 -0
  38. package/packages/control/src/cli/commands/seat.ts +179 -0
  39. package/packages/control/src/cli/commands/session.ts +127 -0
  40. package/packages/control/src/cli/commands/supervision.ts +246 -0
  41. package/packages/control/src/cli/commands/sync.ts +119 -0
  42. package/packages/control/src/cli/commands/workflow.ts +86 -0
  43. package/packages/control/src/cli/commands/workspace.ts +50 -0
  44. package/packages/control/src/cli/core-usage.ts +34 -0
  45. package/packages/control/src/cli/prompt.ts +67 -0
  46. package/packages/control/src/cli/supervision-deps.ts +44 -0
  47. package/packages/control/src/cli/usage.ts +241 -0
  48. package/packages/control/src/cli.ts +207 -0
  49. package/packages/control/src/core-cli.ts +50 -0
  50. package/packages/control/src/dispatch/decision.ts +202 -0
  51. package/packages/control/src/dispatch/projection.ts +293 -0
  52. package/packages/control/src/dispatch/record.ts +153 -0
  53. package/packages/control/src/engagement/project.ts +170 -0
  54. package/packages/control/src/fs.ts +19 -0
  55. package/packages/control/src/harness/pruning.ts +406 -0
  56. package/packages/control/src/hooks/dispatcher.ts +117 -0
  57. package/packages/control/src/hooks/outbox.ts +86 -0
  58. package/packages/control/src/hooks/sanitize.ts +6 -0
  59. package/packages/control/src/hooks/types.ts +34 -0
  60. package/packages/control/src/index.ts +384 -0
  61. package/packages/control/src/ledger/entry.ts +303 -0
  62. package/packages/control/src/ledger/indexer.ts +542 -0
  63. package/packages/control/src/memory/context.ts +149 -0
  64. package/packages/control/src/memory/drain-import.ts +207 -0
  65. package/packages/control/src/memory/indexer.ts +284 -0
  66. package/packages/control/src/memory/query.ts +75 -0
  67. package/packages/control/src/memory/sanitize.ts +50 -0
  68. package/packages/control/src/memory/sharded-drain-import.ts +212 -0
  69. package/packages/control/src/memory/status.ts +211 -0
  70. package/packages/control/src/memory/store-lifecycle.ts +509 -0
  71. package/packages/control/src/memory/store.ts +284 -0
  72. package/packages/control/src/memory/types.ts +146 -0
  73. package/packages/control/src/parity/surfaces.ts +748 -0
  74. package/packages/control/src/project.ts +141 -0
  75. package/packages/control/src/projection/local-ledger-view.ts +373 -0
  76. package/packages/control/src/projection/return-enforcement.ts +48 -0
  77. package/packages/control/src/projection/timeline-preview.ts +539 -0
  78. package/packages/control/src/projection/timeline.ts +708 -0
  79. package/packages/control/src/release/readiness.ts +831 -0
  80. package/packages/control/src/rubric/loader.ts +326 -0
  81. package/packages/control/src/rubric/promotion.ts +54 -0
  82. package/packages/control/src/rubric/runner.ts +159 -0
  83. package/packages/control/src/rubric/types.ts +158 -0
  84. package/packages/control/src/seat/owner-card.ts +388 -0
  85. package/packages/control/src/seat/readiness.ts +834 -0
  86. package/packages/control/src/session/runbook.ts +431 -0
  87. package/packages/control/src/shared/sanitize.ts +49 -0
  88. package/packages/control/src/supervision/action-classes.ts +192 -0
  89. package/packages/control/src/supervision/command-apply.ts +378 -0
  90. package/packages/control/src/supervision/errors.ts +14 -0
  91. package/packages/control/src/supervision/event-replay.ts +155 -0
  92. package/packages/control/src/supervision/events.ts +109 -0
  93. package/packages/control/src/supervision/index.ts +16 -0
  94. package/packages/control/src/supervision/manifest.ts +127 -0
  95. package/packages/control/src/supervision/paths.ts +49 -0
  96. package/packages/control/src/supervision/projection-adapter.ts +274 -0
  97. package/packages/control/src/supervision/projection.ts +75 -0
  98. package/packages/control/src/supervision/rebuild.ts +99 -0
  99. package/packages/control/src/supervision/session-open.ts +131 -0
  100. package/packages/control/src/supervision/session-read.ts +99 -0
  101. package/packages/control/src/supervision/sqlite-impl.ts +71 -0
  102. package/packages/control/src/supervision/sqlite.ts +121 -0
  103. package/packages/control/src/supervision/store-rows.ts +371 -0
  104. package/packages/control/src/supervision/turn-close.ts +154 -0
  105. package/packages/control/src/supervision/turn-open.ts +284 -0
  106. package/packages/control/src/sync/event-log-merge-driver.ts +263 -0
  107. package/packages/control/src/sync/join-plan.ts +375 -0
  108. package/packages/control/src/sync/outbox.ts +492 -0
  109. package/packages/control/src/workflow/evaluator.ts +140 -0
  110. package/packages/control/src/workflow/loader.ts +200 -0
  111. package/packages/control/src/workflow/types.ts +90 -0
  112. package/packages/control/src/workspace/authority.ts +499 -0
  113. package/packages/protocol/src/guards.ts +119 -0
  114. package/packages/protocol/src/huddle-board.ts +198 -0
  115. package/packages/protocol/src/huddle.ts +295 -0
  116. package/packages/protocol/src/incident.ts +251 -0
  117. package/packages/protocol/src/index.ts +8 -0
  118. package/packages/protocol/src/interfaces.ts +107 -0
  119. package/packages/protocol/src/packet-profile.ts +195 -0
  120. package/packages/protocol/src/state.ts +81 -0
  121. package/packages/protocol/src/types.ts +434 -0
  122. package/release/lineage.v0.json +14 -0
  123. package/scripts/release-candidate-verify.sh +175 -0
  124. package/scripts/release-checksum.sh +25 -0
  125. package/scripts/release-pack-canary.sh +97 -0
  126. package/scripts/release-scan.sh +249 -0
  127. package/scripts/release-sign.sh +34 -0
  128. package/templates/etiquette-vanilla-v0/README.md +2 -3
  129. package/templates/etiquette-vanilla-v0/source/control-seat/README.md +0 -6
  130. package/templates/etiquette-vanilla-v0/source/control-seat/bin/seat-doctor +0 -5
  131. package/templates/etiquette-vanilla-v0/validate-vanilla.sh +0 -5
  132. package/templates/seat-packs-v0/README.md +3 -2
  133. package/templates/seat-packs-v0/source/claude-code-seat/README.md +3 -29
  134. package/templates/seat-packs-v0/source/codex-seat/README.md +3 -17
  135. package/templates/seat-packs-v0/source/gemini-seat/README.md +3 -23
  136. package/templates/seat-packs-v0/source/ollama-seat/README.md +3 -3
  137. package/templates/seat-packs-v0/source/openrouter-seat/README.md +3 -3
  138. package/docs/AGENT_SPAWNING.md +0 -78
  139. package/docs/INTEGRATION_UI.md +0 -215
  140. package/docs/OPERATOR_PLAYBOOK.md +0 -77
  141. package/docs/TAG_ROUTE.md +0 -95
  142. package/lib/etiquette-core.js +0 -315
  143. package/lib/etiquette.js +0 -1160
  144. package/templates/DISPATCH_PREMISES.md +0 -39
  145. package/templates/etiquette-vanilla-v0/source/control-seat/bin/access-assurance-check +0 -55
  146. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/access/ACCESS-ASSURANCE.md +0 -39
  147. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/access/workspace-secure-profile.v0.json +0 -53
  148. package/templates/hosted-receiver/README.md +0 -41
  149. package/templates/hosted-receiver/w1-github-webhook-receiver.mjs +0 -129
@@ -0,0 +1,212 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import { dirname, posix, resolve } from 'node:path';
4
+ import { stringify as stringifyYaml } from 'yaml';
5
+ import { drainImportMemoryCandidates, type MemoryDrainImportReceipt } from './drain-import';
6
+ import { assertSharedMemoryScopeId, assertSharedMemoryText } from './sanitize';
7
+
8
+ export interface ShardedDrainImportShard {
9
+ shardId: string;
10
+ sourceProjectRoot: string;
11
+ sourceWorkspaceRef?: string;
12
+ }
13
+
14
+ export interface ShardedMemoryDrainImportReceipt {
15
+ schema: 'memory-sharded-drain-import-receipt.v0';
16
+ receipt_id: string;
17
+ created_at: string;
18
+ topology: {
19
+ drain_topology: 'sharded_workspace_drains';
20
+ shard_key: 'workspace_or_team';
21
+ shard_count: number;
22
+ candidate_import_surface: 'candidate_import';
23
+ promotion_surface: 'authority_promotion_gate';
24
+ single_global_drain_is_demo_only: true;
25
+ };
26
+ source: {
27
+ source_store_id: string;
28
+ target_workspace_ref: string;
29
+ target_store_id: string;
30
+ };
31
+ shards: Array<{
32
+ shard_id: string;
33
+ source_workspace_ref: string;
34
+ source_store_id: string;
35
+ target_store_id: string;
36
+ child_receipt_id: string;
37
+ child_receipt_ref?: string;
38
+ candidate_count: number;
39
+ imported_count: number;
40
+ }>;
41
+ totals: {
42
+ candidate_count: number;
43
+ imported_count: number;
44
+ };
45
+ authority_boundary: {
46
+ sharded_drain_can_authorize_work: false;
47
+ sharded_drain_can_promote_candidates: false;
48
+ sharded_drain_can_write_parent_ledger: false;
49
+ child_drains_can_write_parent_ledger: false;
50
+ candidate_import_is_canonical_truth: false;
51
+ imported_entries_are_candidates: true;
52
+ promotion_gate_required: true;
53
+ };
54
+ }
55
+
56
+ export interface ShardedDrainImportMemoryCandidatesOptions {
57
+ targetProjectRoot: string;
58
+ sourceStoreId: string;
59
+ targetStoreId: string;
60
+ shards: ShardedDrainImportShard[];
61
+ targetStoresRoot?: string;
62
+ sourceStoresRoot?: string;
63
+ targetWorkspaceRef?: string;
64
+ receiptDir?: string;
65
+ receiptPath?: string;
66
+ at?: string;
67
+ }
68
+
69
+ function isoNow(at?: string): string {
70
+ const value = at ?? new Date().toISOString();
71
+ if (!Number.isFinite(Date.parse(value))) throw new Error('--at must be an ISO-8601 timestamp');
72
+ return new Date(value).toISOString();
73
+ }
74
+
75
+ function stableHash(value: unknown): string {
76
+ return createHash('sha256').update(JSON.stringify(value)).digest('hex').slice(0, 16);
77
+ }
78
+
79
+ function assertStoreId(value: string, field: string): string {
80
+ assertSharedMemoryScopeId(value, field);
81
+ return value;
82
+ }
83
+
84
+ function assertShardId(value: string): string {
85
+ assertSharedMemoryScopeId(value, 'shard_id');
86
+ return value;
87
+ }
88
+
89
+ function assertOpaque(value: string, field: string): string {
90
+ assertSharedMemoryText(value, field);
91
+ if (value.startsWith('/') || value.startsWith('\\') || /^[A-Za-z]:[\\/]/.test(value)) {
92
+ throw new Error(`${field} must be opaque or repo-relative, not a local absolute path`);
93
+ }
94
+ return value;
95
+ }
96
+
97
+ function safeRepoDir(raw: string | undefined): string | undefined {
98
+ if (!raw) return undefined;
99
+ if (raw.startsWith('/') || raw.startsWith('\\') || /^[A-Za-z]:[\\/]/.test(raw)) {
100
+ throw new Error('--receipt-dir must be repo-relative');
101
+ }
102
+ if (raw.includes('\\')) throw new Error('--receipt-dir must use POSIX / separators');
103
+ const normalized = posix.normalize(raw);
104
+ if (normalized === '.' || normalized === '..' || normalized.startsWith('../')) {
105
+ throw new Error('--receipt-dir must stay inside the target project');
106
+ }
107
+ assertSharedMemoryText(normalized, 'receipt_dir');
108
+ return normalized.replace(/\/+$/g, '');
109
+ }
110
+
111
+ function childReceiptRef(receiptDir: string | undefined, shardId: string): string | undefined {
112
+ return receiptDir ? `${receiptDir}/${shardId}.memory-drain-import-receipt.yaml` : undefined;
113
+ }
114
+
115
+ function childReceiptPath(targetProjectRoot: string, receiptRef: string | undefined): string | undefined {
116
+ return receiptRef ? resolve(targetProjectRoot, receiptRef) : undefined;
117
+ }
118
+
119
+ function shardPathPrefix(shardId: string, sourceStoreId: string): string {
120
+ return `imports/${shardId}/${sourceStoreId}`;
121
+ }
122
+
123
+ export async function shardedDrainImportMemoryCandidates(
124
+ options: ShardedDrainImportMemoryCandidatesOptions,
125
+ ): Promise<ShardedMemoryDrainImportReceipt> {
126
+ const createdAt = isoNow(options.at);
127
+ const targetProjectRoot = resolve(options.targetProjectRoot);
128
+ const sourceStoreId = assertStoreId(options.sourceStoreId, 'source_store');
129
+ const targetStoreId = assertStoreId(options.targetStoreId, 'target_store');
130
+ const targetWorkspaceRef = assertOpaque(options.targetWorkspaceRef ?? 'org-workspace', 'target_workspace_ref');
131
+ const receiptDir = safeRepoDir(options.receiptDir);
132
+ if (options.shards.length === 0) throw new Error('--shard must include at least one id=path entry');
133
+
134
+ const seen = new Set<string>();
135
+ const childReceipts: Array<{ shardId: string; sourceWorkspaceRef: string; receipt: MemoryDrainImportReceipt; receiptRef?: string }> = [];
136
+ for (const shard of options.shards) {
137
+ const shardId = assertShardId(shard.shardId);
138
+ if (seen.has(shardId)) throw new Error(`duplicate shard id: ${shardId}`);
139
+ seen.add(shardId);
140
+ const sourceWorkspaceRef = assertOpaque(shard.sourceWorkspaceRef ?? shardId, 'source_workspace_ref');
141
+ const receiptRef = childReceiptRef(receiptDir, shardId);
142
+ const receipt = await drainImportMemoryCandidates({
143
+ sourceProjectRoot: shard.sourceProjectRoot,
144
+ targetProjectRoot,
145
+ sourceStoresRoot: options.sourceStoresRoot,
146
+ targetStoresRoot: options.targetStoresRoot,
147
+ sourceStoreId,
148
+ targetStoreId,
149
+ sourceWorkspaceRef,
150
+ targetWorkspaceRef,
151
+ pathPrefix: shardPathPrefix(shardId, sourceStoreId),
152
+ receiptPath: childReceiptPath(targetProjectRoot, receiptRef),
153
+ at: createdAt,
154
+ });
155
+ childReceipts.push({ shardId, sourceWorkspaceRef, receipt, receiptRef });
156
+ }
157
+
158
+ const receipt: ShardedMemoryDrainImportReceipt = {
159
+ schema: 'memory-sharded-drain-import-receipt.v0',
160
+ receipt_id: `memory-sharded-drain-import:${stableHash({
161
+ targetWorkspaceRef,
162
+ sourceStoreId,
163
+ targetStoreId,
164
+ shards: childReceipts.map((shard) => `${shard.shardId}:${shard.receipt.receipt_id}`),
165
+ })}`,
166
+ created_at: createdAt,
167
+ topology: {
168
+ drain_topology: 'sharded_workspace_drains',
169
+ shard_key: 'workspace_or_team',
170
+ shard_count: childReceipts.length,
171
+ candidate_import_surface: 'candidate_import',
172
+ promotion_surface: 'authority_promotion_gate',
173
+ single_global_drain_is_demo_only: true,
174
+ },
175
+ source: {
176
+ source_store_id: sourceStoreId,
177
+ target_workspace_ref: targetWorkspaceRef,
178
+ target_store_id: targetStoreId,
179
+ },
180
+ shards: childReceipts.map((shard) => ({
181
+ shard_id: shard.shardId,
182
+ source_workspace_ref: shard.sourceWorkspaceRef,
183
+ source_store_id: sourceStoreId,
184
+ target_store_id: targetStoreId,
185
+ child_receipt_id: shard.receipt.receipt_id,
186
+ ...(shard.receiptRef ? { child_receipt_ref: shard.receiptRef } : {}),
187
+ candidate_count: shard.receipt.source.candidate_count,
188
+ imported_count: shard.receipt.target.imported_count,
189
+ })),
190
+ totals: {
191
+ candidate_count: childReceipts.reduce((sum, shard) => sum + shard.receipt.source.candidate_count, 0),
192
+ imported_count: childReceipts.reduce((sum, shard) => sum + shard.receipt.target.imported_count, 0),
193
+ },
194
+ authority_boundary: {
195
+ sharded_drain_can_authorize_work: false,
196
+ sharded_drain_can_promote_candidates: false,
197
+ sharded_drain_can_write_parent_ledger: false,
198
+ child_drains_can_write_parent_ledger: false,
199
+ candidate_import_is_canonical_truth: false,
200
+ imported_entries_are_candidates: true,
201
+ promotion_gate_required: true,
202
+ },
203
+ };
204
+
205
+ if (options.receiptPath) {
206
+ const receiptPath = resolve(options.receiptPath);
207
+ await mkdir(dirname(receiptPath), { recursive: true });
208
+ await writeFile(receiptPath, stringifyYaml(receipt));
209
+ }
210
+
211
+ return receipt;
212
+ }
@@ -0,0 +1,211 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir, readFile } from 'node:fs/promises';
3
+ import { extname, join, resolve } from 'node:path';
4
+ import { parse as parseYaml } from 'yaml';
5
+ import { APPROVED_SCAN_DIRS, collectApprovedMemoryFiles, defaultMemoryDbPath } from './indexer';
6
+ import { openMemoryStore, type MemoryStoreStats } from './store';
7
+ import { defaultMemoryStoresRoot, listMemoryStores, type MemoryStoreSummary } from './store-lifecycle';
8
+
9
+ const TEXT_EXTENSIONS = new Set(['.md', '.yaml', '.yml', '.json', '.jsonl', '.ndjson', '.txt']);
10
+
11
+ export interface MemoryCapabilityStatus {
12
+ schema: 'memory-capability-status.v0';
13
+ module_status: 'experimental';
14
+ parity_posture: {
15
+ managed_runtime_filesystem_memory: 'partial';
16
+ mem0_style_semantic_memory: 'reserved';
17
+ full_feature_parity_claimed: false;
18
+ };
19
+ evidence_sources: {
20
+ approved_scan_dirs: {
21
+ path: string;
22
+ present: boolean;
23
+ file_count: number;
24
+ }[];
25
+ approved_file_count: number;
26
+ active_import_adapters: ['local_importer', 'manual'];
27
+ reserved_import_adapters: ['provider_memory_importers'];
28
+ };
29
+ recall_index: {
30
+ db_initialized: boolean;
31
+ search_adapter: 'sqlite_fts';
32
+ vector_adapter: 'reserved';
33
+ stats: MemoryStoreStats;
34
+ };
35
+ store_lifecycle: {
36
+ stores_root_initialized: boolean;
37
+ store_count: number;
38
+ candidate_entries: number;
39
+ rollups: number;
40
+ stores: MemoryStoreSummary[];
41
+ };
42
+ remote_writeback: {
43
+ mode: 'candidate_only';
44
+ drain_import_receipts: number;
45
+ drain_can_write_parent_ledger: false;
46
+ };
47
+ consolidation: {
48
+ status: 'candidate_rollups_available';
49
+ rollups: number;
50
+ marination_is_same_primitive: false;
51
+ };
52
+ authority_boundary: {
53
+ memory_can_authorize_work: false;
54
+ memory_can_close_task: false;
55
+ memory_can_merge: false;
56
+ memory_can_mutate_ledger: false;
57
+ memory_is_execution_truth: false;
58
+ };
59
+ missing_for_full_parity: string[];
60
+ }
61
+
62
+ async function countFilesUnder(root: string, relDir: string): Promise<number> {
63
+ const absDir = join(root, relDir);
64
+ if (!existsSync(absDir)) return 0;
65
+ let count = 0;
66
+ async function walk(dir: string): Promise<void> {
67
+ const entries = await readdir(dir, { withFileTypes: true });
68
+ for (const entry of entries) {
69
+ const abs = join(dir, entry.name);
70
+ if (entry.isDirectory()) {
71
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '_worktrees') continue;
72
+ await walk(abs);
73
+ continue;
74
+ }
75
+ if (entry.isFile() && TEXT_EXTENSIONS.has(extname(entry.name))) count += 1;
76
+ }
77
+ }
78
+ await walk(absDir);
79
+ return count;
80
+ }
81
+
82
+ async function countDrainImportReceipts(projectRoot: string): Promise<number> {
83
+ const receiptsRoot = join(projectRoot, 'docs', 'work', 'receipts');
84
+ if (!existsSync(receiptsRoot)) return 0;
85
+ let count = 0;
86
+ async function walk(dir: string): Promise<void> {
87
+ const entries = await readdir(dir, { withFileTypes: true });
88
+ for (const entry of entries) {
89
+ const abs = join(dir, entry.name);
90
+ if (entry.isDirectory()) {
91
+ await walk(abs);
92
+ continue;
93
+ }
94
+ if (!entry.isFile() || !TEXT_EXTENSIONS.has(extname(entry.name))) continue;
95
+ try {
96
+ const raw = await readFile(abs, 'utf-8');
97
+ const parsed = parseYaml(raw) as unknown;
98
+ if (
99
+ typeof parsed === 'object'
100
+ && parsed !== null
101
+ && !Array.isArray(parsed)
102
+ && (parsed as Record<string, unknown>).schema === 'memory-drain-import-receipt.v0'
103
+ ) {
104
+ count += 1;
105
+ }
106
+ } catch {
107
+ // Status is advisory; malformed receipts should not prevent the operator
108
+ // from seeing the rest of the memory capability picture.
109
+ }
110
+ }
111
+ }
112
+ await walk(receiptsRoot);
113
+ return count;
114
+ }
115
+
116
+ function summarizeStores(stores: MemoryStoreSummary[]): { candidate_entries: number; rollups: number } {
117
+ return stores.reduce((totals, summary) => ({
118
+ candidate_entries: totals.candidate_entries + summary.counts.candidate_entries,
119
+ rollups: totals.rollups + summary.counts.rollups,
120
+ }), { candidate_entries: 0, rollups: 0 });
121
+ }
122
+
123
+ export async function memoryCapabilityStatus(options: {
124
+ projectRoot: string;
125
+ dbPath?: string;
126
+ storesRoot?: string;
127
+ }): Promise<MemoryCapabilityStatus> {
128
+ const projectRoot = resolve(options.projectRoot);
129
+ const dbPath = options.dbPath ?? defaultMemoryDbPath(projectRoot);
130
+ const storesRoot = resolve(options.storesRoot ?? defaultMemoryStoresRoot(projectRoot));
131
+ const approvedFiles = await collectApprovedMemoryFiles(projectRoot);
132
+ const approvedScanDirs = await Promise.all(APPROVED_SCAN_DIRS.map(async (dir) => ({
133
+ path: dir,
134
+ present: existsSync(join(projectRoot, dir)),
135
+ file_count: await countFilesUnder(projectRoot, dir),
136
+ })));
137
+ let stats: MemoryStoreStats;
138
+ if (existsSync(dbPath)) {
139
+ const store = openMemoryStore(dbPath);
140
+ try {
141
+ stats = store.stats();
142
+ } finally {
143
+ store.close();
144
+ }
145
+ } else {
146
+ stats = {
147
+ candidates: {
148
+ total: 0,
149
+ pending: 0,
150
+ promoted: 0,
151
+ rejected: 0,
152
+ },
153
+ recall_capsules: 0,
154
+ };
155
+ }
156
+ const stores = await listMemoryStores({ projectRoot, storesRoot });
157
+ const storeCounts = summarizeStores(stores);
158
+ const drainImportReceipts = await countDrainImportReceipts(projectRoot);
159
+
160
+ return {
161
+ schema: 'memory-capability-status.v0',
162
+ module_status: 'experimental',
163
+ parity_posture: {
164
+ managed_runtime_filesystem_memory: 'partial',
165
+ mem0_style_semantic_memory: 'reserved',
166
+ full_feature_parity_claimed: false,
167
+ },
168
+ evidence_sources: {
169
+ approved_scan_dirs: approvedScanDirs,
170
+ approved_file_count: approvedFiles.length,
171
+ active_import_adapters: ['local_importer', 'manual'],
172
+ reserved_import_adapters: ['provider_memory_importers'],
173
+ },
174
+ recall_index: {
175
+ db_initialized: existsSync(dbPath),
176
+ search_adapter: 'sqlite_fts',
177
+ vector_adapter: 'reserved',
178
+ stats,
179
+ },
180
+ store_lifecycle: {
181
+ stores_root_initialized: existsSync(storesRoot),
182
+ store_count: stores.length,
183
+ candidate_entries: storeCounts.candidate_entries,
184
+ rollups: storeCounts.rollups,
185
+ stores,
186
+ },
187
+ remote_writeback: {
188
+ mode: 'candidate_only',
189
+ drain_import_receipts: drainImportReceipts,
190
+ drain_can_write_parent_ledger: false,
191
+ },
192
+ consolidation: {
193
+ status: 'candidate_rollups_available',
194
+ rollups: storeCounts.rollups,
195
+ marination_is_same_primitive: false,
196
+ },
197
+ authority_boundary: {
198
+ memory_can_authorize_work: false,
199
+ memory_can_close_task: false,
200
+ memory_can_merge: false,
201
+ memory_can_mutate_ledger: false,
202
+ memory_is_execution_truth: false,
203
+ },
204
+ missing_for_full_parity: [
205
+ 'semantic vector adapter',
206
+ 'scheduled curator promotion job',
207
+ 'org_remote memory promotion policy UI',
208
+ 'provider memory importer adapters',
209
+ ],
210
+ };
211
+ }