@remnic/core 9.3.654 → 9.3.655
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-cli.js +23 -23
- package/dist/access-http.d.ts +4 -4
- package/dist/access-http.js +16 -16
- package/dist/access-mcp.d.ts +4 -4
- package/dist/access-mcp.js +15 -15
- package/dist/{access-service-C8A5hoXJ.d.ts → access-service-BEJvriUt.d.ts} +2 -2
- package/dist/access-service.d.ts +4 -4
- package/dist/access-service.js +14 -14
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +3 -3
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +4 -4
- package/dist/{chunk-BP2EV6W5.js → chunk-2RCGZ67B.js} +4 -4
- package/dist/{chunk-FVRBLJP6.js → chunk-54LOUIBE.js} +2 -2
- package/dist/{chunk-LJCEWTG3.js → chunk-55ZMNKMQ.js} +8 -8
- package/dist/{chunk-7WEB3FLJ.js → chunk-5PLUC5OB.js} +2 -2
- package/dist/{chunk-OL2364SB.js → chunk-5QD3QD76.js} +854 -223
- package/dist/chunk-5QD3QD76.js.map +1 -0
- package/dist/{chunk-DBM2BD22.js → chunk-67G4T7KI.js} +3 -3
- package/dist/{chunk-JX2RINDR.js → chunk-6G5JEN55.js} +2 -2
- package/dist/{chunk-2DSTAWNZ.js → chunk-A3Y37UWI.js} +3 -3
- package/dist/{chunk-T2C6QJG2.js → chunk-BGKXTVNG.js} +2 -2
- package/dist/{chunk-JYN7QNTA.js → chunk-COVZLGMR.js} +17 -17
- package/dist/{chunk-JVRPJ7D4.js → chunk-EKQMQQ3U.js} +48 -12
- package/dist/chunk-EKQMQQ3U.js.map +1 -0
- package/dist/{chunk-JBHXMCYN.js → chunk-GRYAECRV.js} +2 -2
- package/dist/{chunk-RGPUQ66K.js → chunk-IQ53ZSXV.js} +2 -2
- package/dist/{chunk-YLZLPVKK.js → chunk-KOI765XP.js} +104 -1
- package/dist/chunk-KOI765XP.js.map +1 -0
- package/dist/{chunk-QW6JZO5P.js → chunk-KZZ4YAEC.js} +2 -2
- package/dist/{chunk-2DGQLOOM.js → chunk-M3VYPE2H.js} +1 -1
- package/dist/{chunk-2DGQLOOM.js.map → chunk-M3VYPE2H.js.map} +1 -1
- package/dist/{chunk-QKK64Z6M.js → chunk-NCSJKK23.js} +2 -2
- package/dist/{chunk-NCGWXCSW.js → chunk-NRBGRZW4.js} +2 -2
- package/dist/{chunk-GPW2E4LN.js → chunk-OKW6F5S5.js} +3 -3
- package/dist/{chunk-Y2RIIF6H.js → chunk-PTMJ2FH2.js} +2 -2
- package/dist/{chunk-JMQSYGXS.js → chunk-PVE7KSQP.js} +2 -2
- package/dist/{chunk-NAZWHTYV.js → chunk-QDVQ4AN2.js} +4 -4
- package/dist/{chunk-PYWNNF2I.js → chunk-QRSKPI62.js} +99 -66
- package/dist/chunk-QRSKPI62.js.map +1 -0
- package/dist/{chunk-YM3LR4LS.js → chunk-SSSXWIBP.js} +5 -5
- package/dist/{chunk-NE2JBMLN.js → chunk-TDZSSJV4.js} +1 -1
- package/dist/chunk-TDZSSJV4.js.map +1 -0
- package/dist/{chunk-6CVI6BP6.js → chunk-TEO46GMM.js} +2 -2
- package/dist/{chunk-K6X553JB.js → chunk-UCEABZZN.js} +2 -2
- package/dist/{chunk-ENV6RDTD.js → chunk-UCEDY5M7.js} +2 -2
- package/dist/{chunk-ZCORQM74.js → chunk-UYNFWZWG.js} +2 -2
- package/dist/{chunk-XWQ6ERUG.js → chunk-WDTUYOLS.js} +2 -2
- package/dist/{chunk-Z5MQI7K2.js → chunk-XOFXKASO.js} +2 -2
- package/dist/{chunk-3RACUBII.js → chunk-XRKQOQLY.js} +2 -2
- package/dist/{chunk-BJA6DQOC.js → chunk-YYN3LIYA.js} +5 -5
- package/dist/{cli-uQgvDFNE.d.ts → cli-BGahB_d3.d.ts} +3 -3
- package/dist/cli.d.ts +5 -5
- package/dist/cli.js +28 -28
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +3 -3
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +33 -33
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +4 -4
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +11 -11
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +15 -4
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.d.ts +1 -1
- package/dist/namespaces/storage.js +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +14 -14
- package/dist/{orchestrator-B4Y4sWQH.d.ts → orchestrator-BgzZlWxH.d.ts} +11 -14
- package/dist/orchestrator.d.ts +3 -3
- package/dist/orchestrator.js +19 -19
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +5 -1
- package/dist/qmd.js +2 -2
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +6 -6
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +6 -6
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +17 -1
- package/dist/search/port.js +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-BKd0Pype.d.ts → semantic-consolidation-Z8d_uMq8.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +2 -2
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/types.d.ts +12 -12
- package/dist/{types-BgChEr0M.d.ts → types-2OPlQWJG.d.ts} +12 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/config.test.ts +69 -0
- package/src/config.ts +135 -0
- package/src/maintenance/namespace-planner.test.ts +1120 -0
- package/src/maintenance/namespace-planner.ts +893 -0
- package/src/namespaces/search.test.ts +130 -2
- package/src/namespaces/search.ts +71 -10
- package/src/orchestrator-flush.test.ts +464 -44
- package/src/orchestrator.ts +97 -66
- package/src/qmd-client.test.ts +59 -0
- package/src/qmd.ts +124 -84
- package/src/search/port.ts +16 -0
- package/src/types.ts +12 -0
- package/dist/chunk-JVRPJ7D4.js.map +0 -1
- package/dist/chunk-NE2JBMLN.js.map +0 -1
- package/dist/chunk-OL2364SB.js.map +0 -1
- package/dist/chunk-PYWNNF2I.js.map +0 -1
- package/dist/chunk-YLZLPVKK.js.map +0 -1
- /package/dist/{chunk-BP2EV6W5.js.map → chunk-2RCGZ67B.js.map} +0 -0
- /package/dist/{chunk-FVRBLJP6.js.map → chunk-54LOUIBE.js.map} +0 -0
- /package/dist/{chunk-LJCEWTG3.js.map → chunk-55ZMNKMQ.js.map} +0 -0
- /package/dist/{chunk-7WEB3FLJ.js.map → chunk-5PLUC5OB.js.map} +0 -0
- /package/dist/{chunk-DBM2BD22.js.map → chunk-67G4T7KI.js.map} +0 -0
- /package/dist/{chunk-JX2RINDR.js.map → chunk-6G5JEN55.js.map} +0 -0
- /package/dist/{chunk-2DSTAWNZ.js.map → chunk-A3Y37UWI.js.map} +0 -0
- /package/dist/{chunk-T2C6QJG2.js.map → chunk-BGKXTVNG.js.map} +0 -0
- /package/dist/{chunk-JYN7QNTA.js.map → chunk-COVZLGMR.js.map} +0 -0
- /package/dist/{chunk-JBHXMCYN.js.map → chunk-GRYAECRV.js.map} +0 -0
- /package/dist/{chunk-RGPUQ66K.js.map → chunk-IQ53ZSXV.js.map} +0 -0
- /package/dist/{chunk-QW6JZO5P.js.map → chunk-KZZ4YAEC.js.map} +0 -0
- /package/dist/{chunk-QKK64Z6M.js.map → chunk-NCSJKK23.js.map} +0 -0
- /package/dist/{chunk-NCGWXCSW.js.map → chunk-NRBGRZW4.js.map} +0 -0
- /package/dist/{chunk-GPW2E4LN.js.map → chunk-OKW6F5S5.js.map} +0 -0
- /package/dist/{chunk-Y2RIIF6H.js.map → chunk-PTMJ2FH2.js.map} +0 -0
- /package/dist/{chunk-JMQSYGXS.js.map → chunk-PVE7KSQP.js.map} +0 -0
- /package/dist/{chunk-NAZWHTYV.js.map → chunk-QDVQ4AN2.js.map} +0 -0
- /package/dist/{chunk-YM3LR4LS.js.map → chunk-SSSXWIBP.js.map} +0 -0
- /package/dist/{chunk-6CVI6BP6.js.map → chunk-TEO46GMM.js.map} +0 -0
- /package/dist/{chunk-K6X553JB.js.map → chunk-UCEABZZN.js.map} +0 -0
- /package/dist/{chunk-ENV6RDTD.js.map → chunk-UCEDY5M7.js.map} +0 -0
- /package/dist/{chunk-ZCORQM74.js.map → chunk-UYNFWZWG.js.map} +0 -0
- /package/dist/{chunk-XWQ6ERUG.js.map → chunk-WDTUYOLS.js.map} +0 -0
- /package/dist/{chunk-Z5MQI7K2.js.map → chunk-XOFXKASO.js.map} +0 -0
- /package/dist/{chunk-3RACUBII.js.map → chunk-XRKQOQLY.js.map} +0 -0
- /package/dist/{chunk-BJA6DQOC.js.map → chunk-YYN3LIYA.js.map} +0 -0
|
@@ -11,6 +11,7 @@ import { parseConfig } from "./config.js";
|
|
|
11
11
|
import type { BufferTurn } from "./types.js";
|
|
12
12
|
import type { ImportTurn } from "./bulk-import/types.js";
|
|
13
13
|
import { namespaceIdentityToken } from "./namespaces/identity.js";
|
|
14
|
+
import { readNamespaceMaintenanceStatuses } from "./maintenance/namespace-planner.js";
|
|
14
15
|
|
|
15
16
|
function makeTurn(sessionKey: string, content: string): BufferTurn {
|
|
16
17
|
return {
|
|
@@ -1780,8 +1781,10 @@ test("runExtraction still clears the session buffer after persistence even if re
|
|
|
1780
1781
|
// cataloged namespaces.
|
|
1781
1782
|
test("runQmdMaintenance updates and embeds cataloged dynamic namespaces (NGnei)", async () => {
|
|
1782
1783
|
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
1783
|
-
|
|
1784
|
-
|
|
1784
|
+
const updateArgs: string[] = [];
|
|
1785
|
+
const updateCalls: Array<{ namespaces: string[]; strict: boolean | undefined }> = [];
|
|
1786
|
+
const embedArgs: string[] = [];
|
|
1787
|
+
const embedCalls: string[][] = [];
|
|
1785
1788
|
const memoryDir = path.join(os.tmpdir(), "remnic-qmd-maintenance-ngnei");
|
|
1786
1789
|
const dynamicNamespace = "project-origin-dynamic";
|
|
1787
1790
|
const dynamicStorageDir = path.join(
|
|
@@ -1797,6 +1800,7 @@ test("runQmdMaintenance updates and embeds cataloged dynamic namespaces (NGnei)"
|
|
|
1797
1800
|
defaultNamespace: "default",
|
|
1798
1801
|
sharedNamespace: "shared",
|
|
1799
1802
|
namespacePolicies: [],
|
|
1803
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
1800
1804
|
qmdAutoEmbedEnabled: true,
|
|
1801
1805
|
qmdEmbedMinIntervalMs: 0,
|
|
1802
1806
|
};
|
|
@@ -1820,35 +1824,98 @@ test("runQmdMaintenance updates and embeds cataloged dynamic namespaces (NGnei)"
|
|
|
1820
1824
|
},
|
|
1821
1825
|
};
|
|
1822
1826
|
orchestrator.namespaceSearchRouter = {
|
|
1823
|
-
async
|
|
1824
|
-
|
|
1825
|
-
|
|
1827
|
+
async updateNamespacesDetailed(ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
1828
|
+
updateCalls.push({ namespaces: [...ns], strict: options?.strict });
|
|
1829
|
+
updateArgs.push(...ns);
|
|
1830
|
+
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
1826
1831
|
},
|
|
1827
1832
|
async embedNamespaces(ns: string[]) {
|
|
1828
|
-
|
|
1833
|
+
embedCalls.push([...ns]);
|
|
1834
|
+
embedArgs.push(...ns);
|
|
1829
1835
|
},
|
|
1830
1836
|
};
|
|
1831
1837
|
|
|
1832
1838
|
await orchestrator.runQmdMaintenance();
|
|
1833
1839
|
|
|
1834
|
-
assert.ok(
|
|
1840
|
+
assert.ok(updateArgs.length > 0, "updateNamespaces must be called");
|
|
1841
|
+
assert.equal(updateCalls.length, 1, "global QMD maintenance must batch selected namespaces into one update call");
|
|
1842
|
+
assert.equal(updateCalls[0]?.strict, true, "recurring QMD maintenance must use strict update semantics");
|
|
1835
1843
|
assert.ok(
|
|
1836
|
-
|
|
1844
|
+
updateArgs.includes(dynamicNamespace),
|
|
1837
1845
|
"QMD update must cover the cataloged dynamic namespace, not just configured ones",
|
|
1838
1846
|
);
|
|
1839
1847
|
assert.ok(
|
|
1840
|
-
|
|
1848
|
+
updateArgs.includes("default") && updateArgs.includes("shared"),
|
|
1841
1849
|
"configured namespaces remain covered",
|
|
1842
1850
|
);
|
|
1843
1851
|
assert.ok(
|
|
1844
|
-
|
|
1852
|
+
embedArgs.includes(dynamicNamespace),
|
|
1845
1853
|
"QMD embed must cover the cataloged dynamic namespace",
|
|
1846
1854
|
);
|
|
1855
|
+
assert.equal(embedCalls.length, 1, "QMD embed must batch all selected namespaces into one router call");
|
|
1856
|
+
assert.deepEqual(new Set(embedCalls[0]), new Set(["default", "shared", dynamicNamespace]));
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
test("runQmdMaintenance tracks namespace embed cadence across budget rotation", async () => {
|
|
1860
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
1861
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-namespace-embed-cadence-"));
|
|
1862
|
+
const updateCalls: string[][] = [];
|
|
1863
|
+
const embedCalls: string[][] = [];
|
|
1864
|
+
|
|
1865
|
+
try {
|
|
1866
|
+
orchestrator.config = {
|
|
1867
|
+
memoryDir,
|
|
1868
|
+
namespacesEnabled: true,
|
|
1869
|
+
defaultNamespace: "default",
|
|
1870
|
+
sharedNamespace: "shared",
|
|
1871
|
+
namespacePolicies: [{ name: "project-a" }, { name: "project-b" }],
|
|
1872
|
+
maintenanceMaxNamespacesPerCycle: 3,
|
|
1873
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
1874
|
+
qmdAutoEmbedEnabled: true,
|
|
1875
|
+
qmdEmbedMinIntervalMs: 60_000,
|
|
1876
|
+
};
|
|
1877
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
1878
|
+
orchestrator.qmdMaintenancePending = true;
|
|
1879
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
1880
|
+
orchestrator.lastQmdEmbedAtMsByNamespace = new Map();
|
|
1881
|
+
orchestrator.namespaceCatalog = {
|
|
1882
|
+
enabled: false,
|
|
1883
|
+
async listNamespaces() {
|
|
1884
|
+
throw new Error("catalog disabled - must not be read");
|
|
1885
|
+
},
|
|
1886
|
+
};
|
|
1887
|
+
orchestrator.namespaceSearchRouter = {
|
|
1888
|
+
async updateNamespacesDetailed(ns: string[]) {
|
|
1889
|
+
updateCalls.push([...ns]);
|
|
1890
|
+
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
1891
|
+
},
|
|
1892
|
+
async embedNamespaces(ns: string[]) {
|
|
1893
|
+
embedCalls.push([...ns]);
|
|
1894
|
+
},
|
|
1895
|
+
};
|
|
1896
|
+
|
|
1897
|
+
await orchestrator.runQmdMaintenance();
|
|
1898
|
+
orchestrator.qmdMaintenancePending = true;
|
|
1899
|
+
await orchestrator.runQmdMaintenance();
|
|
1900
|
+
|
|
1901
|
+
assert.deepEqual(updateCalls, [
|
|
1902
|
+
["default", "shared", "project-a"],
|
|
1903
|
+
["default", "shared", "project-b"],
|
|
1904
|
+
]);
|
|
1905
|
+
assert.deepEqual(
|
|
1906
|
+
embedCalls,
|
|
1907
|
+
[["default", "shared", "project-a"], ["project-b"]],
|
|
1908
|
+
"a global embed timestamp must not suppress embeddings for newly budgeted namespaces",
|
|
1909
|
+
);
|
|
1910
|
+
} finally {
|
|
1911
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
1912
|
+
}
|
|
1847
1913
|
});
|
|
1848
1914
|
|
|
1849
1915
|
test("runQmdMaintenance skips cataloged dynamic namespaces whose live root is unsafe", async () => {
|
|
1850
1916
|
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
1851
|
-
|
|
1917
|
+
const updateArgs: string[] = [];
|
|
1918
|
+
const updateCalls: string[][] = [];
|
|
1852
1919
|
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-unsafe-root-"));
|
|
1853
1920
|
const outsideDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-unsafe-target-"));
|
|
1854
1921
|
try {
|
|
@@ -1868,6 +1935,7 @@ test("runQmdMaintenance skips cataloged dynamic namespaces whose live root is un
|
|
|
1868
1935
|
defaultNamespace: "default",
|
|
1869
1936
|
sharedNamespace: "shared",
|
|
1870
1937
|
namespacePolicies: [],
|
|
1938
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
1871
1939
|
qmdAutoEmbedEnabled: false,
|
|
1872
1940
|
qmdEmbedMinIntervalMs: 0,
|
|
1873
1941
|
};
|
|
@@ -1890,18 +1958,20 @@ test("runQmdMaintenance skips cataloged dynamic namespaces whose live root is un
|
|
|
1890
1958
|
},
|
|
1891
1959
|
};
|
|
1892
1960
|
orchestrator.namespaceSearchRouter = {
|
|
1893
|
-
async
|
|
1894
|
-
|
|
1895
|
-
|
|
1961
|
+
async updateNamespacesDetailed(ns: string[]) {
|
|
1962
|
+
updateCalls.push([...ns]);
|
|
1963
|
+
updateArgs.push(...ns);
|
|
1964
|
+
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
1896
1965
|
},
|
|
1897
1966
|
async embedNamespaces() {},
|
|
1898
1967
|
};
|
|
1899
1968
|
|
|
1900
1969
|
await orchestrator.runQmdMaintenance();
|
|
1901
1970
|
|
|
1902
|
-
assert.ok(
|
|
1971
|
+
assert.ok(updateArgs.length > 0, "updateNamespaces must be called");
|
|
1972
|
+
assert.equal(updateCalls.length, 1, "global QMD maintenance must update once for the locked namespace set");
|
|
1903
1973
|
assert.deepEqual(
|
|
1904
|
-
[...
|
|
1974
|
+
[...updateArgs].sort(),
|
|
1905
1975
|
["default", "shared"],
|
|
1906
1976
|
"cataloged dynamic namespaces are skipped when the live router root differs from the catalog-sanitized root",
|
|
1907
1977
|
);
|
|
@@ -1911,46 +1981,395 @@ test("runQmdMaintenance skips cataloged dynamic namespaces whose live root is un
|
|
|
1911
1981
|
}
|
|
1912
1982
|
});
|
|
1913
1983
|
|
|
1984
|
+
test("runQmdMaintenance treats zero namespace updates as failed maintenance", async () => {
|
|
1985
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
1986
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-zero-update-"));
|
|
1987
|
+
let markMaintenanceCalls = 0;
|
|
1988
|
+
let embedCalls = 0;
|
|
1989
|
+
try {
|
|
1990
|
+
orchestrator.config = {
|
|
1991
|
+
memoryDir,
|
|
1992
|
+
namespacesEnabled: true,
|
|
1993
|
+
defaultNamespace: "default",
|
|
1994
|
+
sharedNamespace: "shared",
|
|
1995
|
+
namespacePolicies: [],
|
|
1996
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
1997
|
+
qmdAutoEmbedEnabled: false,
|
|
1998
|
+
qmdEmbedMinIntervalMs: 0,
|
|
1999
|
+
};
|
|
2000
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
2001
|
+
orchestrator.qmdMaintenancePending = true;
|
|
2002
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2003
|
+
orchestrator.namespaceCatalog = {
|
|
2004
|
+
enabled: true,
|
|
2005
|
+
async listNamespaces() {
|
|
2006
|
+
return [{ namespace: "default" }];
|
|
2007
|
+
},
|
|
2008
|
+
async markMaintenance() {
|
|
2009
|
+
markMaintenanceCalls += 1;
|
|
2010
|
+
},
|
|
2011
|
+
};
|
|
2012
|
+
orchestrator.namespaceSearchRouter = {
|
|
2013
|
+
async updateNamespacesDetailed() {
|
|
2014
|
+
return { backendCount: 0, eligibleNamespaces: [] };
|
|
2015
|
+
},
|
|
2016
|
+
async embedNamespaces() {
|
|
2017
|
+
embedCalls += 1;
|
|
2018
|
+
},
|
|
2019
|
+
};
|
|
2020
|
+
|
|
2021
|
+
await orchestrator.runQmdMaintenance();
|
|
2022
|
+
|
|
2023
|
+
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2024
|
+
assert.ok(
|
|
2025
|
+
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2026
|
+
"zero updates should be recorded as failed maintenance, not a successful run",
|
|
2027
|
+
);
|
|
2028
|
+
assert.equal(markMaintenanceCalls, 0);
|
|
2029
|
+
assert.equal(embedCalls, 0);
|
|
2030
|
+
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2031
|
+
} finally {
|
|
2032
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2033
|
+
}
|
|
2034
|
+
});
|
|
2035
|
+
|
|
2036
|
+
test("runQmdMaintenance treats partial namespace update eligibility as failed maintenance", async () => {
|
|
2037
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2038
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-partial-update-"));
|
|
2039
|
+
let markMaintenanceCalls = 0;
|
|
2040
|
+
let embedCalls = 0;
|
|
2041
|
+
try {
|
|
2042
|
+
orchestrator.config = {
|
|
2043
|
+
memoryDir,
|
|
2044
|
+
namespacesEnabled: true,
|
|
2045
|
+
defaultNamespace: "default",
|
|
2046
|
+
sharedNamespace: "shared",
|
|
2047
|
+
namespacePolicies: [],
|
|
2048
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
2049
|
+
qmdAutoEmbedEnabled: false,
|
|
2050
|
+
qmdEmbedMinIntervalMs: 0,
|
|
2051
|
+
};
|
|
2052
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
2053
|
+
orchestrator.qmdMaintenancePending = true;
|
|
2054
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2055
|
+
orchestrator.namespaceCatalog = {
|
|
2056
|
+
enabled: true,
|
|
2057
|
+
async listNamespaces() {
|
|
2058
|
+
return [{ namespace: "default" }];
|
|
2059
|
+
},
|
|
2060
|
+
async markMaintenance() {
|
|
2061
|
+
markMaintenanceCalls += 1;
|
|
2062
|
+
},
|
|
2063
|
+
};
|
|
2064
|
+
orchestrator.namespaceSearchRouter = {
|
|
2065
|
+
async updateNamespacesDetailed(ns: string[]) {
|
|
2066
|
+
assert.ok(ns.includes("default") && ns.includes("shared"));
|
|
2067
|
+
return { backendCount: 1, eligibleNamespaces: ["default"] };
|
|
2068
|
+
},
|
|
2069
|
+
async embedNamespaces() {
|
|
2070
|
+
embedCalls += 1;
|
|
2071
|
+
},
|
|
2072
|
+
};
|
|
2073
|
+
|
|
2074
|
+
await orchestrator.runQmdMaintenance();
|
|
2075
|
+
|
|
2076
|
+
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2077
|
+
assert.ok(
|
|
2078
|
+
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2079
|
+
"partial update eligibility should not be recorded as successful maintenance",
|
|
2080
|
+
);
|
|
2081
|
+
assert.ok(
|
|
2082
|
+
statuses.some((status) => status.namespace === "shared" && status.state === "failed"),
|
|
2083
|
+
"ineligible selected namespaces should not be rotated as maintained",
|
|
2084
|
+
);
|
|
2085
|
+
assert.equal(markMaintenanceCalls, 0);
|
|
2086
|
+
assert.equal(embedCalls, 0);
|
|
2087
|
+
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2088
|
+
} finally {
|
|
2089
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2090
|
+
}
|
|
2091
|
+
});
|
|
2092
|
+
|
|
2093
|
+
test("runQmdMaintenance treats namespace embed errors as failed maintenance", async () => {
|
|
2094
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2095
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-embed-failure-"));
|
|
2096
|
+
let markMaintenanceCalls = 0;
|
|
2097
|
+
try {
|
|
2098
|
+
orchestrator.config = {
|
|
2099
|
+
memoryDir,
|
|
2100
|
+
namespacesEnabled: true,
|
|
2101
|
+
defaultNamespace: "default",
|
|
2102
|
+
sharedNamespace: "shared",
|
|
2103
|
+
namespacePolicies: [],
|
|
2104
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
2105
|
+
qmdAutoEmbedEnabled: true,
|
|
2106
|
+
qmdEmbedMinIntervalMs: 0,
|
|
2107
|
+
};
|
|
2108
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
2109
|
+
orchestrator.qmdMaintenancePending = true;
|
|
2110
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2111
|
+
orchestrator.namespaceCatalog = {
|
|
2112
|
+
enabled: true,
|
|
2113
|
+
async listNamespaces() {
|
|
2114
|
+
return [{ namespace: "default" }];
|
|
2115
|
+
},
|
|
2116
|
+
async markMaintenance() {
|
|
2117
|
+
markMaintenanceCalls += 1;
|
|
2118
|
+
},
|
|
2119
|
+
};
|
|
2120
|
+
orchestrator.namespaceSearchRouter = {
|
|
2121
|
+
async updateNamespacesDetailed(ns: string[]) {
|
|
2122
|
+
return { backendCount: 1, eligibleNamespaces: ns };
|
|
2123
|
+
},
|
|
2124
|
+
async embedNamespaces() {
|
|
2125
|
+
throw Object.assign(new Error("embed failed"), { code: "EQMD" });
|
|
2126
|
+
},
|
|
2127
|
+
};
|
|
2128
|
+
|
|
2129
|
+
await orchestrator.runQmdMaintenance();
|
|
2130
|
+
|
|
2131
|
+
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2132
|
+
assert.ok(
|
|
2133
|
+
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2134
|
+
"embed failures should not be recorded as successful namespace maintenance",
|
|
2135
|
+
);
|
|
2136
|
+
assert.equal(markMaintenanceCalls, 0);
|
|
2137
|
+
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2138
|
+
} finally {
|
|
2139
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2140
|
+
}
|
|
2141
|
+
});
|
|
2142
|
+
|
|
2143
|
+
test("runQmdMaintenance records QMD min-interval throttles as skipped maintenance", async () => {
|
|
2144
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2145
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-throttled-update-"));
|
|
2146
|
+
let markMaintenanceCalls = 0;
|
|
2147
|
+
let embedCalls = 0;
|
|
2148
|
+
let updateCalls = 0;
|
|
2149
|
+
try {
|
|
2150
|
+
orchestrator.config = {
|
|
2151
|
+
memoryDir,
|
|
2152
|
+
namespacesEnabled: true,
|
|
2153
|
+
defaultNamespace: "default",
|
|
2154
|
+
sharedNamespace: "shared",
|
|
2155
|
+
namespacePolicies: [],
|
|
2156
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
2157
|
+
qmdAutoEmbedEnabled: false,
|
|
2158
|
+
qmdEmbedMinIntervalMs: 0,
|
|
2159
|
+
};
|
|
2160
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
2161
|
+
orchestrator.qmdMaintenancePending = true;
|
|
2162
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2163
|
+
orchestrator.namespaceCatalog = {
|
|
2164
|
+
enabled: true,
|
|
2165
|
+
async listNamespaces() {
|
|
2166
|
+
return [{ namespace: "default" }];
|
|
2167
|
+
},
|
|
2168
|
+
async markMaintenance() {
|
|
2169
|
+
markMaintenanceCalls += 1;
|
|
2170
|
+
},
|
|
2171
|
+
};
|
|
2172
|
+
orchestrator.namespaceSearchRouter = {
|
|
2173
|
+
async updateNamespacesDetailed(_ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
2174
|
+
updateCalls += 1;
|
|
2175
|
+
assert.equal(options?.strict, true, "recurring maintenance must use strict QMD updates");
|
|
2176
|
+
throw new Error("QMD update skipped by global min-interval gate");
|
|
2177
|
+
},
|
|
2178
|
+
async embedNamespaces() {
|
|
2179
|
+
embedCalls += 1;
|
|
2180
|
+
},
|
|
2181
|
+
};
|
|
2182
|
+
|
|
2183
|
+
await orchestrator.runQmdMaintenance();
|
|
2184
|
+
|
|
2185
|
+
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2186
|
+
assert.equal(updateCalls, 1, "strict global QMD maintenance should be attempted once");
|
|
2187
|
+
assert.ok(
|
|
2188
|
+
statuses.some(
|
|
2189
|
+
(status) =>
|
|
2190
|
+
status.namespace === "default" &&
|
|
2191
|
+
status.state === "skipped" &&
|
|
2192
|
+
status.reason === "throttled",
|
|
2193
|
+
),
|
|
2194
|
+
"QMD min-interval throttles should be recorded as skipped maintenance",
|
|
2195
|
+
);
|
|
2196
|
+
assert.ok(
|
|
2197
|
+
statuses.some(
|
|
2198
|
+
(status) =>
|
|
2199
|
+
status.namespace === "shared" &&
|
|
2200
|
+
status.state === "skipped" &&
|
|
2201
|
+
status.reason === "throttled",
|
|
2202
|
+
),
|
|
2203
|
+
"every selected namespace should receive the throttled skip status",
|
|
2204
|
+
);
|
|
2205
|
+
assert.equal(markMaintenanceCalls, 0);
|
|
2206
|
+
assert.equal(embedCalls, 0);
|
|
2207
|
+
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2208
|
+
} finally {
|
|
2209
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
|
|
2213
|
+
test("runQmdMaintenance still embeds when due update is throttled", async () => {
|
|
2214
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2215
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-throttled-update-embed-"));
|
|
2216
|
+
let markMaintenanceCalls = 0;
|
|
2217
|
+
let updateCalls = 0;
|
|
2218
|
+
const embedCalls: string[][] = [];
|
|
2219
|
+
try {
|
|
2220
|
+
orchestrator.config = {
|
|
2221
|
+
memoryDir,
|
|
2222
|
+
namespacesEnabled: true,
|
|
2223
|
+
defaultNamespace: "default",
|
|
2224
|
+
sharedNamespace: "shared",
|
|
2225
|
+
namespacePolicies: [],
|
|
2226
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
2227
|
+
qmdAutoEmbedEnabled: true,
|
|
2228
|
+
qmdEmbedMinIntervalMs: 0,
|
|
2229
|
+
};
|
|
2230
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
2231
|
+
orchestrator.qmdMaintenancePending = true;
|
|
2232
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2233
|
+
orchestrator.namespaceCatalog = {
|
|
2234
|
+
enabled: true,
|
|
2235
|
+
async listNamespaces() {
|
|
2236
|
+
return [{ namespace: "default" }];
|
|
2237
|
+
},
|
|
2238
|
+
async markMaintenance() {
|
|
2239
|
+
markMaintenanceCalls += 1;
|
|
2240
|
+
},
|
|
2241
|
+
};
|
|
2242
|
+
orchestrator.namespaceSearchRouter = {
|
|
2243
|
+
async updateNamespacesDetailed(_ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
2244
|
+
updateCalls += 1;
|
|
2245
|
+
assert.equal(options?.strict, true, "recurring maintenance must use strict QMD updates");
|
|
2246
|
+
throw new Error("QMD update skipped by global min-interval gate");
|
|
2247
|
+
},
|
|
2248
|
+
async embedNamespaces(ns: string[], options?: { strict?: boolean }) {
|
|
2249
|
+
assert.equal(options?.strict, true, "due embed retries must surface embed failures");
|
|
2250
|
+
embedCalls.push([...ns]);
|
|
2251
|
+
},
|
|
2252
|
+
};
|
|
2253
|
+
|
|
2254
|
+
await orchestrator.runQmdMaintenance();
|
|
2255
|
+
|
|
2256
|
+
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2257
|
+
assert.equal(updateCalls, 1, "strict global QMD maintenance should be attempted once");
|
|
2258
|
+
assert.deepEqual(embedCalls, [["default", "shared"]]);
|
|
2259
|
+
assert.ok(
|
|
2260
|
+
statuses.every((status) => status.state === "skipped" && status.reason === "throttled"),
|
|
2261
|
+
"a throttled update should still be recorded as skipped after the due embed retry",
|
|
2262
|
+
);
|
|
2263
|
+
assert.equal(markMaintenanceCalls, 0);
|
|
2264
|
+
assert.notEqual(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2265
|
+
} finally {
|
|
2266
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
|
|
2270
|
+
test("runQmdMaintenance treats strict namespace update errors as failed maintenance", async () => {
|
|
2271
|
+
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2272
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-update-failure-"));
|
|
2273
|
+
let markMaintenanceCalls = 0;
|
|
2274
|
+
let embedCalls = 0;
|
|
2275
|
+
let updateCalls = 0;
|
|
2276
|
+
try {
|
|
2277
|
+
orchestrator.config = {
|
|
2278
|
+
memoryDir,
|
|
2279
|
+
namespacesEnabled: true,
|
|
2280
|
+
defaultNamespace: "default",
|
|
2281
|
+
sharedNamespace: "shared",
|
|
2282
|
+
namespacePolicies: [],
|
|
2283
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
2284
|
+
qmdAutoEmbedEnabled: true,
|
|
2285
|
+
qmdEmbedMinIntervalMs: 0,
|
|
2286
|
+
};
|
|
2287
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
2288
|
+
orchestrator.qmdMaintenancePending = true;
|
|
2289
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2290
|
+
orchestrator.namespaceCatalog = {
|
|
2291
|
+
enabled: true,
|
|
2292
|
+
async listNamespaces() {
|
|
2293
|
+
return [{ namespace: "default" }];
|
|
2294
|
+
},
|
|
2295
|
+
async markMaintenance() {
|
|
2296
|
+
markMaintenanceCalls += 1;
|
|
2297
|
+
},
|
|
2298
|
+
};
|
|
2299
|
+
orchestrator.namespaceSearchRouter = {
|
|
2300
|
+
async updateNamespacesDetailed(_ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
2301
|
+
updateCalls += 1;
|
|
2302
|
+
assert.equal(options?.strict, true, "recurring maintenance must require strict update failure propagation");
|
|
2303
|
+
throw new Error("qmd exploded");
|
|
2304
|
+
},
|
|
2305
|
+
async embedNamespaces() {
|
|
2306
|
+
embedCalls += 1;
|
|
2307
|
+
},
|
|
2308
|
+
};
|
|
2309
|
+
|
|
2310
|
+
await orchestrator.runQmdMaintenance();
|
|
2311
|
+
|
|
2312
|
+
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2313
|
+
assert.equal(updateCalls, 1, "strict global QMD maintenance should be attempted once");
|
|
2314
|
+
assert.ok(
|
|
2315
|
+
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2316
|
+
"strict update errors should be recorded as failed maintenance",
|
|
2317
|
+
);
|
|
2318
|
+
assert.equal(markMaintenanceCalls, 0);
|
|
2319
|
+
assert.equal(embedCalls, 0);
|
|
2320
|
+
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2321
|
+
} finally {
|
|
2322
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2323
|
+
}
|
|
2324
|
+
});
|
|
2325
|
+
|
|
1914
2326
|
// NGnei fallback: when the catalog is disabled, maintenance covers exactly the
|
|
1915
2327
|
// configured set (no catalog read), and a catalog read failure degrades to the
|
|
1916
2328
|
// configured set rather than breaking maintenance.
|
|
1917
2329
|
test("runQmdMaintenance falls back to configured namespaces when the catalog is disabled (NGnei)", async () => {
|
|
1918
2330
|
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
1919
|
-
|
|
2331
|
+
const updateArgs: string[] = [];
|
|
2332
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-disabled-catalog-"));
|
|
1920
2333
|
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2334
|
+
try {
|
|
2335
|
+
orchestrator.config = {
|
|
2336
|
+
memoryDir,
|
|
2337
|
+
namespacesEnabled: true,
|
|
2338
|
+
defaultNamespace: "default",
|
|
2339
|
+
sharedNamespace: "shared",
|
|
1925
2340
|
namespacePolicies: [],
|
|
2341
|
+
maintenanceNamespaceLockStaleMs: 100,
|
|
1926
2342
|
qmdAutoEmbedEnabled: false,
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
2343
|
+
qmdEmbedMinIntervalMs: 0,
|
|
2344
|
+
};
|
|
2345
|
+
orchestrator.qmdMaintenanceInFlight = false;
|
|
2346
|
+
orchestrator.qmdMaintenancePending = true;
|
|
2347
|
+
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2348
|
+
orchestrator.namespaceCatalog = {
|
|
2349
|
+
enabled: false,
|
|
2350
|
+
async listNamespaces() {
|
|
2351
|
+
throw new Error("catalog disabled — must not be read");
|
|
2352
|
+
},
|
|
1937
2353
|
};
|
|
1938
2354
|
orchestrator.namespaceSearchRouter = {
|
|
1939
|
-
async
|
|
1940
|
-
|
|
1941
|
-
return ns.length;
|
|
2355
|
+
async updateNamespacesDetailed(ns: string[]) {
|
|
2356
|
+
updateArgs.push(...ns);
|
|
2357
|
+
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
1942
2358
|
},
|
|
1943
|
-
|
|
1944
|
-
|
|
2359
|
+
async embedNamespaces() {},
|
|
2360
|
+
};
|
|
1945
2361
|
|
|
1946
|
-
|
|
2362
|
+
await orchestrator.runQmdMaintenance();
|
|
1947
2363
|
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2364
|
+
assert.ok(updateArgs.length > 0, "updateNamespaces must be called");
|
|
2365
|
+
assert.deepEqual(
|
|
2366
|
+
[...updateArgs].sort(),
|
|
2367
|
+
["default", "shared"],
|
|
2368
|
+
"a disabled catalog covers exactly the configured set",
|
|
2369
|
+
);
|
|
2370
|
+
} finally {
|
|
2371
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2372
|
+
}
|
|
1954
2373
|
});
|
|
1955
2374
|
|
|
1956
2375
|
// ── NHZEV (codex P2): the QMD STARTUP sync in deferredInitialize() must cover
|
|
@@ -1981,6 +2400,7 @@ test("deferredInitialize startup sync covers cataloged dynamic namespaces (NHZEV
|
|
|
1981
2400
|
sharedNamespace: "shared",
|
|
1982
2401
|
namespacePolicies: [],
|
|
1983
2402
|
qmdMaintenanceEnabled: true,
|
|
2403
|
+
maintenanceMaxNamespacesPerCycle: 2,
|
|
1984
2404
|
};
|
|
1985
2405
|
orchestrator.qmd = {
|
|
1986
2406
|
isAvailable: () => true,
|
|
@@ -2017,7 +2437,7 @@ test("deferredInitialize startup sync covers cataloged dynamic namespaces (NHZEV
|
|
|
2017
2437
|
assert.ok(updateArg, "startup updateNamespaces must be called");
|
|
2018
2438
|
assert.ok(
|
|
2019
2439
|
updateArg!.includes(dynamicNamespace),
|
|
2020
|
-
"startup sync must cover the cataloged dynamic namespace
|
|
2440
|
+
"startup sync must cover the cataloged dynamic namespace even when it exceeds the recurring maintenance cycle budget",
|
|
2021
2441
|
);
|
|
2022
2442
|
assert.ok(
|
|
2023
2443
|
updateArg!.includes("default") && updateArg!.includes("shared"),
|