@remnic/core 9.3.676 → 9.3.677

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 (62) hide show
  1. package/dist/access-cli.js +16 -16
  2. package/dist/access-http.js +13 -13
  3. package/dist/access-mcp.js +12 -12
  4. package/dist/access-schema.js +3 -3
  5. package/dist/access-service.js +10 -10
  6. package/dist/{chunk-OG7A6AZX.js → chunk-2DKXY243.js} +4 -4
  7. package/dist/{chunk-Q5ZU3RNY.js → chunk-57ME5VSI.js} +4 -4
  8. package/dist/{chunk-SDLJ2W7S.js → chunk-7UTCHQTB.js} +2 -2
  9. package/dist/{chunk-T2AOOHDA.js → chunk-ACYX37IM.js} +2 -2
  10. package/dist/{chunk-ZLINDOBG.js → chunk-CZMLLVU2.js} +3 -3
  11. package/dist/{chunk-DOCTITOP.js → chunk-DGEZKYVI.js} +4 -4
  12. package/dist/{chunk-Q6MIDQEL.js → chunk-EQYP3HA6.js} +2 -2
  13. package/dist/{chunk-52LZ42LI.js → chunk-ERA5RSMZ.js} +1 -1
  14. package/dist/{chunk-IPLYGWQF.js → chunk-KQAFEZQX.js} +5 -5
  15. package/dist/{chunk-SF45RQDX.js → chunk-RP64QP7G.js} +3 -3
  16. package/dist/{chunk-QLRYXOAD.js → chunk-UDJLF3BO.js} +2 -2
  17. package/dist/{chunk-R37A3BEW.js → chunk-YEQBJXVO.js} +111 -101
  18. package/dist/chunk-YEQBJXVO.js.map +1 -0
  19. package/dist/{chunk-B55KFEGS.js → chunk-YJ4J2JJ2.js} +10 -10
  20. package/dist/{chunk-XVVEKF5I.js → chunk-Z56KDLDK.js} +20 -20
  21. package/dist/{chunk-OUWAQVDJ.js → chunk-Z6SEG36L.js} +4 -4
  22. package/dist/cli.js +22 -22
  23. package/dist/{coding-graph-types-Dd2tGrnm.d.ts → coding/coding-graph-types.d.ts} +1 -1
  24. package/dist/coding/coding-graph-types.js +10 -0
  25. package/dist/coding/coding-graph-types.js.map +1 -0
  26. package/dist/coding/optional-coding-graph.d.ts +2 -2
  27. package/dist/coding/optional-coding-graph.js +1 -1
  28. package/dist/contradiction/index.js +4 -4
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +33 -33
  31. package/dist/lcm/index.js +3 -3
  32. package/dist/namespaces/migrate.js +8 -8
  33. package/dist/namespaces/search.js +7 -7
  34. package/dist/operator-toolkit.js +9 -9
  35. package/dist/orchestrator.js +13 -13
  36. package/dist/search/factory.js +6 -6
  37. package/dist/search/index.js +11 -11
  38. package/dist/search/lancedb-backend.js +2 -2
  39. package/dist/search/meilisearch-backend.js +2 -2
  40. package/dist/search/orama-backend.js +2 -2
  41. package/dist/transfer/autodetect.js +1 -1
  42. package/dist/transfer/backup.js +1 -1
  43. package/dist/transfer/capsule-export.js +2 -2
  44. package/package.json +7 -2
  45. package/src/orchestrator.ts +50 -197
  46. package/src/scopes/scope-plan.test.ts +360 -0
  47. package/src/scopes/scope-plan.ts +320 -0
  48. package/dist/chunk-R37A3BEW.js.map +0 -1
  49. /package/dist/{chunk-OG7A6AZX.js.map → chunk-2DKXY243.js.map} +0 -0
  50. /package/dist/{chunk-Q5ZU3RNY.js.map → chunk-57ME5VSI.js.map} +0 -0
  51. /package/dist/{chunk-SDLJ2W7S.js.map → chunk-7UTCHQTB.js.map} +0 -0
  52. /package/dist/{chunk-T2AOOHDA.js.map → chunk-ACYX37IM.js.map} +0 -0
  53. /package/dist/{chunk-ZLINDOBG.js.map → chunk-CZMLLVU2.js.map} +0 -0
  54. /package/dist/{chunk-DOCTITOP.js.map → chunk-DGEZKYVI.js.map} +0 -0
  55. /package/dist/{chunk-Q6MIDQEL.js.map → chunk-EQYP3HA6.js.map} +0 -0
  56. /package/dist/{chunk-52LZ42LI.js.map → chunk-ERA5RSMZ.js.map} +0 -0
  57. /package/dist/{chunk-IPLYGWQF.js.map → chunk-KQAFEZQX.js.map} +0 -0
  58. /package/dist/{chunk-SF45RQDX.js.map → chunk-RP64QP7G.js.map} +0 -0
  59. /package/dist/{chunk-QLRYXOAD.js.map → chunk-UDJLF3BO.js.map} +0 -0
  60. /package/dist/{chunk-B55KFEGS.js.map → chunk-YJ4J2JJ2.js.map} +0 -0
  61. /package/dist/{chunk-XVVEKF5I.js.map → chunk-Z56KDLDK.js.map} +0 -0
  62. /package/dist/{chunk-OUWAQVDJ.js.map → chunk-Z6SEG36L.js.map} +0 -0
