@remnic/core 9.3.690 → 9.3.692
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-boundary.d.ts +7 -6
- package/dist/access-boundary.js +21 -21
- package/dist/access-cli.js +49 -47
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +7 -6
- package/dist/access-http.js +26 -26
- package/dist/access-mcp.d.ts +7 -6
- package/dist/access-mcp.js +23 -23
- package/dist/access-operations.d.ts +7 -6
- package/dist/access-operations.js +22 -22
- package/dist/{access-service-Dujr3MTm.d.ts → access-service-DvApf9LZ.d.ts} +2 -2
- package/dist/access-service.d.ts +7 -6
- package/dist/access-service.js +20 -20
- package/dist/access-surface-catalog.d.ts +7 -6
- 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/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-NUQWSFQD.js → auto-sync-RWXT6P5M.js} +5 -5
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +6 -5
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +5 -5
- 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/calibration.js +2 -2
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-CKPtJ114.d.ts → catalog-BEWLjdOr.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 +8 -8
- package/dist/{chunk-GTDH3IUH.js → chunk-2D27ODNX.js} +2 -2
- package/dist/{chunk-46URPRE6.js → chunk-2EN5WZQB.js} +2 -2
- package/dist/{chunk-3K37VPM3.js → chunk-3BRA3N5A.js} +2 -2
- package/dist/{chunk-RTFAZOIR.js → chunk-3VVH6ZQW.js} +2 -2
- package/dist/{chunk-ZU7N3S6V.js → chunk-4IQNAOP3.js} +6 -142
- package/dist/chunk-4IQNAOP3.js.map +1 -0
- package/dist/{chunk-FIVDN2SM.js → chunk-4RGWCRIJ.js} +4 -4
- package/dist/{chunk-AGJH5ISO.js → chunk-5KBNNUXS.js} +2 -2
- package/dist/{chunk-X7RLU5CR.js → chunk-66HROLW5.js} +2 -2
- package/dist/chunk-6C2BMUZA.js +444 -0
- package/dist/chunk-6C2BMUZA.js.map +1 -0
- package/dist/{chunk-TYF3D4MS.js → chunk-6ZNIUGXB.js} +11 -11
- package/dist/{chunk-UPIBE2DK.js → chunk-ADUAF5EV.js} +2 -2
- package/dist/{chunk-LLONI6PY.js → chunk-AFWEUTMS.js} +2 -2
- package/dist/{chunk-ZUDM75KG.js → chunk-DCWIQFNA.js} +4 -4
- package/dist/{chunk-OMKUJTVJ.js → chunk-DO7MDZMU.js} +4 -4
- package/dist/{chunk-NGFEWFNK.js → chunk-DQHZA7Y3.js} +1 -1
- package/dist/chunk-DQHZA7Y3.js.map +1 -0
- package/dist/{chunk-LXIEXSHU.js → chunk-E6BOD3UJ.js} +2 -2
- package/dist/{chunk-OBXTMFZQ.js → chunk-ECZPWSB5.js} +2 -2
- package/dist/{chunk-CWE74HRG.js → chunk-FM7KLRKN.js} +3 -3
- package/dist/{chunk-BFVPIKDY.js → chunk-GCOSQBQ2.js} +3 -3
- package/dist/chunk-HXNMDPHN.js +149 -0
- package/dist/chunk-HXNMDPHN.js.map +1 -0
- package/dist/{chunk-MDYG7VI7.js → chunk-IREVVFTM.js} +10 -1
- package/dist/{chunk-MDYG7VI7.js.map → chunk-IREVVFTM.js.map} +1 -1
- package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
- package/dist/{chunk-CP2NZQLT.js → chunk-KYH53EFD.js} +6 -6
- package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
- package/dist/{chunk-SB6CQTKP.js → chunk-MCV2DD6F.js} +2 -2
- package/dist/{chunk-3FC6LW6T.js → chunk-MRFVMSTR.js} +2 -2
- package/dist/{chunk-WDXCNJSF.js → chunk-MXZWKOOR.js} +2 -2
- package/dist/{chunk-NSVXK7M5.js → chunk-NEILAC3H.js} +3 -3
- package/dist/{chunk-RGNEARXW.js → chunk-NUZ3N445.js} +7 -7
- package/dist/{chunk-WROKD3XC.js → chunk-O4ZIV62T.js} +35 -35
- package/dist/{chunk-PHZKALOE.js → chunk-QFG23CRE.js} +2 -2
- package/dist/{chunk-A4HH2EWA.js → chunk-QVG4LAQO.js} +2 -2
- package/dist/{chunk-54PVJDO5.js → chunk-SAC5V6JT.js} +2 -2
- package/dist/{chunk-XKUKJIOY.js → chunk-SEK4XIDN.js} +6 -6
- package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
- package/dist/{chunk-2N6UNJSX.js → chunk-U2OBZSGG.js} +2 -2
- package/dist/{chunk-BKAMHZYR.js → chunk-UOCLRM74.js} +2 -2
- package/dist/{chunk-A7UZR6RA.js → chunk-UTUYAPLY.js} +14 -1
- package/dist/chunk-UTUYAPLY.js.map +1 -0
- package/dist/{chunk-Z7KILAOU.js → chunk-V56S3A23.js} +19 -19
- package/dist/{chunk-CUNTLEJP.js → chunk-VFVG4NS2.js} +30 -9
- package/dist/chunk-VFVG4NS2.js.map +1 -0
- package/dist/{chunk-UD5OKH4J.js → chunk-W4ELOUNX.js} +2 -2
- package/dist/{chunk-CE6CBRCV.js → chunk-W4MICQIZ.js} +89 -407
- package/dist/chunk-W4MICQIZ.js.map +1 -0
- package/dist/{chunk-GR77Z2BM.js → chunk-WIKNVFBS.js} +2 -2
- package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
- package/dist/{chunk-67MMWC74.js → chunk-ZLXGFAML.js} +2 -2
- package/dist/{cli-BkDp6WNi.d.ts → cli-BjsY9Le7.d.ts} +3 -3
- package/dist/cli.d.ts +8 -7
- package/dist/cli.js +45 -44
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +5 -5
- 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 +5 -5
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +5 -5
- 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/backend.js +2 -2
- 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 +5 -5
- package/dist/entity-schema.d.ts +1 -1
- package/dist/event-time.d.ts +35 -0
- package/dist/event-time.js +325 -0
- package/dist/event-time.js.map +1 -0
- package/dist/explicit-capture.d.ts +6 -5
- 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-judge.js +3 -3
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +3 -3
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/fallback-llm.js +2 -2
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.js +77 -75
- package/dist/index.js.map +1 -1
- 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 +5 -5
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
- package/dist/maintenance/rebuild-memory-projection.js +6 -6
- package/dist/mcp-memory-inspector-app.d.ts +7 -6
- 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 +2 -2
- package/dist/namespaces/migrate.js +15 -15
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +5 -5
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +20 -19
- package/dist/orchestration/maintenance.d.ts +112 -0
- package/dist/orchestration/maintenance.js +39 -0
- package/dist/orchestration/maintenance.js.map +1 -0
- package/dist/{orchestrator-B7ixmUkP.d.ts → orchestrator-CNJCYjZ1.d.ts} +12 -36
- package/dist/orchestrator.d.ts +5 -4
- package/dist/orchestrator.js +42 -40
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- 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-planner-llm.js +2 -2
- 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/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +13 -13
- 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 +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-CWch5uM7.d.ts → semantic-consolidation-D5U0Mrdb.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +6 -6
- package/dist/semantic-rule-promotion.js +5 -5
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +5 -5
- 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 +3 -1
- package/dist/storage.js +4 -4
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-supersession.js +2 -2
- package/dist/temporal-validity.d.ts +22 -2
- package/dist/temporal-validity.js +3 -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/{types-Dm5xxVrr.d.ts → types-2NoBoRBM.d.ts} +38 -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 +5 -5
- package/package.json +12 -2
- package/src/bitemporal-frontmatter.test.ts +177 -0
- package/src/config.ts +13 -0
- package/src/event-time.test.ts +514 -0
- package/src/event-time.ts +548 -0
- package/src/orchestration/maintenance.test.ts +738 -0
- package/src/orchestration/maintenance.ts +557 -0
- package/src/orchestrator-flush.test.ts +0 -599
- package/src/orchestrator.ts +60 -420
- package/src/storage.ts +34 -0
- package/src/temporal-validity.ts +34 -0
- package/src/types.ts +38 -0
- package/dist/chunk-A7UZR6RA.js.map +0 -1
- package/dist/chunk-CE6CBRCV.js.map +0 -1
- package/dist/chunk-CUNTLEJP.js.map +0 -1
- package/dist/chunk-NGFEWFNK.js.map +0 -1
- package/dist/chunk-ZU7N3S6V.js.map +0 -1
- /package/dist/{auto-sync-NUQWSFQD.js.map → auto-sync-RWXT6P5M.js.map} +0 -0
- /package/dist/{chunk-GTDH3IUH.js.map → chunk-2D27ODNX.js.map} +0 -0
- /package/dist/{chunk-46URPRE6.js.map → chunk-2EN5WZQB.js.map} +0 -0
- /package/dist/{chunk-3K37VPM3.js.map → chunk-3BRA3N5A.js.map} +0 -0
- /package/dist/{chunk-RTFAZOIR.js.map → chunk-3VVH6ZQW.js.map} +0 -0
- /package/dist/{chunk-FIVDN2SM.js.map → chunk-4RGWCRIJ.js.map} +0 -0
- /package/dist/{chunk-AGJH5ISO.js.map → chunk-5KBNNUXS.js.map} +0 -0
- /package/dist/{chunk-X7RLU5CR.js.map → chunk-66HROLW5.js.map} +0 -0
- /package/dist/{chunk-TYF3D4MS.js.map → chunk-6ZNIUGXB.js.map} +0 -0
- /package/dist/{chunk-UPIBE2DK.js.map → chunk-ADUAF5EV.js.map} +0 -0
- /package/dist/{chunk-LLONI6PY.js.map → chunk-AFWEUTMS.js.map} +0 -0
- /package/dist/{chunk-ZUDM75KG.js.map → chunk-DCWIQFNA.js.map} +0 -0
- /package/dist/{chunk-OMKUJTVJ.js.map → chunk-DO7MDZMU.js.map} +0 -0
- /package/dist/{chunk-LXIEXSHU.js.map → chunk-E6BOD3UJ.js.map} +0 -0
- /package/dist/{chunk-OBXTMFZQ.js.map → chunk-ECZPWSB5.js.map} +0 -0
- /package/dist/{chunk-CWE74HRG.js.map → chunk-FM7KLRKN.js.map} +0 -0
- /package/dist/{chunk-BFVPIKDY.js.map → chunk-GCOSQBQ2.js.map} +0 -0
- /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-CP2NZQLT.js.map → chunk-KYH53EFD.js.map} +0 -0
- /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
- /package/dist/{chunk-SB6CQTKP.js.map → chunk-MCV2DD6F.js.map} +0 -0
- /package/dist/{chunk-3FC6LW6T.js.map → chunk-MRFVMSTR.js.map} +0 -0
- /package/dist/{chunk-WDXCNJSF.js.map → chunk-MXZWKOOR.js.map} +0 -0
- /package/dist/{chunk-NSVXK7M5.js.map → chunk-NEILAC3H.js.map} +0 -0
- /package/dist/{chunk-RGNEARXW.js.map → chunk-NUZ3N445.js.map} +0 -0
- /package/dist/{chunk-WROKD3XC.js.map → chunk-O4ZIV62T.js.map} +0 -0
- /package/dist/{chunk-PHZKALOE.js.map → chunk-QFG23CRE.js.map} +0 -0
- /package/dist/{chunk-A4HH2EWA.js.map → chunk-QVG4LAQO.js.map} +0 -0
- /package/dist/{chunk-54PVJDO5.js.map → chunk-SAC5V6JT.js.map} +0 -0
- /package/dist/{chunk-XKUKJIOY.js.map → chunk-SEK4XIDN.js.map} +0 -0
- /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
- /package/dist/{chunk-2N6UNJSX.js.map → chunk-U2OBZSGG.js.map} +0 -0
- /package/dist/{chunk-BKAMHZYR.js.map → chunk-UOCLRM74.js.map} +0 -0
- /package/dist/{chunk-Z7KILAOU.js.map → chunk-V56S3A23.js.map} +0 -0
- /package/dist/{chunk-UD5OKH4J.js.map → chunk-W4ELOUNX.js.map} +0 -0
- /package/dist/{chunk-GR77Z2BM.js.map → chunk-WIKNVFBS.js.map} +0 -0
- /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
- /package/dist/{chunk-67MMWC74.js.map → chunk-ZLXGFAML.js.map} +0 -0
|
@@ -1915,605 +1915,6 @@ test("runExtraction still clears the session buffer after persistence even if re
|
|
|
1915
1915
|
);
|
|
1916
1916
|
});
|
|
1917
1917
|
|
|
1918
|
-
// ── NGnei (codex P2): runQmdMaintenance must cover CATALOGED dynamic namespaces,
|
|
1919
|
-
// not only the configured set. An extraction writing to a coding-scoped/dynamic
|
|
1920
|
-
// namespace is made discoverable via the catalog; if maintenance embeds only
|
|
1921
|
-
// configuredNamespaces(), that namespace's QMD collection stays stale. We stub the
|
|
1922
|
-
// orchestrator internals and assert update/embed receive the UNION of configured +
|
|
1923
|
-
// cataloged namespaces.
|
|
1924
|
-
test("runQmdMaintenance updates and embeds cataloged dynamic namespaces (NGnei)", async () => {
|
|
1925
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
1926
|
-
const updateArgs: string[] = [];
|
|
1927
|
-
const updateCalls: Array<{ namespaces: string[]; strict: boolean | undefined }> = [];
|
|
1928
|
-
const embedArgs: string[] = [];
|
|
1929
|
-
const embedCalls: string[][] = [];
|
|
1930
|
-
const memoryDir = path.join(os.tmpdir(), "remnic-qmd-maintenance-ngnei");
|
|
1931
|
-
const dynamicNamespace = "project-origin-dynamic";
|
|
1932
|
-
const dynamicStorageDir = path.join(
|
|
1933
|
-
memoryDir,
|
|
1934
|
-
"namespaces",
|
|
1935
|
-
namespaceIdentityToken(dynamicNamespace),
|
|
1936
|
-
);
|
|
1937
|
-
await mkdir(path.join(dynamicStorageDir, "facts"), { recursive: true });
|
|
1938
|
-
|
|
1939
|
-
orchestrator.config = {
|
|
1940
|
-
memoryDir,
|
|
1941
|
-
namespacesEnabled: true,
|
|
1942
|
-
defaultNamespace: "default",
|
|
1943
|
-
sharedNamespace: "shared",
|
|
1944
|
-
namespacePolicies: [],
|
|
1945
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
1946
|
-
qmdAutoEmbedEnabled: true,
|
|
1947
|
-
qmdEmbedMinIntervalMs: 0,
|
|
1948
|
-
};
|
|
1949
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
1950
|
-
orchestrator.qmdMaintenancePending = true;
|
|
1951
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
1952
|
-
orchestrator.namespaceCatalog = {
|
|
1953
|
-
enabled: true,
|
|
1954
|
-
async listNamespaces() {
|
|
1955
|
-
return [
|
|
1956
|
-
{ namespace: "default" },
|
|
1957
|
-
{
|
|
1958
|
-
namespace: dynamicNamespace,
|
|
1959
|
-
identityToken: namespaceIdentityToken(dynamicNamespace),
|
|
1960
|
-
kind: "project",
|
|
1961
|
-
createdAt: "2026-04-12T12:00:00.000Z",
|
|
1962
|
-
storageDir: dynamicStorageDir,
|
|
1963
|
-
discoveredBy: "write",
|
|
1964
|
-
}, // dynamic, NOT configured
|
|
1965
|
-
];
|
|
1966
|
-
},
|
|
1967
|
-
};
|
|
1968
|
-
orchestrator.namespaceSearchRouter = {
|
|
1969
|
-
async updateNamespacesDetailed(ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
1970
|
-
updateCalls.push({ namespaces: [...ns], strict: options?.strict });
|
|
1971
|
-
updateArgs.push(...ns);
|
|
1972
|
-
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
1973
|
-
},
|
|
1974
|
-
async embedNamespaces(ns: string[]) {
|
|
1975
|
-
embedCalls.push([...ns]);
|
|
1976
|
-
embedArgs.push(...ns);
|
|
1977
|
-
},
|
|
1978
|
-
};
|
|
1979
|
-
|
|
1980
|
-
await orchestrator.runQmdMaintenance();
|
|
1981
|
-
|
|
1982
|
-
assert.ok(updateArgs.length > 0, "updateNamespaces must be called");
|
|
1983
|
-
assert.equal(updateCalls.length, 1, "global QMD maintenance must batch selected namespaces into one update call");
|
|
1984
|
-
assert.equal(updateCalls[0]?.strict, true, "recurring QMD maintenance must use strict update semantics");
|
|
1985
|
-
assert.ok(
|
|
1986
|
-
updateArgs.includes(dynamicNamespace),
|
|
1987
|
-
"QMD update must cover the cataloged dynamic namespace, not just configured ones",
|
|
1988
|
-
);
|
|
1989
|
-
assert.ok(
|
|
1990
|
-
updateArgs.includes("default") && updateArgs.includes("shared"),
|
|
1991
|
-
"configured namespaces remain covered",
|
|
1992
|
-
);
|
|
1993
|
-
assert.ok(
|
|
1994
|
-
embedArgs.includes(dynamicNamespace),
|
|
1995
|
-
"QMD embed must cover the cataloged dynamic namespace",
|
|
1996
|
-
);
|
|
1997
|
-
assert.equal(embedCalls.length, 1, "QMD embed must batch all selected namespaces into one router call");
|
|
1998
|
-
assert.deepEqual(new Set(embedCalls[0]), new Set(["default", "shared", dynamicNamespace]));
|
|
1999
|
-
});
|
|
2000
|
-
|
|
2001
|
-
test("runQmdMaintenance tracks namespace embed cadence across budget rotation", async () => {
|
|
2002
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2003
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-namespace-embed-cadence-"));
|
|
2004
|
-
const updateCalls: string[][] = [];
|
|
2005
|
-
const embedCalls: string[][] = [];
|
|
2006
|
-
|
|
2007
|
-
try {
|
|
2008
|
-
orchestrator.config = {
|
|
2009
|
-
memoryDir,
|
|
2010
|
-
namespacesEnabled: true,
|
|
2011
|
-
defaultNamespace: "default",
|
|
2012
|
-
sharedNamespace: "shared",
|
|
2013
|
-
namespacePolicies: [{ name: "project-a" }, { name: "project-b" }],
|
|
2014
|
-
maintenanceMaxNamespacesPerCycle: 3,
|
|
2015
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2016
|
-
qmdAutoEmbedEnabled: true,
|
|
2017
|
-
qmdEmbedMinIntervalMs: 60_000,
|
|
2018
|
-
};
|
|
2019
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2020
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2021
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2022
|
-
orchestrator.lastQmdEmbedAtMsByNamespace = new Map();
|
|
2023
|
-
orchestrator.namespaceCatalog = {
|
|
2024
|
-
enabled: false,
|
|
2025
|
-
async listNamespaces() {
|
|
2026
|
-
throw new Error("catalog disabled - must not be read");
|
|
2027
|
-
},
|
|
2028
|
-
};
|
|
2029
|
-
orchestrator.namespaceSearchRouter = {
|
|
2030
|
-
async updateNamespacesDetailed(ns: string[]) {
|
|
2031
|
-
updateCalls.push([...ns]);
|
|
2032
|
-
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
2033
|
-
},
|
|
2034
|
-
async embedNamespaces(ns: string[]) {
|
|
2035
|
-
embedCalls.push([...ns]);
|
|
2036
|
-
},
|
|
2037
|
-
};
|
|
2038
|
-
|
|
2039
|
-
await orchestrator.runQmdMaintenance();
|
|
2040
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2041
|
-
await orchestrator.runQmdMaintenance();
|
|
2042
|
-
|
|
2043
|
-
assert.deepEqual(updateCalls, [
|
|
2044
|
-
["default", "shared", "project-a"],
|
|
2045
|
-
["default", "shared", "project-b"],
|
|
2046
|
-
]);
|
|
2047
|
-
assert.deepEqual(
|
|
2048
|
-
embedCalls,
|
|
2049
|
-
[["default", "shared", "project-a"], ["project-b"]],
|
|
2050
|
-
"a global embed timestamp must not suppress embeddings for newly budgeted namespaces",
|
|
2051
|
-
);
|
|
2052
|
-
} finally {
|
|
2053
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2054
|
-
}
|
|
2055
|
-
});
|
|
2056
|
-
|
|
2057
|
-
test("runQmdMaintenance skips cataloged dynamic namespaces whose live root is unsafe", async () => {
|
|
2058
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2059
|
-
const updateArgs: string[] = [];
|
|
2060
|
-
const updateCalls: string[][] = [];
|
|
2061
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-unsafe-root-"));
|
|
2062
|
-
const outsideDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-unsafe-target-"));
|
|
2063
|
-
try {
|
|
2064
|
-
const dynamicNamespace = "project-origin-symlinked";
|
|
2065
|
-
const liveLegacyRoot = path.join(memoryDir, "namespaces", dynamicNamespace);
|
|
2066
|
-
const catalogSafeRoot = path.join(
|
|
2067
|
-
memoryDir,
|
|
2068
|
-
"namespaces",
|
|
2069
|
-
namespaceIdentityToken(dynamicNamespace),
|
|
2070
|
-
);
|
|
2071
|
-
await mkdir(path.dirname(liveLegacyRoot), { recursive: true });
|
|
2072
|
-
await symlink(outsideDir, liveLegacyRoot, "dir");
|
|
2073
|
-
|
|
2074
|
-
orchestrator.config = {
|
|
2075
|
-
memoryDir,
|
|
2076
|
-
namespacesEnabled: true,
|
|
2077
|
-
defaultNamespace: "default",
|
|
2078
|
-
sharedNamespace: "shared",
|
|
2079
|
-
namespacePolicies: [],
|
|
2080
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2081
|
-
qmdAutoEmbedEnabled: false,
|
|
2082
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2083
|
-
};
|
|
2084
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2085
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2086
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2087
|
-
orchestrator.namespaceCatalog = {
|
|
2088
|
-
enabled: true,
|
|
2089
|
-
async listNamespaces() {
|
|
2090
|
-
return [
|
|
2091
|
-
{
|
|
2092
|
-
namespace: dynamicNamespace,
|
|
2093
|
-
identityToken: namespaceIdentityToken(dynamicNamespace),
|
|
2094
|
-
kind: "project",
|
|
2095
|
-
createdAt: "2026-04-12T12:00:00.000Z",
|
|
2096
|
-
storageDir: catalogSafeRoot,
|
|
2097
|
-
discoveredBy: "write",
|
|
2098
|
-
},
|
|
2099
|
-
];
|
|
2100
|
-
},
|
|
2101
|
-
};
|
|
2102
|
-
orchestrator.namespaceSearchRouter = {
|
|
2103
|
-
async updateNamespacesDetailed(ns: string[]) {
|
|
2104
|
-
updateCalls.push([...ns]);
|
|
2105
|
-
updateArgs.push(...ns);
|
|
2106
|
-
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
2107
|
-
},
|
|
2108
|
-
async embedNamespaces() {},
|
|
2109
|
-
};
|
|
2110
|
-
|
|
2111
|
-
await orchestrator.runQmdMaintenance();
|
|
2112
|
-
|
|
2113
|
-
assert.ok(updateArgs.length > 0, "updateNamespaces must be called");
|
|
2114
|
-
assert.equal(updateCalls.length, 1, "global QMD maintenance must update once for the locked namespace set");
|
|
2115
|
-
assert.deepEqual(
|
|
2116
|
-
[...updateArgs].sort(),
|
|
2117
|
-
["default", "shared"],
|
|
2118
|
-
"cataloged dynamic namespaces are skipped when the live router root differs from the catalog-sanitized root",
|
|
2119
|
-
);
|
|
2120
|
-
} finally {
|
|
2121
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2122
|
-
await rm(outsideDir, { recursive: true, force: true });
|
|
2123
|
-
}
|
|
2124
|
-
});
|
|
2125
|
-
|
|
2126
|
-
test("runQmdMaintenance treats zero namespace updates as failed maintenance", async () => {
|
|
2127
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2128
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-zero-update-"));
|
|
2129
|
-
let markMaintenanceCalls = 0;
|
|
2130
|
-
let embedCalls = 0;
|
|
2131
|
-
try {
|
|
2132
|
-
orchestrator.config = {
|
|
2133
|
-
memoryDir,
|
|
2134
|
-
namespacesEnabled: true,
|
|
2135
|
-
defaultNamespace: "default",
|
|
2136
|
-
sharedNamespace: "shared",
|
|
2137
|
-
namespacePolicies: [],
|
|
2138
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2139
|
-
qmdAutoEmbedEnabled: false,
|
|
2140
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2141
|
-
};
|
|
2142
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2143
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2144
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2145
|
-
orchestrator.namespaceCatalog = {
|
|
2146
|
-
enabled: true,
|
|
2147
|
-
async listNamespaces() {
|
|
2148
|
-
return [{ namespace: "default" }];
|
|
2149
|
-
},
|
|
2150
|
-
async markMaintenance() {
|
|
2151
|
-
markMaintenanceCalls += 1;
|
|
2152
|
-
},
|
|
2153
|
-
};
|
|
2154
|
-
orchestrator.namespaceSearchRouter = {
|
|
2155
|
-
async updateNamespacesDetailed() {
|
|
2156
|
-
return { backendCount: 0, eligibleNamespaces: [] };
|
|
2157
|
-
},
|
|
2158
|
-
async embedNamespaces() {
|
|
2159
|
-
embedCalls += 1;
|
|
2160
|
-
},
|
|
2161
|
-
};
|
|
2162
|
-
|
|
2163
|
-
await orchestrator.runQmdMaintenance();
|
|
2164
|
-
|
|
2165
|
-
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2166
|
-
assert.ok(
|
|
2167
|
-
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2168
|
-
"zero updates should be recorded as failed maintenance, not a successful run",
|
|
2169
|
-
);
|
|
2170
|
-
assert.equal(markMaintenanceCalls, 0);
|
|
2171
|
-
assert.equal(embedCalls, 0);
|
|
2172
|
-
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2173
|
-
} finally {
|
|
2174
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2175
|
-
}
|
|
2176
|
-
});
|
|
2177
|
-
|
|
2178
|
-
test("runQmdMaintenance treats partial namespace update eligibility as failed maintenance", async () => {
|
|
2179
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2180
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-partial-update-"));
|
|
2181
|
-
let markMaintenanceCalls = 0;
|
|
2182
|
-
let embedCalls = 0;
|
|
2183
|
-
try {
|
|
2184
|
-
orchestrator.config = {
|
|
2185
|
-
memoryDir,
|
|
2186
|
-
namespacesEnabled: true,
|
|
2187
|
-
defaultNamespace: "default",
|
|
2188
|
-
sharedNamespace: "shared",
|
|
2189
|
-
namespacePolicies: [],
|
|
2190
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2191
|
-
qmdAutoEmbedEnabled: false,
|
|
2192
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2193
|
-
};
|
|
2194
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2195
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2196
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2197
|
-
orchestrator.namespaceCatalog = {
|
|
2198
|
-
enabled: true,
|
|
2199
|
-
async listNamespaces() {
|
|
2200
|
-
return [{ namespace: "default" }];
|
|
2201
|
-
},
|
|
2202
|
-
async markMaintenance() {
|
|
2203
|
-
markMaintenanceCalls += 1;
|
|
2204
|
-
},
|
|
2205
|
-
};
|
|
2206
|
-
orchestrator.namespaceSearchRouter = {
|
|
2207
|
-
async updateNamespacesDetailed(ns: string[]) {
|
|
2208
|
-
assert.ok(ns.includes("default") && ns.includes("shared"));
|
|
2209
|
-
return { backendCount: 1, eligibleNamespaces: ["default"] };
|
|
2210
|
-
},
|
|
2211
|
-
async embedNamespaces() {
|
|
2212
|
-
embedCalls += 1;
|
|
2213
|
-
},
|
|
2214
|
-
};
|
|
2215
|
-
|
|
2216
|
-
await orchestrator.runQmdMaintenance();
|
|
2217
|
-
|
|
2218
|
-
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2219
|
-
assert.ok(
|
|
2220
|
-
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2221
|
-
"partial update eligibility should not be recorded as successful maintenance",
|
|
2222
|
-
);
|
|
2223
|
-
assert.ok(
|
|
2224
|
-
statuses.some((status) => status.namespace === "shared" && status.state === "failed"),
|
|
2225
|
-
"ineligible selected namespaces should not be rotated as maintained",
|
|
2226
|
-
);
|
|
2227
|
-
assert.equal(markMaintenanceCalls, 0);
|
|
2228
|
-
assert.equal(embedCalls, 0);
|
|
2229
|
-
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2230
|
-
} finally {
|
|
2231
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2232
|
-
}
|
|
2233
|
-
});
|
|
2234
|
-
|
|
2235
|
-
test("runQmdMaintenance treats namespace embed errors as failed maintenance", async () => {
|
|
2236
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2237
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-embed-failure-"));
|
|
2238
|
-
let markMaintenanceCalls = 0;
|
|
2239
|
-
try {
|
|
2240
|
-
orchestrator.config = {
|
|
2241
|
-
memoryDir,
|
|
2242
|
-
namespacesEnabled: true,
|
|
2243
|
-
defaultNamespace: "default",
|
|
2244
|
-
sharedNamespace: "shared",
|
|
2245
|
-
namespacePolicies: [],
|
|
2246
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2247
|
-
qmdAutoEmbedEnabled: true,
|
|
2248
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2249
|
-
};
|
|
2250
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2251
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2252
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2253
|
-
orchestrator.namespaceCatalog = {
|
|
2254
|
-
enabled: true,
|
|
2255
|
-
async listNamespaces() {
|
|
2256
|
-
return [{ namespace: "default" }];
|
|
2257
|
-
},
|
|
2258
|
-
async markMaintenance() {
|
|
2259
|
-
markMaintenanceCalls += 1;
|
|
2260
|
-
},
|
|
2261
|
-
};
|
|
2262
|
-
orchestrator.namespaceSearchRouter = {
|
|
2263
|
-
async updateNamespacesDetailed(ns: string[]) {
|
|
2264
|
-
return { backendCount: 1, eligibleNamespaces: ns };
|
|
2265
|
-
},
|
|
2266
|
-
async embedNamespaces() {
|
|
2267
|
-
throw Object.assign(new Error("embed failed"), { code: "EQMD" });
|
|
2268
|
-
},
|
|
2269
|
-
};
|
|
2270
|
-
|
|
2271
|
-
await orchestrator.runQmdMaintenance();
|
|
2272
|
-
|
|
2273
|
-
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2274
|
-
assert.ok(
|
|
2275
|
-
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2276
|
-
"embed failures should not be recorded as successful namespace maintenance",
|
|
2277
|
-
);
|
|
2278
|
-
assert.equal(markMaintenanceCalls, 0);
|
|
2279
|
-
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2280
|
-
} finally {
|
|
2281
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2282
|
-
}
|
|
2283
|
-
});
|
|
2284
|
-
|
|
2285
|
-
test("runQmdMaintenance records QMD min-interval throttles as skipped maintenance", async () => {
|
|
2286
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2287
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-throttled-update-"));
|
|
2288
|
-
let markMaintenanceCalls = 0;
|
|
2289
|
-
let embedCalls = 0;
|
|
2290
|
-
let updateCalls = 0;
|
|
2291
|
-
try {
|
|
2292
|
-
orchestrator.config = {
|
|
2293
|
-
memoryDir,
|
|
2294
|
-
namespacesEnabled: true,
|
|
2295
|
-
defaultNamespace: "default",
|
|
2296
|
-
sharedNamespace: "shared",
|
|
2297
|
-
namespacePolicies: [],
|
|
2298
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2299
|
-
qmdAutoEmbedEnabled: false,
|
|
2300
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2301
|
-
};
|
|
2302
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2303
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2304
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2305
|
-
orchestrator.namespaceCatalog = {
|
|
2306
|
-
enabled: true,
|
|
2307
|
-
async listNamespaces() {
|
|
2308
|
-
return [{ namespace: "default" }];
|
|
2309
|
-
},
|
|
2310
|
-
async markMaintenance() {
|
|
2311
|
-
markMaintenanceCalls += 1;
|
|
2312
|
-
},
|
|
2313
|
-
};
|
|
2314
|
-
orchestrator.namespaceSearchRouter = {
|
|
2315
|
-
async updateNamespacesDetailed(_ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
2316
|
-
updateCalls += 1;
|
|
2317
|
-
assert.equal(options?.strict, true, "recurring maintenance must use strict QMD updates");
|
|
2318
|
-
throw new Error("QMD update skipped by global min-interval gate");
|
|
2319
|
-
},
|
|
2320
|
-
async embedNamespaces() {
|
|
2321
|
-
embedCalls += 1;
|
|
2322
|
-
},
|
|
2323
|
-
};
|
|
2324
|
-
|
|
2325
|
-
await orchestrator.runQmdMaintenance();
|
|
2326
|
-
|
|
2327
|
-
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2328
|
-
assert.equal(updateCalls, 1, "strict global QMD maintenance should be attempted once");
|
|
2329
|
-
assert.ok(
|
|
2330
|
-
statuses.some(
|
|
2331
|
-
(status) =>
|
|
2332
|
-
status.namespace === "default" &&
|
|
2333
|
-
status.state === "skipped" &&
|
|
2334
|
-
status.reason === "throttled",
|
|
2335
|
-
),
|
|
2336
|
-
"QMD min-interval throttles should be recorded as skipped maintenance",
|
|
2337
|
-
);
|
|
2338
|
-
assert.ok(
|
|
2339
|
-
statuses.some(
|
|
2340
|
-
(status) =>
|
|
2341
|
-
status.namespace === "shared" &&
|
|
2342
|
-
status.state === "skipped" &&
|
|
2343
|
-
status.reason === "throttled",
|
|
2344
|
-
),
|
|
2345
|
-
"every selected namespace should receive the throttled skip status",
|
|
2346
|
-
);
|
|
2347
|
-
assert.equal(markMaintenanceCalls, 0);
|
|
2348
|
-
assert.equal(embedCalls, 0);
|
|
2349
|
-
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2350
|
-
} finally {
|
|
2351
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2352
|
-
}
|
|
2353
|
-
});
|
|
2354
|
-
|
|
2355
|
-
test("runQmdMaintenance still embeds when due update is throttled", async () => {
|
|
2356
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2357
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-throttled-update-embed-"));
|
|
2358
|
-
let markMaintenanceCalls = 0;
|
|
2359
|
-
let updateCalls = 0;
|
|
2360
|
-
const embedCalls: string[][] = [];
|
|
2361
|
-
try {
|
|
2362
|
-
orchestrator.config = {
|
|
2363
|
-
memoryDir,
|
|
2364
|
-
namespacesEnabled: true,
|
|
2365
|
-
defaultNamespace: "default",
|
|
2366
|
-
sharedNamespace: "shared",
|
|
2367
|
-
namespacePolicies: [],
|
|
2368
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2369
|
-
qmdAutoEmbedEnabled: true,
|
|
2370
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2371
|
-
};
|
|
2372
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2373
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2374
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2375
|
-
orchestrator.namespaceCatalog = {
|
|
2376
|
-
enabled: true,
|
|
2377
|
-
async listNamespaces() {
|
|
2378
|
-
return [{ namespace: "default" }];
|
|
2379
|
-
},
|
|
2380
|
-
async markMaintenance() {
|
|
2381
|
-
markMaintenanceCalls += 1;
|
|
2382
|
-
},
|
|
2383
|
-
};
|
|
2384
|
-
orchestrator.namespaceSearchRouter = {
|
|
2385
|
-
async updateNamespacesDetailed(_ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
2386
|
-
updateCalls += 1;
|
|
2387
|
-
assert.equal(options?.strict, true, "recurring maintenance must use strict QMD updates");
|
|
2388
|
-
throw new Error("QMD update skipped by global min-interval gate");
|
|
2389
|
-
},
|
|
2390
|
-
async embedNamespaces(ns: string[], options?: { strict?: boolean }) {
|
|
2391
|
-
assert.equal(options?.strict, true, "due embed retries must surface embed failures");
|
|
2392
|
-
embedCalls.push([...ns]);
|
|
2393
|
-
},
|
|
2394
|
-
};
|
|
2395
|
-
|
|
2396
|
-
await orchestrator.runQmdMaintenance();
|
|
2397
|
-
|
|
2398
|
-
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2399
|
-
assert.equal(updateCalls, 1, "strict global QMD maintenance should be attempted once");
|
|
2400
|
-
assert.deepEqual(embedCalls, [["default", "shared"]]);
|
|
2401
|
-
assert.ok(
|
|
2402
|
-
statuses.every((status) => status.state === "skipped" && status.reason === "throttled"),
|
|
2403
|
-
"a throttled update should still be recorded as skipped after the due embed retry",
|
|
2404
|
-
);
|
|
2405
|
-
assert.equal(markMaintenanceCalls, 0);
|
|
2406
|
-
assert.notEqual(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2407
|
-
} finally {
|
|
2408
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2409
|
-
}
|
|
2410
|
-
});
|
|
2411
|
-
|
|
2412
|
-
test("runQmdMaintenance treats strict namespace update errors as failed maintenance", async () => {
|
|
2413
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2414
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-update-failure-"));
|
|
2415
|
-
let markMaintenanceCalls = 0;
|
|
2416
|
-
let embedCalls = 0;
|
|
2417
|
-
let updateCalls = 0;
|
|
2418
|
-
try {
|
|
2419
|
-
orchestrator.config = {
|
|
2420
|
-
memoryDir,
|
|
2421
|
-
namespacesEnabled: true,
|
|
2422
|
-
defaultNamespace: "default",
|
|
2423
|
-
sharedNamespace: "shared",
|
|
2424
|
-
namespacePolicies: [],
|
|
2425
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2426
|
-
qmdAutoEmbedEnabled: true,
|
|
2427
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2428
|
-
};
|
|
2429
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2430
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2431
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2432
|
-
orchestrator.namespaceCatalog = {
|
|
2433
|
-
enabled: true,
|
|
2434
|
-
async listNamespaces() {
|
|
2435
|
-
return [{ namespace: "default" }];
|
|
2436
|
-
},
|
|
2437
|
-
async markMaintenance() {
|
|
2438
|
-
markMaintenanceCalls += 1;
|
|
2439
|
-
},
|
|
2440
|
-
};
|
|
2441
|
-
orchestrator.namespaceSearchRouter = {
|
|
2442
|
-
async updateNamespacesDetailed(_ns: string[], _execution?: unknown, options?: { strict?: boolean }) {
|
|
2443
|
-
updateCalls += 1;
|
|
2444
|
-
assert.equal(options?.strict, true, "recurring maintenance must require strict update failure propagation");
|
|
2445
|
-
throw new Error("qmd exploded");
|
|
2446
|
-
},
|
|
2447
|
-
async embedNamespaces() {
|
|
2448
|
-
embedCalls += 1;
|
|
2449
|
-
},
|
|
2450
|
-
};
|
|
2451
|
-
|
|
2452
|
-
await orchestrator.runQmdMaintenance();
|
|
2453
|
-
|
|
2454
|
-
const statuses = await readNamespaceMaintenanceStatuses(orchestrator.config);
|
|
2455
|
-
assert.equal(updateCalls, 1, "strict global QMD maintenance should be attempted once");
|
|
2456
|
-
assert.ok(
|
|
2457
|
-
statuses.some((status) => status.namespace === "default" && status.state === "failed"),
|
|
2458
|
-
"strict update errors should be recorded as failed maintenance",
|
|
2459
|
-
);
|
|
2460
|
-
assert.equal(markMaintenanceCalls, 0);
|
|
2461
|
-
assert.equal(embedCalls, 0);
|
|
2462
|
-
assert.equal(orchestrator.lastQmdEmbedAtMs, 0);
|
|
2463
|
-
} finally {
|
|
2464
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2465
|
-
}
|
|
2466
|
-
});
|
|
2467
|
-
|
|
2468
|
-
// NGnei fallback: when the catalog is disabled, maintenance covers exactly the
|
|
2469
|
-
// configured set (no catalog read), and a catalog read failure degrades to the
|
|
2470
|
-
// configured set rather than breaking maintenance.
|
|
2471
|
-
test("runQmdMaintenance falls back to configured namespaces when the catalog is disabled (NGnei)", async () => {
|
|
2472
|
-
const orchestrator = Object.create(Orchestrator.prototype) as any;
|
|
2473
|
-
const updateArgs: string[] = [];
|
|
2474
|
-
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-qmd-disabled-catalog-"));
|
|
2475
|
-
|
|
2476
|
-
try {
|
|
2477
|
-
orchestrator.config = {
|
|
2478
|
-
memoryDir,
|
|
2479
|
-
namespacesEnabled: true,
|
|
2480
|
-
defaultNamespace: "default",
|
|
2481
|
-
sharedNamespace: "shared",
|
|
2482
|
-
namespacePolicies: [],
|
|
2483
|
-
maintenanceNamespaceLockStaleMs: 100,
|
|
2484
|
-
qmdAutoEmbedEnabled: false,
|
|
2485
|
-
qmdEmbedMinIntervalMs: 0,
|
|
2486
|
-
};
|
|
2487
|
-
orchestrator.qmdMaintenanceInFlight = false;
|
|
2488
|
-
orchestrator.qmdMaintenancePending = true;
|
|
2489
|
-
orchestrator.lastQmdEmbedAtMs = 0;
|
|
2490
|
-
orchestrator.namespaceCatalog = {
|
|
2491
|
-
enabled: false,
|
|
2492
|
-
async listNamespaces() {
|
|
2493
|
-
throw new Error("catalog disabled — must not be read");
|
|
2494
|
-
},
|
|
2495
|
-
};
|
|
2496
|
-
orchestrator.namespaceSearchRouter = {
|
|
2497
|
-
async updateNamespacesDetailed(ns: string[]) {
|
|
2498
|
-
updateArgs.push(...ns);
|
|
2499
|
-
return { backendCount: ns.length, eligibleNamespaces: ns };
|
|
2500
|
-
},
|
|
2501
|
-
async embedNamespaces() {},
|
|
2502
|
-
};
|
|
2503
|
-
|
|
2504
|
-
await orchestrator.runQmdMaintenance();
|
|
2505
|
-
|
|
2506
|
-
assert.ok(updateArgs.length > 0, "updateNamespaces must be called");
|
|
2507
|
-
assert.deepEqual(
|
|
2508
|
-
[...updateArgs].sort(),
|
|
2509
|
-
["default", "shared"],
|
|
2510
|
-
"a disabled catalog covers exactly the configured set",
|
|
2511
|
-
);
|
|
2512
|
-
} finally {
|
|
2513
|
-
await rm(memoryDir, { recursive: true, force: true });
|
|
2514
|
-
}
|
|
2515
|
-
});
|
|
2516
|
-
|
|
2517
1918
|
// ── NHZEV (codex P2): the QMD STARTUP sync in deferredInitialize() must cover
|
|
2518
1919
|
// cataloged dynamic namespaces too, not only configuredNamespaces(). A dynamic
|
|
2519
1920
|
// namespace written before a daemon restart exists ONLY in the persisted catalog;
|