From 374d201f3239ceb82b216fd5da2ada60225f1f7d Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Mon, 13 Apr 2026 13:18:14 -0400 Subject: [PATCH 01/14] collab with Bob --- scripts/js/lib/links/ExternalLink.test.ts | 112 ++++++++++++++++++++++ scripts/js/lib/links/ExternalLink.ts | 33 ++++++- scripts/js/lib/links/ignores.ts | 7 ++ 3 files changed, 147 insertions(+), 5 deletions(-) diff --git a/scripts/js/lib/links/ExternalLink.test.ts b/scripts/js/lib/links/ExternalLink.test.ts index 2b72fcd6ccc..a03ad623793 100644 --- a/scripts/js/lib/links/ExternalLink.test.ts +++ b/scripts/js/lib/links/ExternalLink.test.ts @@ -72,4 +72,116 @@ test.describe("ExternalLink.check()", () => { "❌ Failed to fetch 'https://bad-link.com': some issue. Appears in:\n /testorigin.mdx", ); }); + + test("301 redirect is considered valid", async () => { + global.fetch = () => Promise.resolve(new Response("", { status: 301 })); + let link = new ExternalLink("https://redirect-link.com", [ + "/testorigin.mdx", + ]); + const result = await link.check(); + expect(result).toBeUndefined(); + }); + + test("302 redirect is considered valid", async () => { + global.fetch = () => Promise.resolve(new Response("", { status: 302 })); + let link = new ExternalLink("https://redirect-link.com", [ + "/testorigin.mdx", + ]); + const result = await link.check(); + expect(result).toBeUndefined(); + }); + + test("403 Forbidden is considered valid", async () => { + global.fetch = () => Promise.resolve(new Response("", { status: 403 })); + let link = new ExternalLink("https://forbidden-link.com", [ + "/testorigin.mdx", + ]); + const result = await link.check(); + expect(result).toBeUndefined(); + }); + + test("429 Too Many Requests is considered valid", async () => { + global.fetch = () => Promise.resolve(new Response("", { status: 429 })); + let link = new ExternalLink("https://rate-limited-link.com", [ + "/testorigin.mdx", + ]); + const result = await link.check(); + expect(result).toBeUndefined(); + }); + + test("405 Method Not Allowed triggers GET fallback", async () => { + let callCount = 0; + global.fetch = (url: any, options: any) => { + callCount++; + if (callCount === 1) { + // First call (HEAD) returns 405 + expect(options.method).toBe("HEAD"); + return Promise.resolve(new Response("", { status: 405 })); + } else { + // Second call (GET) returns 200 + expect(options.method).toBe("GET"); + return Promise.resolve(new Response("", { status: 200 })); + } + }; + let link = new ExternalLink("https://pdf-link.com/file.pdf", [ + "/testorigin.mdx", + ]); + const result = await link.check(); + expect(result).toBeUndefined(); + expect(callCount).toBe(2); // Verify both HEAD and GET were called + }); + + test("501 Not Implemented triggers GET fallback", async () => { + let callCount = 0; + global.fetch = (url: any, options: any) => { + callCount++; + if (callCount === 1) { + // First call (HEAD) returns 501 + expect(options.method).toBe("HEAD"); + return Promise.resolve(new Response("", { status: 501 })); + } else { + // Second call (GET) returns 200 + expect(options.method).toBe("GET"); + return Promise.resolve(new Response("", { status: 200 })); + } + }; + let link = new ExternalLink("https://binary-link.com/file.zip", [ + "/testorigin.mdx", + ]); + const result = await link.check(); + expect(result).toBeUndefined(); + expect(callCount).toBe(2); + }); + + test("URL with escape characters is handled correctly", async () => { + global.fetch = (url: any) => { + // Verify the URL is properly normalized + expect(url).toContain("%20"); + return Promise.resolve(new Response("", { status: 200 })); + }; + let link = new ExternalLink( + "https://example.com/file%20with%20spaces.pdf", + ["/testorigin.mdx"], + ); + const result = await link.check(); + expect(result).toBeUndefined(); + }); + + test("405 followed by GET failure still reports error", async () => { + let callCount = 0; + global.fetch = (url: any, options: any) => { + callCount++; + if (callCount === 1) { + return Promise.resolve(new Response("", { status: 405 })); + } else { + return Promise.resolve(new Response("", { status: 404 })); + } + }; + let link = new ExternalLink("https://bad-pdf.com/missing.pdf", [ + "/testorigin.mdx", + ]); + const result = await link.check(); + expect(result).toContain("Could not find link"); + expect(result).toContain("404"); + }); }); diff --git a/scripts/js/lib/links/ExternalLink.ts b/scripts/js/lib/links/ExternalLink.ts index 98c0866e312..28660a2c404 100644 --- a/scripts/js/lib/links/ExternalLink.ts +++ b/scripts/js/lib/links/ExternalLink.ts @@ -34,7 +34,15 @@ export class ExternalLink { * Returns an error message if link failed. */ async check(): Promise { - const result = await safeFetch(this.value); + // Try HEAD request first (faster, less bandwidth) + let result = await safeFetch(this.value, "HEAD"); + + // If HEAD returns 405 (Method Not Allowed) or 501 (Not Implemented), + // retry with GET as many servers don't support HEAD for PDFs and binary files + if ("response" in result && shouldRetryWithGet(result.response.status)) { + result = await safeFetch(this.value, "GET"); + } + const error = "response" in result ? responseToErrorMessage(this.value, result.response) @@ -53,11 +61,18 @@ export class ExternalLink { async function safeFetch( link: string, + method: "HEAD" | "GET" = "HEAD", ): Promise<{ response: Response } | { error: Error }> { try { - const response = await fetch(link, { + // Normalize URL to handle encoding issues + const normalizedUrl = new URL(link).href; + + const response = await fetch(normalizedUrl, { headers: getHeaders(link), - method: "HEAD", + method: method, + // For GET requests, we only need headers to check if link is valid + // Using signal to abort after receiving headers saves bandwidth + signal: method === "GET" ? AbortSignal.timeout(10000) : undefined, }); return { response }; } catch (err) { @@ -70,16 +85,24 @@ function responseToErrorMessage( response: Response, ): string | undefined { const httpCode = response.status; - const isOk = httpCode >= 100 && httpCode < 300; + + // Accept 1xx-3xx as valid (including redirects) + // Accept 403 as potentially valid (resource exists but access is forbidden) + // Accept 429 as valid (resource exists but we're being rate limited) + const isOk = (httpCode >= 100 && httpCode < 400) || httpCode === 403 || httpCode === 429; if (isOk) return undefined; if (httpCode === 404) return `Could not find link '${link}' (${httpCode})`; if (httpCode === 410) return `Link '${link}' has been removed (${httpCode})`; - if (httpCode === 418) return `Link '${link}' is a teapot (${httpCode})`; return `Link '${link}' returned unexpected code: ${httpCode}`; } +function shouldRetryWithGet(status: number): boolean { + // Retry with GET when HEAD is not supported + return status === 405 || status === 501; +} + export function getHeaders(link: string): HeadersInit { const headers: HeadersInit = { "User-Agent": "qiskit-documentation-scripts", diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index 622897ab8cf..6c3a364a673 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -34,6 +34,7 @@ const FORBIDS_OUR_USER_AGENT = [ "http://dx.doi.org/10.1145/1278349.1278355", "https://academic.oup.com/book/36426", "https://crates.io/crates/faer", + "https://crates.io/crates/log", "https://dl.acm.org/doi/10.1145/237814.237838", "https://dl.acm.org/doi/10.1145/237814.237866", "https://dl.acm.org/doi/10.1145/3445814.3446706", @@ -173,6 +174,12 @@ const ALWAYS_IGNORED_URLS__SHOULD_FIX: string[] = [ "https://auth.quantum.ibm.com/api", "https://quantum-computing.cloud.ibm.com", + // Contains escape characters, but is only the hyperlink text anyway; the actual hyperlink is properly formatted and works + "https://en.wikipedia.org/wiki/Time-evolving\_block\_decimation", + + // In an old version of qiskit-ibm-runtime that we won't update + "https://qiskit.github.io/qiskit-serverless/migration/migration_from_qiskit_runtime_programs.html", + "https://qiskit.github.io/qiskit-serverless/migration/migration\_from\_qiskit\_runtime\_programs.html", // Other links that don't seem to exist any more "https://www.cs.bham.ac.uk/~xin/papers/published_tec_sep00_constraint.pdf", "https://www.globaldataquantum.com/en/quantum-portfolio-optimizer/#form", From 468a3cd37652b39533d5f15b67e1baecbca9db55 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Mon, 13 Apr 2026 13:26:19 -0400 Subject: [PATCH 02/14] backslash ignore --- scripts/js/lib/links/ignores.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index 6c3a364a673..cdd548de347 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -143,6 +143,9 @@ const ALWAYS_IGNORED_URLS__EXPECTED = [ // the same status code. Sometimes it's 404, sometimes 503, sometimes 200 etc. // They do work whenever I've tested them in a browser. "https://csrc.nist.gov/news/2023/three-draft-fips-for-post-quantum-cryptography", + + // Markdown reference definitions with escaped characters that are extracted as links + "https://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/#controlled-random-search-crs-with-local-mutation", "https://csrc.nist.gov/pubs/fips/205/ipd", "https://doi.org/10.6028/jres.104.027", "https://eprint.iacr.org/2012/090", From e85a3b4ab34265996a7f972cd5cf50c9d251d8b7 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Mon, 13 Apr 2026 13:41:15 -0400 Subject: [PATCH 03/14] Update ignores.ts --- scripts/js/lib/links/ignores.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index cdd548de347..2065830cf3d 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -126,9 +126,12 @@ const FETCH_RETURNS_405 = [ // Fetching these links throws an error, but they work in-browser. Not sure why. const FETCH_FAILS = [ "https://www.cs.tau.ac.il/~nogaa/PDFS/r.pdf", + "https://www.cs.tau.ac.il/\\~nogaa/PDFS/r.pdf", // Escaped version from markdown "https://www.mckinsey.com/business-functions/mckinsey-digital/our-insights/quantum-computing-use-cases-are-getting-real-what-you-need-to-know", "https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/quantum-computing-just-might-save-the-planet", + "https://www.mckinsey.com/capabilities/mckinsey-technology/our-insights/solving-chemistrys-toughest-problems-the-quantum-computing-advantage", "https://www.mckinsey.com/industries/chemicals/our-insights/the-next-big-thing-quantum-computings-potential-impact-on-chemicals?cid=eml-web", + "https://www.mckinsey.com/capabilities/tech-and-ai/our-insights/the-year-of-quantum-from-concept-to-reality-in-2025", // The following link is only accessible through IBM VPN "https://w3.ibm.com/w3publisher/w3-privacy-notice", ]; @@ -146,6 +149,9 @@ const ALWAYS_IGNORED_URLS__EXPECTED = [ // Markdown reference definitions with escaped characters that are extracted as links "https://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/#controlled-random-search-crs-with-local-mutation", + "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin\\_l\\_bfgs\\_b.html", + "https://qiskit.org/ecosystem/aer/apidocs/aer\\_library.html", + "https://github.com/Qiskit/openqasm3\\_parser", "https://csrc.nist.gov/pubs/fips/205/ipd", "https://doi.org/10.6028/jres.104.027", "https://eprint.iacr.org/2012/090", @@ -182,7 +188,7 @@ const ALWAYS_IGNORED_URLS__SHOULD_FIX: string[] = [ // In an old version of qiskit-ibm-runtime that we won't update "https://qiskit.github.io/qiskit-serverless/migration/migration_from_qiskit_runtime_programs.html", - "https://qiskit.github.io/qiskit-serverless/migration/migration\_from\_qiskit\_runtime\_programs.html", + "https://qiskit.github.io/qiskit-serverless/migration/migration\\_from\\_qiskit\\_runtime\\_programs.html", // Other links that don't seem to exist any more "https://www.cs.bham.ac.uk/~xin/papers/published_tec_sep00_constraint.pdf", "https://www.globaldataquantum.com/en/quantum-portfolio-optimizer/#form", From 8f158b789b819ea275c213e621821708e916a33d Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Mon, 13 Apr 2026 13:59:17 -0400 Subject: [PATCH 04/14] Update ignores.ts --- scripts/js/lib/links/ignores.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index 2065830cf3d..f596b0bcd2e 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -127,6 +127,7 @@ const FETCH_RETURNS_405 = [ const FETCH_FAILS = [ "https://www.cs.tau.ac.il/~nogaa/PDFS/r.pdf", "https://www.cs.tau.ac.il/\\~nogaa/PDFS/r.pdf", // Escaped version from markdown + "https://www.epo.org/en/news-events/press-centre/press-release/2025/1361562", "https://www.mckinsey.com/business-functions/mckinsey-digital/our-insights/quantum-computing-use-cases-are-getting-real-what-you-need-to-know", "https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/quantum-computing-just-might-save-the-planet", "https://www.mckinsey.com/capabilities/mckinsey-technology/our-insights/solving-chemistrys-toughest-problems-the-quantum-computing-advantage", @@ -184,7 +185,7 @@ const ALWAYS_IGNORED_URLS__SHOULD_FIX: string[] = [ "https://quantum-computing.cloud.ibm.com", // Contains escape characters, but is only the hyperlink text anyway; the actual hyperlink is properly formatted and works - "https://en.wikipedia.org/wiki/Time-evolving\_block\_decimation", + "https://en.wikipedia.org/wiki/Time-evolving\\_block\\_decimation", // In an old version of qiskit-ibm-runtime that we won't update "https://qiskit.github.io/qiskit-serverless/migration/migration_from_qiskit_runtime_programs.html", From cd3410127ec17fcc2e2c45b92d6a5948f7d66c70 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Mon, 13 Apr 2026 14:15:09 -0400 Subject: [PATCH 05/14] rmv dead link --- .../courses/quantum-business-foundations/business-impacts.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/learning/courses/quantum-business-foundations/business-impacts.mdx b/learning/courses/quantum-business-foundations/business-impacts.mdx index ee79245e713..5e631d9ca96 100644 --- a/learning/courses/quantum-business-foundations/business-impacts.mdx +++ b/learning/courses/quantum-business-foundations/business-impacts.mdx @@ -133,8 +133,6 @@ Review the resources listed here to learn more about how IBM Quantum computers a As part of its decarbonization efforts, E.ON has partnered with IBM to explore the potential of quantum computing to [optimize the world’s increasingly decentralized energy infrastructure](https://www.eon.com/en/about-us/media/press-release/2021/2021-09-02-eon-allies-with-ibm-quantum.html). “You plug in your electric car to charge the battery, and you might have a solar panel that powers your house and car. But can you sell that excess energy to your neighbors down the road? Why do you have to get energy from thousands of kilometers away that was made in the power plant burning gas?” asks Corey O’Meara, E.ON digital technology quantum computing lead (see [“IBM Panel Highlights Quantum Role in Sustainability”](https://www.iotworldtoday.com/smart-cities/ibm-panel-highlights-quantum-role-in-sustainability)). Quantum computing algorithms could hold the key to managing the complexity that results when additional assets are plugged into the grid. -The potential for quantum computing to aid in the discovery of new materials designed to improve the generation, transfer, and storage of energy is one reason why bp is allying with IBM Quantum to [achieve its net-zero goals](https://newsroom.ibm.com/2021-02-15-bp-joins-the-IBM-Quantum-Network-to-advance-use-of-quantum-computing-in-energy). - Woodside Energy, an IBM partner, is experimenting with new algorithms to reduce the overhead of data transfers between classical and quantum systems, making it possible to [apply quantum kernels to streaming data](https://arxiv.org/abs/2112.08449). In the telecommunications industry, quantum computing shows potential to deliver solutions for network traffic routing and workload balancing, GHG/energy consumption, and contextual customer segmentation. Vodafone is partnering with IBM Quantum to help [validate and progress potential quantum use cases in telecommunications](https://newsroom.ibm.com/2022-11-09-IBM-and-Vodafone-Join-Forces-in-Exploration-of-Quantum-Computing-Technology-and-Quantum-Safe-Cryptography). From 9f859fca4d7f49cd19ca1a1751673313ce06c7c7 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Tue, 14 Apr 2026 16:50:52 -0400 Subject: [PATCH 06/14] fix link --- docs/responsible-quantum-computing.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/responsible-quantum-computing.mdx b/docs/responsible-quantum-computing.mdx index b63d1b5c45c..dfb0f28df6b 100644 --- a/docs/responsible-quantum-computing.mdx +++ b/docs/responsible-quantum-computing.mdx @@ -38,5 +38,5 @@ Finally, as part of our responsible quantum effort, we are committed to fosterin - [IBM Quantum Safe](https://www.ibm.com/quantum/quantum-safe) - [Blog: The era of quantum utility must also be the era of responsible quantum computing](https://www.ibm.com/quantum/blog/responsible-quantum) - - [IBM Research Responsible and Inclusive Tech](https://research.ibm.com/projects/responsible-and-inclusive-tech-research) + - [Topic: Responsible Technology (IBM Research)](https://research.ibm.com/topics/responsible-technology) \ No newline at end of file From b5d6833a50e52e8247bdff224dab93a310745354 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Tue, 14 Apr 2026 16:54:55 -0400 Subject: [PATCH 07/14] manually update some links --- docs/api/functions/qunova-chemistry.mdx | 2 +- docs/guides/multiverse-computing-singularity.ipynb | 4 ++-- docs/tutorials/quantum-kernel-training.ipynb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/functions/qunova-chemistry.mdx b/docs/api/functions/qunova-chemistry.mdx index 1463cb7dd15..3085269ec2f 100644 --- a/docs/api/functions/qunova-chemistry.mdx +++ b/docs/api/functions/qunova-chemistry.mdx @@ -198,7 +198,7 @@ Whether or not to use configuration [`recovery`](/docs/api/qiskit-addon-sqd/conf #### `ansatz_entanglement` -This specifies the entanglement scheme that should be used within the quantum circuit, following common Qiskit and [ffsim conventions for the LUCJ ansatz](https://qiskit-community.github.io/ffsim/how-to-guides/simulate-lucj.html). +This specifies the entanglement scheme that should be used within the quantum circuit, following common Qiskit and [ffsim conventions for the LUCJ ansatz](https://qiskit-community.github.io/ffsim/how-to-guides/qiskit-lucj.html). - Valid range: Any one of `"linear"`, `"reverse_linear"`, `"pairwise"`, `"circular"`, `"full"`, or `"sca"`. If using the `"lucj"` ansatz, `"lucj_default"` is also an option. diff --git a/docs/guides/multiverse-computing-singularity.ipynb b/docs/guides/multiverse-computing-singularity.ipynb index 601259fc336..1e5d2432165 100644 --- a/docs/guides/multiverse-computing-singularity.ipynb +++ b/docs/guides/multiverse-computing-singularity.ipynb @@ -179,7 +179,7 @@ "Perform the following steps:\n", "\n", "1) Create the synthetic dataset using the [`make_moons`](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_moons.html) function from [scikit-learn](https://scikit-learn.org/).\n", - "2) Upload the generated synthetic dataset to the [shared data directory](https://qiskit.github.io/qiskit-serverless/getting_started/experimental/manage_data_directory.html).\n", + "2) Upload the generated synthetic dataset to the [shared data directory](https://qiskit.github.io/qiskit-serverless/function_features/experimental/manage_data_directory.html).\n", "3) Create the quantum-enhanced classifier using the [`create`](/docs/api/functions/multiverse-computing-singularity#create) action.\n", "4) Enlist your classifiers using the [`list`](/docs/api/functions/multiverse-computing-singularity#list) action.\n", "5) Train the classifier on the train data using the [`fit`](/docs/api/functions/multiverse-computing-singularity#fit) action.\n", @@ -247,7 +247,7 @@ "id": "639f459f", "metadata": {}, "source": [ - "**Step 2.** Save the labeled training and test datasets on your local disk, and then upload them to the [shared data directory](https://qiskit.github.io/qiskit-serverless/getting_started/experimental/manage_data_directory.html)." + "**Step 2.** Save the labeled training and test datasets on your local disk, and then upload them to the [shared data directory](https://qiskit.github.io/qiskit-serverless/function_features/experimental/manage_data_directory.html)." ] }, { diff --git a/docs/tutorials/quantum-kernel-training.ipynb b/docs/tutorials/quantum-kernel-training.ipynb index 2d07485ef08..9ca5223336d 100644 --- a/docs/tutorials/quantum-kernel-training.ipynb +++ b/docs/tutorials/quantum-kernel-training.ipynb @@ -322,7 +322,7 @@ "source": [ "## Deploy the Qiskit pattern to the cloud\n", "\n", - "To do this, move the source code above to a file, `./source/generate_kernel_entry.py`, wrap the code in a script which takes inputs returns the final solution, and finally upload it to a remote cluster using the `QiskitFunction` class from `Qiskit Serverless`. For guidance on specifying external dependencies, passing input arguments, and more, check out the [Qiskit Serverless guides](https://qiskit.github.io/qiskit-serverless/getting_started/index.html).\n", + "To do this, move the source code above to a file, `./source/generate_kernel_entry.py`, wrap the code in a script which takes inputs returns the final solution, and finally upload it to a remote cluster using the `QiskitFunction` class from `Qiskit Serverless`. For guidance on specifying external dependencies, passing input arguments, and more, check out the [Qiskit Serverless guides](https://qiskit.github.io/qiskit-serverless/function_features/index.html#function-features).\n", "\n", "The input to the Pattern is a pair of data samples, `x1` and `x2`. The output is the fidelity between the two samples. This value will be used to populate the kernel matrix entry corresponding to these two samples." ] From e34acc75abf1e4e99b9372494cbf4a8ba7b8bfc4 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Tue, 14 Apr 2026 16:58:10 -0400 Subject: [PATCH 08/14] more manual fixes --- learning/courses/quantum-safe-cryptography/index.mdx | 2 +- learning/courses/quantum-safe-cryptography/works-cited.mdx | 2 +- scripts/js/lib/links/ignores.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/learning/courses/quantum-safe-cryptography/index.mdx b/learning/courses/quantum-safe-cryptography/index.mdx index f00763d7ebd..72e237ec322 100644 --- a/learning/courses/quantum-safe-cryptography/index.mdx +++ b/learning/courses/quantum-safe-cryptography/index.mdx @@ -28,7 +28,7 @@ PDFs of lecture notes will be available soon - [Fundamentals of quantum algorithms](/learning/courses/fundamentals-of-quantum-algorithms/index), which explores computational advantages of quantum systems, and includes further details on algorithms such as Grover's algorithm - [IBM Quantum Safe™](https://www.ibm.com/quantum/quantum-safe) - [Why it’s time to take quantum-safe cryptography seriously](https://research.ibm.com/blog/quantum-safe-cryptography-for-industry) -- [Fundamentals of Encryption & Quantum-Safe Techniques](https://catalog.skills.network/2766) +- [Fundamentals of Encryption & Quantum-Safe Techniques](https://catalog.skills.network/catalog_item/2766/) ## Exam diff --git a/learning/courses/quantum-safe-cryptography/works-cited.mdx b/learning/courses/quantum-safe-cryptography/works-cited.mdx index 21ca6c7416a..e130780b33a 100644 --- a/learning/courses/quantum-safe-cryptography/works-cited.mdx +++ b/learning/courses/quantum-safe-cryptography/works-cited.mdx @@ -280,4 +280,4 @@ description: This document provides a list of all citations used within the cour [135] Fujisaki, Eiichiro, and Okamato, Tatsuaki, "Secure Integration of Symmetric and Asymmetric Encryption Schemes," Journal of Crytography, pp. 80-101, Jan. 2013, doi: [10.1007/s00145-011-9114-1](https://doi.org/10.1007/s00145-011-9114-1) -[136] Snow, Dwaine, "Fundamentals of Encryption & Quantum-Safe Techniques," Skills Network. [Online]. Available: [https://catalog.skills.network/2766](https://catalog.skills.network/2766) +[136] Snow, Dwaine, "Fundamentals of Encryption & Quantum-Safe Techniques," Skills Network. [Online]. Available: [https://catalog.skills.network/catalog_item/2766/](https://catalog.skills.network/catalog_item/2766/) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index f596b0bcd2e..43927f918fe 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -125,6 +125,7 @@ const FETCH_RETURNS_405 = [ // Fetching these links throws an error, but they work in-browser. Not sure why. const FETCH_FAILS = [ + "https://sphincs.org/", "https://www.cs.tau.ac.il/~nogaa/PDFS/r.pdf", "https://www.cs.tau.ac.il/\\~nogaa/PDFS/r.pdf", // Escaped version from markdown "https://www.epo.org/en/news-events/press-centre/press-release/2025/1361562", From c58d472bbf211a75220afc5a408c9a3bc0aee121 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Wed, 15 Apr 2026 09:37:04 -0400 Subject: [PATCH 09/14] Update ignores.ts --- scripts/js/lib/links/ignores.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index 43927f918fe..2b94b5951d3 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -152,7 +152,6 @@ const ALWAYS_IGNORED_URLS__EXPECTED = [ // Markdown reference definitions with escaped characters that are extracted as links "https://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/#controlled-random-search-crs-with-local-mutation", "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin\\_l\\_bfgs\\_b.html", - "https://qiskit.org/ecosystem/aer/apidocs/aer\\_library.html", "https://github.com/Qiskit/openqasm3\\_parser", "https://csrc.nist.gov/pubs/fips/205/ipd", "https://doi.org/10.6028/jres.104.027", From 5f452a1035a22d73f76a59e9fad3ab77f61675dc Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Wed, 15 Apr 2026 15:52:17 -0400 Subject: [PATCH 10/14] bob's latest fixes --- scripts/js/lib/links/ExternalLink.test.ts | 10 +++++----- scripts/js/lib/links/ExternalLink.ts | 10 ++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts/js/lib/links/ExternalLink.test.ts b/scripts/js/lib/links/ExternalLink.test.ts index a03ad623793..0239cd9a3b4 100644 --- a/scripts/js/lib/links/ExternalLink.test.ts +++ b/scripts/js/lib/links/ExternalLink.test.ts @@ -16,7 +16,7 @@ import { ExternalLink } from "./ExternalLink.js"; test("ExternalLink constructor ignores anchors", () => { const link = new ExternalLink("https://ibm.com#my-anchor", []); - expect(link.value).toEqual("https://ibm.com"); + expect(link.value).toEqual("https://ibm.com/"); }); test.describe("ExternalLink.check()", () => { @@ -42,7 +42,7 @@ test.describe("ExternalLink.check()", () => { let link = new ExternalLink("https://bad-link.com", ["/testorigin.mdx"]); const result = await link.check(); expect(result).toEqual( - "❌ Could not find link 'https://bad-link.com' (404). Appears in:\n /testorigin.mdx", + "❌ Could not find link 'https://bad-link.com/' (404). Appears in:\n /testorigin.mdx", ); }); @@ -51,7 +51,7 @@ test.describe("ExternalLink.check()", () => { let link = new ExternalLink("https://bad-link.com", ["/testorigin.mdx"]); const result = await link.check(); expect(result).toEqual( - "❌ Link 'https://bad-link.com' has been removed (410). Appears in:\n /testorigin.mdx", + "❌ Link 'https://bad-link.com/' has been removed (410). Appears in:\n /testorigin.mdx", ); }); @@ -60,7 +60,7 @@ test.describe("ExternalLink.check()", () => { let link = new ExternalLink("https://bad-link.com", ["/testorigin.mdx"]); const result = await link.check(); expect(result).toEqual( - "❌ Link 'https://bad-link.com' returned unexpected code: 502. Appears in:\n /testorigin.mdx", + "❌ Link 'https://bad-link.com/' returned unexpected code: 502. Appears in:\n /testorigin.mdx", ); }); @@ -69,7 +69,7 @@ test.describe("ExternalLink.check()", () => { let link = new ExternalLink("https://bad-link.com", ["/testorigin.mdx"]); const result = await link.check(); expect(result).toEqual( - "❌ Failed to fetch 'https://bad-link.com': some issue. Appears in:\n /testorigin.mdx", + "❌ Failed to fetch 'https://bad-link.com/': some issue. Appears in:\n /testorigin.mdx", ); }); diff --git a/scripts/js/lib/links/ExternalLink.ts b/scripts/js/lib/links/ExternalLink.ts index 28660a2c404..fce42ed5161 100644 --- a/scripts/js/lib/links/ExternalLink.ts +++ b/scripts/js/lib/links/ExternalLink.ts @@ -25,8 +25,14 @@ export class ExternalLink { ); } - // We strip anchors. - this.value = linkString.split("#", 2)[0]; + // Normalize URL to handle escape characters, then strip anchors + try { + const normalized = new URL(linkString).href; + this.value = normalized.split("#", 2)[0]; + } catch { + // If URL parsing fails, fall back to original behavior + this.value = linkString.split("#", 2)[0]; + } this.originFiles = new Set(originFiles); } From fb7f5295a060fa28a047e488e11aa2e972c52233 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Wed, 15 Apr 2026 16:23:52 -0400 Subject: [PATCH 11/14] bob tries again --- scripts/js/lib/links/ignores.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index 2b94b5951d3..122363e7af9 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -134,6 +134,7 @@ const FETCH_FAILS = [ "https://www.mckinsey.com/capabilities/mckinsey-technology/our-insights/solving-chemistrys-toughest-problems-the-quantum-computing-advantage", "https://www.mckinsey.com/industries/chemicals/our-insights/the-next-big-thing-quantum-computings-potential-impact-on-chemicals?cid=eml-web", "https://www.mckinsey.com/capabilities/tech-and-ai/our-insights/the-year-of-quantum-from-concept-to-reality-in-2025", + "https://thequantuminsider.com/2024/10/12/ibm-quantum-roadmap-guide-scaling-and-expanding-the-usefulness-of-quantum-computing/", // The following link is only accessible through IBM VPN "https://w3.ibm.com/w3publisher/w3-privacy-notice", ]; @@ -150,7 +151,9 @@ const ALWAYS_IGNORED_URLS__EXPECTED = [ "https://csrc.nist.gov/news/2023/three-draft-fips-for-post-quantum-cryptography", // Markdown reference definitions with escaped characters that are extracted as links + "http://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/", "https://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/#controlled-random-search-crs-with-local-mutation", + "https://qiskit.org/ecosystem/aer/apidocs/aer\\_library.html", "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin\\_l\\_bfgs\\_b.html", "https://github.com/Qiskit/openqasm3\\_parser", "https://csrc.nist.gov/pubs/fips/205/ipd", From 69d738e4f4830be41a66612fd79429078191964a Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Wed, 15 Apr 2026 16:30:06 -0400 Subject: [PATCH 12/14] new Bob help --- scripts/js/lib/links/ignores.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index 122363e7af9..dfc170030e8 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -151,9 +151,13 @@ const ALWAYS_IGNORED_URLS__EXPECTED = [ "https://csrc.nist.gov/news/2023/three-draft-fips-for-post-quantum-cryptography", // Markdown reference definitions with escaped characters that are extracted as links + // Note: Some URLs are extracted with /_ instead of \_ depending on the markdown parser "http://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/", + "http://nlopt.readthedocs.io/en/latest/NLopt/_Algorithms/", "https://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/#controlled-random-search-crs-with-local-mutation", "https://qiskit.org/ecosystem/aer/apidocs/aer\\_library.html", + "https://qiskit.org/ecosystem/aer/apidocs/aer/_library.html", + "https://optuna.readthedocs.io/en/stable/tutorial/20/_recipes/009/_ask/_and/_tell.html", "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin\\_l\\_bfgs\\_b.html", "https://github.com/Qiskit/openqasm3\\_parser", "https://csrc.nist.gov/pubs/fips/205/ipd", From a979b6c2e49ac57eed62e593fe03469813722bc7 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Wed, 15 Apr 2026 18:39:03 -0400 Subject: [PATCH 13/14] Update ignores.ts --- scripts/js/lib/links/ignores.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index dfc170030e8..d957d2dfd03 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -161,6 +161,7 @@ const ALWAYS_IGNORED_URLS__EXPECTED = [ "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin\\_l\\_bfgs\\_b.html", "https://github.com/Qiskit/openqasm3\\_parser", "https://csrc.nist.gov/pubs/fips/205/ipd", + "https://csrc.nist.gov/pubs/fips/203/ipd", "https://doi.org/10.6028/jres.104.027", "https://eprint.iacr.org/2012/090", "https://finance.yahoo.com/quote/8801.T", From 48ca3b289f3ba27981eb7dd29a074c571bdff0f9 Mon Sep 17 00:00:00 2001 From: ABBY CROSS Date: Wed, 15 Apr 2026 22:13:58 -0400 Subject: [PATCH 14/14] npm run fmt --- scripts/js/lib/links/ExternalLink.ts | 7 ++++--- scripts/js/lib/links/ignores.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/js/lib/links/ExternalLink.ts b/scripts/js/lib/links/ExternalLink.ts index fce42ed5161..6dfb70a442c 100644 --- a/scripts/js/lib/links/ExternalLink.ts +++ b/scripts/js/lib/links/ExternalLink.ts @@ -72,7 +72,7 @@ async function safeFetch( try { // Normalize URL to handle encoding issues const normalizedUrl = new URL(link).href; - + const response = await fetch(normalizedUrl, { headers: getHeaders(link), method: method, @@ -91,11 +91,12 @@ function responseToErrorMessage( response: Response, ): string | undefined { const httpCode = response.status; - + // Accept 1xx-3xx as valid (including redirects) // Accept 403 as potentially valid (resource exists but access is forbidden) // Accept 429 as valid (resource exists but we're being rate limited) - const isOk = (httpCode >= 100 && httpCode < 400) || httpCode === 403 || httpCode === 429; + const isOk = + (httpCode >= 100 && httpCode < 400) || httpCode === 403 || httpCode === 429; if (isOk) return undefined; if (httpCode === 404) return `Could not find link '${link}' (${httpCode})`; diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index d957d2dfd03..862c81c69ea 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -149,7 +149,7 @@ const ALWAYS_IGNORED_URLS__EXPECTED = [ // the same status code. Sometimes it's 404, sometimes 503, sometimes 200 etc. // They do work whenever I've tested them in a browser. "https://csrc.nist.gov/news/2023/three-draft-fips-for-post-quantum-cryptography", - + // Markdown reference definitions with escaped characters that are extracted as links // Note: Some URLs are extracted with /_ instead of \_ depending on the markdown parser "http://nlopt.readthedocs.io/en/latest/NLopt\\_Algorithms/",