@@ -28,23 +28,23 @@ import {
28
28
  sanitizeSessionKeyForFilename,
29
29
  shouldFilterLifecycleRecallCandidate,
30
30
  summarizeGraphShadowComparison
31
- } from "./chunk-R37A3BEW.js";
31
+ } from "./chunk-YEQBJXVO.js";
32
32
  import "./chunk-4SKKVWLQ.js";
33
- import "./chunk-666A3MOW.js";
34
33
  import "./chunk-7HYPN2GC.js";
34
+ import "./chunk-666A3MOW.js";
35
35
  import "./chunk-HHLLAQGZ.js";
36
- import "./chunk-UWK5OXUJ.js";
37
36
  import "./chunk-3IE22DJ2.js";
38
37
  import "./chunk-TPDBFYEG.js";
39
38
  import "./chunk-KCYE2MZM.js";
40
39
  import "./chunk-MAV46GWQ.js";
41
40
  import "./chunk-5VDJMYTF.js";
41
+ import "./chunk-UWK5OXUJ.js";
42
+ import "./chunk-T2PO5MUF.js";
42
43
  import "./chunk-HP5FMB6L.js";
43
44
  import "./chunk-7N4KAIGN.js";
44
- import "./chunk-TECVW3JP.js";
45
- import "./chunk-T2PO5MUF.js";
46
45
  import "./chunk-B6IUW76R.js";
47
46
  import "./chunk-DRD2Q7HQ.js";
47
+ import "./chunk-TECVW3JP.js";
48
48
  import "./chunk-UHGBNIOS.js";
49
49
  import "./chunk-LXOM6IQU.js";
50
50
  import "./chunk-YTWNKQ2G.js";
@@ -99,8 +99,8 @@ import "./chunk-V4ZHKCGA.js";
99
99
  import "./chunk-CTCPB57O.js";
100
100
  import "./chunk-LN4YGHTM.js";
101
101
  import "./chunk-JLNBQWZ2.js";
102
- import "./chunk-X6IRLNOO.js";
103
102
  import "./chunk-2PRQG7PV.js";
103
+ import "./chunk-X6IRLNOO.js";
104
104
  import "./chunk-NXBXM7Q6.js";
105
105
  import "./chunk-2F6NP3NT.js";
106
106
  import "./chunk-TERNBNJB.js";
@@ -147,16 +147,16 @@ import "./chunk-RS25QOKZ.js";
147
147
  import "./chunk-JGSKJHF7.js";
148
148
  import "./chunk-FF4KLI5W.js";
149
149
  import "./chunk-6VP3YUCS.js";
150
- import "./chunk-Q6MIDQEL.js";
151
- import "./chunk-B55KFEGS.js";
152
- import "./chunk-OUWAQVDJ.js";
153
- import "./chunk-DOCTITOP.js";
154
- import "./chunk-CYEPCZN5.js";
155
- import "./chunk-Q5ZU3RNY.js";
150
+ import "./chunk-EQYP3HA6.js";
151
+ import "./chunk-YJ4J2JJ2.js";
156
152
  import "./chunk-JOASJWQR.js";
157
- import "./chunk-AER6MT24.js";
158
153
  import "./chunk-RN7MUWON.js";
154
+ import "./chunk-Z6SEG36L.js";
155
+ import "./chunk-DGEZKYVI.js";
156
+ import "./chunk-CYEPCZN5.js";
157
+ import "./chunk-57ME5VSI.js";
159
158
  import "./chunk-CINZGPSJ.js";
159
+ import "./chunk-AER6MT24.js";
160
160
  import "./chunk-ZFXCQPNO.js";
161
161
  import "./chunk-7OGJQP7T.js";
162
162
  import "./chunk-E6ZDCOHM.js";
@@ -2,15 +2,15 @@ import {
2
2
  createConversationIndexRuntime,
3
3
  createConversationSearchBackend,
4
4
  createSearchBackend
5
- } from "../chunk-B55KFEGS.js";
6
- import "../chunk-OUWAQVDJ.js";
7
- import "../chunk-DOCTITOP.js";
8
- import "../chunk-CYEPCZN5.js";
9
- import "../chunk-Q5ZU3RNY.js";
5
+ } from "../chunk-YJ4J2JJ2.js";
10
6
  import "../chunk-JOASJWQR.js";
11
- import "../chunk-AER6MT24.js";
12
7
  import "../chunk-RN7MUWON.js";
8
+ import "../chunk-Z6SEG36L.js";
9
+ import "../chunk-DGEZKYVI.js";
10
+ import "../chunk-CYEPCZN5.js";
11
+ import "../chunk-57ME5VSI.js";
13
12
  import "../chunk-CINZGPSJ.js";
13
+ import "../chunk-AER6MT24.js";
14
14
  import "../chunk-7OGJQP7T.js";
15
15
  import "../chunk-E6ZDCOHM.js";
16
16
  import "../chunk-OIF36KGD.js";
@@ -2,29 +2,29 @@ import {
2
2
  createConversationIndexRuntime,
3
3
  createConversationSearchBackend,
4
4
  createSearchBackend
5
- } from "../chunk-B55KFEGS.js";
5
+ } from "../chunk-YJ4J2JJ2.js";
6
+ import {
7
+ RemoteSearchBackend
8
+ } from "../chunk-JOASJWQR.js";
9
+ import {
10
+ EmbedHelper
11
+ } from "../chunk-RN7MUWON.js";
6
12
  import {
7
13
  LanceDbBackend
8
- } from "../chunk-OUWAQVDJ.js";
14
+ } from "../chunk-Z6SEG36L.js";
9
15
  import {
10
16
  MeilisearchBackend
11
- } from "../chunk-DOCTITOP.js";
17
+ } from "../chunk-DGEZKYVI.js";
12
18
  import {
13
19
  NoopSearchBackend
14
20
  } from "../chunk-CYEPCZN5.js";
15
21
  import {
16
22
  OramaBackend
17
- } from "../chunk-Q5ZU3RNY.js";
18
- import {
19
- RemoteSearchBackend
20
- } from "../chunk-JOASJWQR.js";
23
+ } from "../chunk-57ME5VSI.js";
24
+ import "../chunk-CINZGPSJ.js";
21
25
  import {
22
26
  scanMemoryDir
23
27
  } from "../chunk-AER6MT24.js";
24
- import {
25
- EmbedHelper
26
- } from "../chunk-RN7MUWON.js";
27
- import "../chunk-CINZGPSJ.js";
28
28
  import "../chunk-7OGJQP7T.js";
29
29
  import "../chunk-E6ZDCOHM.js";
30
30
  import "../chunk-OIF36KGD.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  LanceDbBackend
3
- } from "../chunk-OUWAQVDJ.js";
4
- import "../chunk-AER6MT24.js";
3
+ } from "../chunk-Z6SEG36L.js";
5
4
  import "../chunk-CINZGPSJ.js";
5
+ import "../chunk-AER6MT24.js";
6
6
  import "../chunk-YNQ6DFSV.js";
7
7
  import "../chunk-5GPPACXK.js";
8
8
  import "../chunk-2ODBA7MQ.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  MeilisearchBackend
3
- } from "../chunk-DOCTITOP.js";
4
- import "../chunk-AER6MT24.js";
3
+ } from "../chunk-DGEZKYVI.js";
5
4
  import "../chunk-CINZGPSJ.js";
5
+ import "../chunk-AER6MT24.js";
6
6
  import "../chunk-YNQ6DFSV.js";
7
7
  import "../chunk-5GPPACXK.js";
8
8
  import "../chunk-2ODBA7MQ.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  OramaBackend,
3
3
  resolveOramaCollectionDbFilePath
4
- } from "../chunk-Q5ZU3RNY.js";
5
- import "../chunk-AER6MT24.js";
4
+ } from "../chunk-57ME5VSI.js";
6
5
  import "../chunk-CINZGPSJ.js";
6
+ import "../chunk-AER6MT24.js";
7
7
  import "../chunk-YNQ6DFSV.js";
8
8
  import "../chunk-5GPPACXK.js";
9
9
  import "../chunk-2ODBA7MQ.js";
@@ -4,8 +4,8 @@ import {
4
4
  import "../chunk-2DL3OFLD.js";
5
5
  import "../chunk-G4IAEX6D.js";
6
6
  import "../chunk-765K3SAT.js";
7
- import "../chunk-J4EB7DNW.js";
8
7
  import "../chunk-WEHSQBFR.js";
8
+ import "../chunk-J4EB7DNW.js";
9
9
  import "../chunk-UI3NYK34.js";
10
10
  import "../chunk-GCGJW34D.js";
11
11
  import "../chunk-PZ5AY32C.js";
@@ -2,8 +2,8 @@ import {
2
2
  backupMemoryDir
3
3
  } from "../chunk-2NLLXCJG.js";
4
4
  import "../chunk-765K3SAT.js";
5
- import "../chunk-J4EB7DNW.js";
6
5
  import "../chunk-X7Y7WX73.js";
6
+ import "../chunk-J4EB7DNW.js";
7
7
  import "../chunk-UI3NYK34.js";
8
8
  import "../chunk-GCGJW34D.js";
9
9
  import "../chunk-BJMBJZ2Y.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  exportCapsule,
3
3
  isValidCapsuleSince
4
- } from "../chunk-IPLYGWQF.js";
5
- import "../chunk-J4EB7DNW.js";
4
+ } from "../chunk-KQAFEZQX.js";
6
5
  import "../chunk-WEHSQBFR.js";
7
6
  import "../chunk-X7Y7WX73.js";
7
+ import "../chunk-J4EB7DNW.js";
8
8
  import "../chunk-UI3NYK34.js";
9
9
  import "../chunk-GCGJW34D.js";
10
10
  import "../chunk-BJMBJZ2Y.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.676",
3
+ "version": "9.3.677",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -451,6 +451,11 @@
451
451
  "remnic-source": "./src/coding/optional-coding-graph.ts",
452
452
  "import": "./dist/coding/optional-coding-graph.js"
453
453
  },
454
+ "./coding/coding-graph-types": {
455
+ "types": "./dist/coding/coding-graph-types.d.ts",
456
+ "remnic-source": "./src/coding/coding-graph-types.ts",
457
+ "import": "./dist/coding/coding-graph-types.js"
458
+ },
454
459
  "./commitment-ledger": {
455
460
  "types": "./dist/commitment-ledger.d.ts",
456
461
  "remnic-source": "./src/commitment-ledger.ts",
@@ -2916,7 +2921,7 @@
2916
2921
  "core"
2917
2922
  ],
2918
2923
  "peerDependencies": {
2919
- "@remnic/coding-graph": "^9.3.676"
2924
+ "@remnic/coding-graph": "^9.3.677"
2920
2925
  },
2921
2926
  "peerDependenciesMeta": {
2922
2927
  "@remnic/coding-graph": {
@@ -325,6 +325,7 @@ import {
325
325
  recallNamespacesForPrincipal,
326
326
  resolvePrincipal,
327
327
  } from "./namespaces/principal.js";
328
+ import { resolveScopePlan } from "./scopes/scope-plan.js";
328
329
  import {
329
330
  expandScopeProfileReadNamespaces,
330
331
  resolveScopeProfilePlan,
@@ -5770,7 +5771,8 @@ export class Orchestrator {
5770
5771
  options.principalOverride.length > 0
5771
5772
  ? options.principalOverride
5772
5773
  : resolvePrincipal(sessionKey, this.config);
5773
- if (this.config.namespacesEnabled && !principal) {
5774
+ const namespacesEnabled = this.config.namespacesEnabled;
5775
+ if (namespacesEnabled && !principal) {
5774
5776
  throw new Error("authentication required: namespaces are enabled and no principal was supplied");
5775
5777
  }
5776
5778
 
@@ -5862,6 +5864,7 @@ export class Orchestrator {
5862
5864
  options.namespace?.trim() || undefined,
5863
5865
  options.principalOverride,
5864
5866
  caps,
5867
+ namespacesEnabled,
5865
5868
  );
5866
5869
  } catch (err) {
5867
5870
  log.debug(`direct-answer observation setup failed: ${err}`);
@@ -5931,62 +5934,27 @@ export class Orchestrator {
5931
5934
  namespaceOverride: string | undefined,
5932
5935
  principalOverride: string | undefined,
5933
5936
  caps: CapabilitySet,
5937
+ namespacesEnabled: boolean,
5934
5938
  ): void {
5935
5939
  const expectedSnapshot = this.lastRecall.get(sessionKey);
5936
5940
  if (expectedSnapshot === null) return;
5937
5941
  if (expectedSnapshot.plannerMode === "no_recall") return;
5938
5942
 
5939
- const principal = principalOverride ?? resolvePrincipal(sessionKey, this.config);
5940
- // Coding-agent overlay (issue #569) is applied when the session has a
5941
- // coding context and there is no explicit namespaceOverride mirrors
5942
- // the main recall path above.
5943
- const observationCodingOverlay =
5944
- namespaceOverride && canReadNamespace(principal, namespaceOverride, this.config)
5945
- ? null
5946
- : this.applyCodingRecallOverlay(sessionKey);
5947
- const observationPrincipalSelf = defaultNamespaceForPrincipal(principal, this.config);
5948
- const observationCodingSelf = observationCodingOverlay
5949
- ? combineNamespaces(observationPrincipalSelf, observationCodingOverlay.namespace)
5950
- : null;
5951
- const observationScopeProfilePlan =
5952
- namespaceOverride && canReadNamespace(principal, namespaceOverride, this.config)
5953
- ? null
5954
- : resolveScopeProfilePlan({
5955
- config: this.config,
5956
- principal,
5957
- codingContext: sessionKey
5958
- ? this.getCodingContextForSession(sessionKey)
5959
- : null,
5960
- codingOverlay: observationCodingOverlay,
5961
- });
5962
- let observationNamespaces: string[];
5963
- if (namespaceOverride && canReadNamespace(principal, namespaceOverride, this.config)) {
5964
- observationNamespaces = [namespaceOverride];
5965
- } else if (observationScopeProfilePlan) {
5966
- observationNamespaces = expandScopeProfileReadNamespaces({
5967
- profilePlan: observationScopeProfilePlan,
5968
- principalSelfNamespace: observationScopeProfilePlan.baseNamespace,
5969
- config: this.config,
5970
- principal,
5971
- codingOverlay: observationCodingOverlay,
5972
- legacyRecallNamespaces: recallNamespacesForPrincipal(principal, this.config),
5973
- });
5974
- } else if (observationCodingOverlay && observationCodingSelf) {
5975
- // Rule 42 / parity with the main recall path: substitute the self
5976
- // namespace within the principal's recall list rather than
5977
- // replacing the full list. Preserves shared and policy-include
5978
- // namespaces for direct-answer observation queries.
5979
- const base = recallNamespacesForPrincipal(principal, this.config);
5980
- const mapped = base.map((ns) =>
5981
- ns === observationPrincipalSelf ? observationCodingSelf : ns,
5982
- );
5983
- const fallbackNs = observationCodingOverlay.readFallbacks.map((fallback) =>
5984
- combineNamespaces(observationPrincipalSelf, fallback),
5985
- );
5986
- observationNamespaces = Array.from(new Set<string>([...mapped, ...fallbackNs]));
5987
- } else {
5988
- observationNamespaces = recallNamespacesForPrincipal(principal, this.config);
5989
- }
5943
+ // Resolve the observation namespace set through the SAME ScopePlan resolver
5944
+ // the main recall path uses (#1521). The observe path does NOT throw on an
5945
+ // unreadable override (it falls through to the coding/legacy branches), so
5946
+ // we skip the readability gate the recall path enforces.
5947
+ const observationScopePlan = resolveScopePlan({
5948
+ config: this.config,
5949
+ sessionKey,
5950
+ namespace: namespaceOverride,
5951
+ principalOverride,
5952
+ codingContext: sessionKey
5953
+ ? this.getCodingContextForSession(sessionKey)
5954
+ : null,
5955
+ namespacesEnabled,
5956
+ });
5957
+ const observationNamespaces = observationScopePlan.readNamespaces;
5990
5958
  const observationQueryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
5991
5959
  cronRecallPolicyEnabled: this.config.cronRecallPolicyEnabled,
5992
5960
  cronRecallNormalizedQueryMaxChars:
@@ -7587,14 +7555,11 @@ export class Orchestrator {
7587
7555
  && options.principalOverride.length > 0
7588
7556
  ? options.principalOverride
7589
7557
  : resolvePrincipal(sessionKey, this.config);
7590
- if (this.config.namespacesEnabled && !principal) {
7558
+ const namespacesEnabled = this.config.namespacesEnabled;
7559
+ if (namespacesEnabled && !principal) {
7591
7560
  throw new Error("authentication required: namespaces are enabled and no principal was supplied");
7592
7561
  }
7593
7562
  const namespaceOverride = options.namespace?.trim() || undefined;
7594
- const readableRecallNamespaces = recallNamespacesForPrincipal(
7595
- principal,
7596
- this.config,
7597
- );
7598
7563
  if (
7599
7564
  namespaceOverride &&
7600
7565
  !canReadNamespace(principal, namespaceOverride, this.config)
@@ -7603,147 +7568,35 @@ export class Orchestrator {
7603
7568
  `namespace override is not readable: ${namespaceOverride}`,
7604
7569
  );
7605
7570
  }
7606
- // Recall path overlay the coding-agent namespace (issue #569) when
7607
- // the session has a codingContext and `codingMode.projectScope` is true.
7608
- // Explicit `namespace` option still wins, preserving pre-#569 semantics.
7609
- //
7610
- // Rule 42: the overlay substitutes the SELF namespace within the
7611
- // principal's recall list — it does NOT replace the full list. Shared
7612
- // and `includeInRecallByDefault` policy namespaces stay in the recall
7613
- // set so coding sessions continue to see team/shared memories. The
7614
- // overlay is combined with the principal base through `combineNamespaces`
7615
- // to preserve principal isolation (cross-tenant leakage guard).
7616
- const codingOverlay = namespaceOverride ? null : this.applyCodingRecallOverlay(sessionKey);
7617
- const principalSelfNamespace = defaultNamespaceForPrincipal(principal, this.config);
7618
- const codingSelfNamespace = codingOverlay
7619
- ? combineNamespaces(principalSelfNamespace, codingOverlay.namespace)
7620
- : null;
7621
- const scopeProfilePlan = namespaceOverride
7622
- ? null
7623
- : resolveScopeProfilePlan({
7624
- config: this.config,
7625
- principal,
7626
- codingContext: sessionKey
7627
- ? this.getCodingContextForSession(sessionKey)
7628
- : null,
7629
- codingOverlay,
7630
- });
7631
- const profileEffectiveNamespace = scopeProfilePlan?.writeNamespace || scopeProfilePlan?.readNamespaces[0];
7632
- const selfNamespace =
7633
- namespaceOverride ??
7634
- profileEffectiveNamespace ??
7635
- codingSelfNamespace ??
7636
- principalSelfNamespace;
7637
- let recallNamespaces: string[];
7638
- if (namespaceOverride) {
7639
- recallNamespaces = [namespaceOverride];
7640
- } else if (scopeProfilePlan) {
7641
- recallNamespaces = expandScopeProfileReadNamespaces({
7642
- profilePlan: scopeProfilePlan,
7643
- principalSelfNamespace: scopeProfilePlan.baseNamespace,
7644
- config: this.config,
7645
- principal,
7646
- codingOverlay,
7647
- legacyRecallNamespaces: readableRecallNamespaces,
7648
- });
7649
- } else if (codingOverlay && codingSelfNamespace) {
7650
- // Substitute the principal's self namespace with the coding-scoped
7651
- // one, and append any read fallbacks (branch→project, PR 3) combined
7652
- // with the principal base so principal isolation is preserved on
7653
- // fallback entries as well.
7654
- const mapped = readableRecallNamespaces.map((ns) =>
7655
- ns === principalSelfNamespace ? codingSelfNamespace : ns,
7656
- );
7657
- const fallbackNs = codingOverlay.readFallbacks.map((fallback) =>
7658
- combineNamespaces(principalSelfNamespace, fallback),
7659
- );
7660
- recallNamespaces = Array.from(new Set<string>([...mapped, ...fallbackNs]));
7661
- } else {
7662
- recallNamespaces = readableRecallNamespaces;
7663
- }
7664
- // Catalog touch (issue #1499): record reads against the recalled namespaces
7665
- // so the catalog reflects active read scopes. Best-effort, failure-tolerant.
7666
- // Round 3 (codex P2): gate behind the no_recall guard — when the planner
7667
- // selects `no_recall` retrieval is skipped entirely (see the early return at
7668
- // `recallMode === "no_recall"` below), so marking every readable namespace as
7669
- // read would falsely inflate `lastReadAt` / catalog recency.
7670
- // Round 4 (codex P2): also skip when the effective memory result limit is
7671
- // zero (`topK: 0`, a disabled/zero `memories` recall section, etc.). The QMD
7672
- // path explicitly returns before searching when `recallResultLimit <= 0`, so
7673
- // no namespace is actually read and the touch would be spurious.
7674
- // NOTE: the catalog read touch is recorded LATER, immediately after the
7675
- // Phase 1 `throwIfRecallAborted` gate (round 6, codex P2 / cursor Medium —
7676
- // NDXHa/NDmle), so it fires only once retrieval is actually about to run.
7677
- // Recording it here (recall entry) would set `lastReadAt` for recalls that
7678
- // are aborted, error out, or short-circuit before any QMD/filesystem read.
7679
-
7680
- // Effective LCM read NAMESPACE SET (#1505 thread "Include coding fallback
7681
- // namespaces in LCM reads"). `observe` archives LCM / structured history
7682
- // under `${effectiveNamespace}:${sessionKey}` for whichever namespace was
7683
- // effective at write time. A branch-scoped session whose evidence was
7684
- // archived at project / root scope must still surface it, exactly as normal
7685
- // QMD/file recall does — QMD/file recall searches the primary overlay key AND
7686
- // `codingOverlay.readFallbacks` (project / root), NOT just the primary
7687
- // overlay key. The prior single `lcmReadSessionId` only targeted the primary
7688
- // overlay, so branch-scoped sessions missed fallback LCM evidence.
7571
+ // Resolve every namespace-bearing field through ONE ScopePlan (#1521): the
7572
+ // read set, the LCM read keys, the coding overlay, and the scope-profile plan
7573
+ // all come from a single pure resolver that delegates to the same helpers
7574
+ // the inline code used. Parity snapshots in scope-plan.test.ts pin the
7575
+ // outputs so this migration cannot change behavior.
7689
7576
  //
7690
- // READ-AUTHORIZATION (preserved from the prior round's
7691
- // `lcmReadNamespaceForSession` gate; rule 39 / 42 / 48): the coding-overlay
7692
- // namespace AND its fallbacks are `<principal>-project-*` sub-namespaces of
7693
- // the principal SELF base, authorized transitively by that base. They are
7694
- // included ONLY when the principal self base is in the readable recall set
7695
- // (`readableRecallNamespaces` — gated by `defaultRecallNamespaces.includes
7696
- // ("self")` AND `canReadNamespace`). When the self base is NOT readable (e.g.
7697
- // a write-only / self-omitted principal), the overlay rows are unauthorized
7698
- // for this reader, so the LCM read collapses to the default store — exactly
7699
- // what an unqualified, unauthorized recall resolves to — and NEVER searches a
7700
- // `<principal>-project-*` key (no cross-tenant read leak). This mirrors what
7701
- // the rest of recall surfaces for such a principal (its readable
7702
- // shared/policy namespaces have no per-session LCM key, so they contribute
7703
- // nothing here). `recallNamespaces` itself appends fallbacks unconditionally
7704
- // for QMD/file recall; the LCM read keys apply the stricter, self-base gate
7705
- // so the prior round's authorization invariant is preserved.
7706
- const codingOverlaySelfReadable =
7707
- codingOverlay !== null &&
7708
- (scopeProfilePlan
7709
- ? scopeProfilePlan.layers.some((layer) => layer.id === "userProject" && layer.readable)
7710
- : readableRecallNamespaces.includes(principalSelfNamespace));
7711
- let lcmReadNamespaces: string[];
7712
- if (namespaceOverride) {
7713
- // Explicit namespace already read-authorized above (canReadNamespace gate).
7714
- lcmReadNamespaces = [namespaceOverride];
7715
- } else if (scopeProfilePlan) {
7716
- // Scope profiles define a layered read stack; LCM-backed evidence uses the
7717
- // same namespace set as QMD/file recall so team/global/shared observations
7718
- // are not silently skipped.
7719
- lcmReadNamespaces = recallNamespaces;
7720
- } else if (codingOverlay && codingSelfNamespace && codingOverlaySelfReadable) {
7721
- // Self base readable → overlay rows authorized. Read the primary overlay
7722
- // key first, then each coding read fallback (project → root), combined with
7723
- // the principal base for isolation — the SAME ordered set QMD/file recall
7724
- // searches for this authorized coding session.
7725
- const fallbackNs = codingOverlay.readFallbacks.map((fallback) =>
7726
- combineNamespaces(principalSelfNamespace, fallback),
7727
- );
7728
- lcmReadNamespaces = [codingSelfNamespace, ...fallbackNs];
7729
- } else {
7730
- // No overlay, OR overlay present but self base unreadable → collapse to the
7731
- // default store (raw sessionKey), exactly as the prior round did. No
7732
- // `<principal>-project-*` overlay key is searched.
7733
- lcmReadNamespaces = [this.config.defaultNamespace];
7734
- }
7735
- // Map the ordered, read-authorized namespace set → ordered, deduped LCM read
7736
- // session_id set. Single-user / no-overlay recall passes a single-namespace
7737
- // set that collapses to the raw `sessionKey`, so this is `[sessionKey]` —
7738
- // byte-for-byte the pre-#1495 single-key behavior.
7739
- const lcmReadSessionIds =
7740
- scopeProfilePlan && !sessionKey
7741
- ? []
7742
- : lcmReadSessionIdsForNamespaces(
7743
- lcmReadNamespaces,
7744
- sessionKey,
7745
- this.config.defaultNamespace,
7746
- );
7577
+ // Catalog read touch (issue #1499) is recorded LATER — after the Phase 1
7578
+ // abort gate so it fires only when retrieval actually runs, not for
7579
+ // aborted / short-circuited recalls.
7580
+ const scopePlan = resolveScopePlan({
7581
+ config: this.config,
7582
+ sessionKey,
7583
+ namespace: options.namespace,
7584
+ principalOverride:
7585
+ typeof options.principalOverride === "string"
7586
+ && options.principalOverride.length > 0
7587
+ ? options.principalOverride
7588
+ : undefined,
7589
+ codingContext: sessionKey
7590
+ ? this.getCodingContextForSession(sessionKey)
7591
+ : null,
7592
+ namespacesEnabled,
7593
+ });
7594
+ const {
7595
+ readNamespaces: recallNamespaces,
7596
+ baseNamespace: selfNamespace,
7597
+ scopeProfilePlan,
7598
+ lcmReadSessionIds,
7599
+ } = scopePlan;
7747
7600
  // Query an LCM-backed read across the ordered read key set and return the
7748
7601
  // FIRST non-empty result (#1505 fallback-namespace unification). The primary
7749
7602
  // overlay key is tried first; if a branch-scoped session has no rows under its