diff --git a/revdep/.gitignore b/revdep/.gitignore index a2b0d9bb972..b06d4d82019 100644 --- a/revdep/.gitignore +++ b/revdep/.gitignore @@ -1,3 +1,4 @@ /cloud.noindex /cloud /review/ +/notifications/ diff --git a/revdep/NOTIFY-README.md b/revdep/NOTIFY-README.md new file mode 100644 index 00000000000..d1ba1850f58 --- /dev/null +++ b/revdep/NOTIFY-README.md @@ -0,0 +1,122 @@ +# Maintainer Notification Script + +This script (`notify-maintainers.sh`) automates the process of notifying package maintainers about reverse dependency issues discovered during igraph development. + +## Features + +- **GitHub Integration**: Automatically creates GitHub issues for packages hosted on GitHub +- **Email Fallback**: Generates email drafts (with pre-filled `To:` and `Cc:` fields) for packages not on GitHub, or optionally for all packages after filing a GitHub issue +- **Issue log**: Records all filed GitHub issue URLs for easy follow-up +- **Template-based**: Creates well-formatted issue descriptions with all relevant information + +## Prerequisites + +### For GitHub Issues (Optional) + +```bash +# Install GitHub CLI +# On macOS: +brew install gh + +# On Linux: +# See https://github.com/cli/cli#installation + +# Authenticate with GitHub +gh auth login +``` + +Note: Issues are created on behalf of the authenticated GitHub user. Ensure you are logged in with appropriate credentials before running the script. + +### For Email Drafts + +No additional setup required – the script automatically fetches maintainer emails from CRAN and pre-fills the `To:` field in each draft. + +## Usage + +```bash +./notify-maintainers.sh +``` + +The script will: + +1. Check if `gh` CLI is available +2. For each affected package: + - Look up the maintainer email from the package DESCRIPTION field stored in CRAN + - Determine whether the package repository on GitHub is accessible + - If accessible: Create a GitHub issue directly using `gh issue create` and log the issue URL + - If not accessible: Create an email draft in `notifications/` with the maintainer email pre-filled + +The script determines upfront which action to take for each package (either GitHub issue OR email draft, not both). Regardless of which path is taken, issue links are always stored in `notifications/issue-log.txt` for follow-up tracking. + +## Output + +Files are created in the `notifications/` directory: + +- `issue-log.txt` – URLs of all GitHub issues created (for follow-up) +- `{Package}-email.txt` – Complete email draft **only for packages that require email drafts** (GitHub not accessible) + +For packages with accessible GitHub repositories, issues are created directly; no local files other than the log entry are saved. + +## Manual Steps + +### If GitHub Issues Fail + +If you see authentication or permission errors when creating GitHub issues: + +1. Check GitHub authentication: `gh auth status` +2. Authenticate if needed: `gh auth login` +3. Or manually create issues via the GitHub web interface + +### For Email Drafts + +When GitHub repositories are not accessible, email drafts are automatically generated with: + +- `To:` pre-filled with the package maintainer's CRAN email address +- `Cc:` pre-filled with the igraph development team address + +To send these emails: + +1. Review the email content in `notifications/{Package}-email.txt` +2. Open your email client and compose a new message +3. Copy the `To:`, `Cc:`, `Subject:`, and body content + +## Package Information + +The script's package list and issue templates need to be updated each time a new set of revdep issues is identified. Package information (GitHub URL, maintainer email) is sourced from the CRAN package metadata (`DESCRIPTION` fields as reported by CRAN). + +## Customization + +To modify the issue templates or add packages to the notification list, edit the script directly. Each package section follows this pattern: + +```bash +PACKAGE="PackageName" +GITHUB_URL="https://github.com/owner/repo" +MAINTAINER_EMAIL="maintainer@example.com" # from CRAN DESCRIPTION + +ISSUE_TITLE="Short description of the issue" +ISSUE_BODY="..." +EMAIL_SUBJECT="..." + +notify_package "$PACKAGE" "$GITHUB_URL" "$MAINTAINER_EMAIL" "$ISSUE_TITLE" "$ISSUE_BODY" "$EMAIL_SUBJECT" +``` + +## Troubleshooting + +### "gh CLI not found" +Install the GitHub CLI as described in Prerequisites. + +### "Failed to create issue" +- Check GitHub authentication: `gh auth status` +- Ensure you have permission to create issues in the repository +- The repository might be private + +### "GitHub repository not accessible" +- The repository might be private +- There might be no link to the repository in the package metadata +- Use the email draft fallback instead + +## See Also + +- [problems-analysis.md](problems-analysis.md) - Detailed analysis of each issue +- [examples/](examples/) - Runnable reproduction scripts +- [cran-response-draft.md](cran-response-draft.md) - Draft response to CRAN review email diff --git a/revdep/cran-response-draft.md b/revdep/cran-response-draft.md new file mode 100644 index 00000000000..5b95d2237f4 --- /dev/null +++ b/revdep/cran-response-draft.md @@ -0,0 +1,43 @@ +# Draft response to CRAN teams' auto-check for igraph 2.3.0 + +**Subject**: Re: CRAN pre-release check results for igraph 2.3.0 – reverse dependency issues + +--- + +Dear CRAN team, + +Thank you for running the pre-release checks. We have reviewed all 7 flagged packages and reached +out to the maintainers of all affected packages on 2026-02-20. + +- **Cascade**: Namespace collision — the new `circulant()` export in 2.3.0 conflicts with + `magic::circulant`. Non-breaking warning only. No GitHub issue filed; maintainer notified + by email. + +- **DiagrammeR**: Bug in DiagrammeR — `get_leverage_centrality()` passes a vector to + `neighbors()`, which now requires a single vertex. Issue filed 2026-02-20: + https://github.com/rich-iannone/DiagrammeR/issues/538 + +- **jewel**: Bug in jewel — non-integer `niter` value (`p * 0.05`) is passed to `rewire()`, + which now strictly validates integer arguments. Issue filed 2026-02-20: + https://github.com/annaplaksienko/jewel/issues/1 + +- **rSpectral**: Behavior change in igraph — `modularity()` now auto-uses the `"weight"` edge + attribute when present, changing test results. Issue filed 2026-02-20 (resolved 2026-03-05): + https://github.com/cmclean5/rSpectral/issues/1 + +- **rgph**: **False positive / unrelated** — the failure is caused by a Java configuration + problem on the check machine, not by igraph changes. igraph is only in `Suggests` for this + package and no igraph-related errors appear in the log. No action needed from igraph's side. + +- **sfnetworks**: Bug in sfnetworks — `all_shortest_paths(from = ...)` now requires a single + vertex; sfnetworks passes a vector. Issue filed 2026-02-20: + https://github.com/luukvdmeer/sfnetworks/issues/292 + +- **tmap.networks**: Cascading failure — imports sfnetworks directly and fails as a consequence. + Will be resolved once sfnetworks is updated. Maintainer notified via sfnetworks issue. + +All fixes are straightforward and the maintainers were notified well in advance. We are happy to +provide further details on any individual case. + +Best regards, +The igraph Development Team diff --git a/revdep/examples/README.md b/revdep/examples/README.md new file mode 100644 index 00000000000..a9349d7e1c3 --- /dev/null +++ b/revdep/examples/README.md @@ -0,0 +1,53 @@ +# Reverse Dependency Problem Examples + +This directory contains minimal reproducible examples for packages that have newly broken checks compared to the most recent CRAN version of igraph. + +## Files + +Each issue has two files: +- `*.R` - Runnable R script with the minimal example +- `*.md` - Markdown documentation with example output (reprex-style) + +### Issues + +1. **cascade-circulant-issue** - Namespace collision between `igraph::circulant` and `magic::circulant` +2. **diagrammer-neighbors-issue** - `neighbors()` now requires exactly one vertex +3. **jewel-integer-issue** - Strict integer validation in `rewire_impl()` +4. **manynet-scalar-issue** - Scalar integer validation in `sample_last_cit()` +5. **rspectral-modularity-issue** - Automatic weight usage in modularity calculations +6. **sfnetworks-from-issue** - `from` parameter must specify exactly one vertex + +## Running the Examples + +Each R script can be run with: + +```r +source("revdep/examples/cascade-circulant-issue.R") +``` + +Or from the command line: + +```bash +Rscript revdep/examples/cascade-circulant-issue.R +``` + +## Format + +The examples follow a simplified format: +- No `cat()` statements for output (comments instead) +- No `tryCatch()` blocks (commented out error cases) +- Clean, runnable code that can be used with `reprex::reprex()` +- Corresponding `.md` files show the expected output + +## Summary of Issues + +| Package | Issue | Severity | Type | +|---------|-------|----------|------| +| Cascade | Namespace collision warning | Low | Inadvertent behavior change | +| DiagrammeR | `neighbors()` requires single vertex | High | API tightening | +| jewel | Integer validation error | High | Uncovered downstream bug | +| manynet | Scalar integer validation | High | API tightening | +| rSpectral | Modularity test failures | Medium | Behavior change with workaround | +| sfnetworks | `from` requires single vertex | High | API tightening | + +See `../problems-analysis.md` for detailed analysis and recommendations. diff --git a/revdep/examples/cascade-circulant-issue.R b/revdep/examples/cascade-circulant-issue.R new file mode 100644 index 00000000000..d38054735cd --- /dev/null +++ b/revdep/examples/cascade-circulant-issue.R @@ -0,0 +1,24 @@ +# Cascade namespace collision issue +# Issue: Warning when loading both igraph and magic packages + +library(igraph) + +# igraph now exports circulant() as a constructor alias +"circulant" %in% ls("package:igraph") + +# The preferred way is to use make_circulant() directly +g <- make_circulant(10, c(1, 3)) +vcount(g) +ecount(g) + +# Root cause: +# - igraph added make_circulant() and constructor alias circulant() in v2.2.1.9003 +# - magic package also has a circulant() function for creating circulant matrices +# - When both packages are loaded, there's a namespace collision +# - This produces: Warning: replacing previous import 'igraph::circulant' by 'magic::circulant' + +# Assessment: +# - This is an inadvertent behavior change in igraph +# - The circulant() function is primarily a constructor alias +# - Users should use make_circulant() directly +# - Cascade package can resolve by explicitly importing magic::circulant in NAMESPACE diff --git a/revdep/examples/cascade-circulant-issue.md b/revdep/examples/cascade-circulant-issue.md new file mode 100644 index 00000000000..82bc9c10175 --- /dev/null +++ b/revdep/examples/cascade-circulant-issue.md @@ -0,0 +1,33 @@ +# Cascade namespace collision issue + +## Issue +Warning when loading both igraph and magic packages + +## Reproducible Example + +```r +library(igraph) + +# igraph now exports circulant() as a constructor alias +"circulant" %in% ls("package:igraph") +#> [1] TRUE + +# The preferred way is to use make_circulant() directly +g <- make_circulant(10, c(1, 3)) +vcount(g) +#> [1] 10 +ecount(g) +#> [1] 20 +``` + +## Root Cause +- igraph added `make_circulant()` and constructor alias `circulant()` in v2.2.1.9003 +- magic package also has a `circulant()` function for creating circulant matrices +- When both packages are loaded, there's a namespace collision +- This produces: `Warning: replacing previous import 'igraph::circulant' by 'magic::circulant'` + +## Assessment +- This is an inadvertent behavior change in igraph +- The `circulant()` function is primarily a constructor alias +- Users should use `make_circulant()` directly +- Cascade package can resolve by explicitly importing `magic::circulant` in NAMESPACE diff --git a/revdep/examples/cascade-issue-draft.md b/revdep/examples/cascade-issue-draft.md new file mode 100644 index 00000000000..05b17f2bf97 --- /dev/null +++ b/revdep/examples/cascade-issue-draft.md @@ -0,0 +1,34 @@ +# Title + +Namespace collision warning when loading Cascade with igraph ≥ 2.3.0 + +# Body + +When loading **Cascade** alongside **igraph** 2.3.0 or later the following warning appears: + +``` +Warning: replacing previous import 'igraph::circulant' by 'magic::circulant' when loading 'Cascade' +``` + +This is a **non-breaking warning** — Cascade continues to work correctly, but the warning may alarm users and will surface in `R CMD check`. + +## Root cause + +igraph 2.3.0 added `make_circulant()` and a constructor alias `circulant()`. +Because **magic** also exports `circulant()`, R detects a conflict between the two imports when Cascade is loaded. + +## Suggested fix + +Explicitly import `magic::circulant` in your NAMESPACE so that R resolves the conflict deterministically (and silently): + +```r +# In NAMESPACE, or equivalently in a roxygen2 tag somewhere in the package: +importFrom(magic, circulant) +``` + +That single line removes the warning entirely. + +--- + +*This issue was identified during reverse-dependency checks for the upcoming igraph 2.3.0 release. +See the igraph repository for full analysis: https://github.com/igraph/rigraph* diff --git a/revdep/examples/diagrammer-neighbors-issue.R b/revdep/examples/diagrammer-neighbors-issue.R new file mode 100644 index 00000000000..0f758d846bb --- /dev/null +++ b/revdep/examples/diagrammer-neighbors-issue.R @@ -0,0 +1,36 @@ +# DiagrammeR neighbors() issue +# Issue: neighbors() now requires exactly one vertex + +library(igraph) + +# Create a simple graph +g <- make_ring(5) + +# This works - single vertex +neighbors(g, 1) + +# This fails - multiple vertices +# neighbors(g, c(1, 2)) +# Error: `vid` must specify exactly one vertex + +# This also fails - passing a non-scalar +# degree_vals <- degree(g) +# neighbors(g, degree_vals) +# Error: `vid` must specify exactly one vertex + +# Root cause: +# - igraph added stricter validation requiring exactly one vertex for neighbors() +# - DiagrammeR's get_leverage_centrality() passes degree_vals (a vector) to neighbors() +# - The code: mean(degree_vals[igraph::neighbors(ig_graph, degree_vals)]) +# - This previously may have worked with implicit vectorization or used first element + +# Assessment: +# - This is an intentional API tightening in igraph for safety +# - DiagrammeR needs to update to iterate over vertices individually +# - The fix should loop: lapply(seq_along(degree_vals), function(i) neighbors(g, i)) + +# Recommendation for DiagrammeR: +# Change from: +# neighbors(ig_graph, degree_vals) +# To: +# lapply(seq_along(degree_vals), function(i) neighbors(ig_graph, i)) diff --git a/revdep/examples/diagrammer-neighbors-issue.md b/revdep/examples/diagrammer-neighbors-issue.md new file mode 100644 index 00000000000..cb71fd96977 --- /dev/null +++ b/revdep/examples/diagrammer-neighbors-issue.md @@ -0,0 +1,48 @@ +# DiagrammeR neighbors() issue + +## Issue +`neighbors()` now requires exactly one vertex + +## Reproducible Example + +```r +library(igraph) + +# Create a simple graph +g <- make_ring(5) + +# This works - single vertex +neighbors(g, 1) +#> + 2/5 vertices, from 7bb1be6: +#> [1] 5 2 + +# This fails - multiple vertices +neighbors(g, c(1, 2)) +#> Error: `vid` must specify exactly one vertex + +# This also fails - passing a vector +degree_vals <- degree(g) +neighbors(g, degree_vals) +#> Error: `vid` must specify exactly one vertex +``` + +## Root Cause +- igraph added stricter validation requiring exactly one vertex for `neighbors()` +- DiagrammeR's `get_leverage_centrality()` passes `degree_vals` (a vector) to `neighbors()` +- The code: `mean(degree_vals[igraph::neighbors(ig_graph, degree_vals)])` +- This previously may have worked with implicit vectorization or used first element + +## Assessment +- This is an intentional API tightening in igraph for safety +- DiagrammeR needs to update to iterate over vertices individually +- The fix should loop: `lapply(seq_along(degree_vals), function(i) neighbors(g, i))` + +## Recommendation for DiagrammeR +Change from: +```r +neighbors(ig_graph, degree_vals) +``` +To: +```r +lapply(seq_along(degree_vals), function(i) neighbors(ig_graph, i)) +``` diff --git a/revdep/examples/jewel-integer-issue.R b/revdep/examples/jewel-integer-issue.R new file mode 100644 index 00000000000..16855eaf5d7 --- /dev/null +++ b/revdep/examples/jewel-integer-issue.R @@ -0,0 +1,43 @@ +# jewel integer validation issue +# Issue: rewire_impl now strictly validates that n is an integer + +library(igraph) + +# Create a simple graph for testing +g <- make_ring(10) + +# This works - integer value +result1 <- rewire(g, keeping_degseq(niter = 100)) +vcount(result1) +ecount(result1) + +# This fails - non-integer value +# rewire(g, keeping_degseq(niter = 2.45)) +# Error: The value 2.4500000000000002 is not representable as an integer + +# Simulating jewel package scenario +p <- 49 +niter <- p * 0.05 # = 2.45 + +# This also fails +# rewire(g, keeping_degseq(niter = niter)) +# Error: The value 2.4500000000000002 is not representable as an integer + +# Workaround using as.integer() +result2 <- rewire(g, keeping_degseq(niter = as.integer(niter))) +vcount(result2) + +# Root cause: +# - rewire_impl() converts n with as.numeric(), preserving fractional parts +# - C code in rinterface_extra.c now strictly validates integer values +# - Previously may have silently truncated, now explicitly rejects + +# Assessment: +# - This uncovered a bug in the jewel package +# - niter should logically be an integer (number of iterations) +# - jewel should use ceiling(), floor(), or round() on computed niter + +# Recommendation: +# - Fix in jewel: niter <- ceiling(p * 0.05) +# - OR fix in igraph for backward compatibility: +# Add as.integer() in rewire_keeping_degseq() before calling rewire_impl() diff --git a/revdep/examples/jewel-integer-issue.md b/revdep/examples/jewel-integer-issue.md new file mode 100644 index 00000000000..5e3db7cc9ea --- /dev/null +++ b/revdep/examples/jewel-integer-issue.md @@ -0,0 +1,51 @@ +# jewel integer validation issue + +## Issue +`rewire_impl` now strictly validates that n is an integer + +## Reproducible Example + +```r +library(igraph) + +# Create a simple graph for testing +g <- make_ring(10) + +# This works - integer value +result1 <- rewire(g, keeping_degseq(niter = 100)) +vcount(result1) +#> [1] 10 +ecount(result1) +#> [1] 10 + +# This fails - non-integer value +rewire(g, keeping_degseq(niter = 2.45)) +#> Error: The value 2.4500000000000002 is not representable as an integer + +# Simulating jewel package scenario +p <- 49 +niter <- p * 0.05 # = 2.45 + +# This also fails +rewire(g, keeping_degseq(niter = niter)) +#> Error: The value 2.4500000000000002 is not representable as an integer + +# Workaround using as.integer() +result2 <- rewire(g, keeping_degseq(niter = as.integer(niter))) +vcount(result2) +#> [1] 10 +``` + +## Root Cause +- `rewire_impl()` converts n with `as.numeric()`, preserving fractional parts +- C code in `rinterface_extra.c` now strictly validates integer values +- Previously may have silently truncated, now explicitly rejects + +## Assessment +- This uncovered a bug in the jewel package +- `niter` should logically be an integer (number of iterations) +- jewel should use `ceiling()`, `floor()`, or `round()` on computed niter + +## Recommendation +- Fix in jewel: `niter <- ceiling(p * 0.05)` +- OR fix in igraph for backward compatibility: Add `as.integer()` in `rewire_keeping_degseq()` before calling `rewire_impl()` diff --git a/revdep/examples/manynet-scalar-issue.R b/revdep/examples/manynet-scalar-issue.R new file mode 100644 index 00000000000..3c190d47bfe --- /dev/null +++ b/revdep/examples/manynet-scalar-issue.R @@ -0,0 +1,29 @@ +# manynet scalar integer issue +# Issue: lastcit_game_impl expects scalar integer but receives vector + +library(igraph) + +# This works - scalar values +g1 <- sample_last_cit(n = 10, edges = 5, agebins = 10, directed = TRUE) +vcount(g1) +ecount(g1) + +# This fails - vector for edges parameter +# edges_vec <- c(5, 10) +# g2 <- sample_last_cit(n = 10, edges = edges_vec, agebins = 10, directed = TRUE) +# Error: Expecting a scalar integer but received a vector of length 2 + +# Root cause: +# - igraph added stricter validation for scalar parameters +# - C code in rinterface_extra.c now validates that scalar parameters are indeed scalars +# - manynet's generate_citations() may be passing a vector where scalar is expected + +# Assessment: +# - This is stricter type checking in igraph +# - manynet needs to ensure it passes scalar values to sample_last_cit() +# - The error message clearly indicates the parameter should be scalar + +# Recommendation for manynet: +# - Check generate_citations() implementation +# - Ensure 'edges' parameter is scalar: edges <- edges[1] or similar +# - Or iterate if multiple values are intended: lapply(edges_vec, function(e) sample_last_cit(...)) diff --git a/revdep/examples/manynet-scalar-issue.md b/revdep/examples/manynet-scalar-issue.md new file mode 100644 index 00000000000..2692b34e3e7 --- /dev/null +++ b/revdep/examples/manynet-scalar-issue.md @@ -0,0 +1,37 @@ +# manynet scalar integer issue + +## Issue +`lastcit_game_impl` expects scalar integer but receives vector + +## Reproducible Example + +```r +library(igraph) + +# This works - scalar values +g1 <- sample_last_cit(n = 10, edges = 5, agebins = 10, directed = TRUE) +vcount(g1) +#> [1] 10 +ecount(g1) +#> [1] 50 + +# This fails - vector for edges parameter +edges_vec <- c(5, 10) +g2 <- sample_last_cit(n = 10, edges = edges_vec, agebins = 10, directed = TRUE) +#> Error: Expecting a scalar integer but received a vector of length 2 +``` + +## Root Cause +- igraph added stricter validation for scalar parameters +- C code in `rinterface_extra.c` now validates that scalar parameters are indeed scalars +- manynet's `generate_citations()` may be passing a vector where scalar is expected + +## Assessment +- This is stricter type checking in igraph +- manynet needs to ensure it passes scalar values to `sample_last_cit()` +- The error message clearly indicates the parameter should be scalar + +## Recommendation for manynet +- Check `generate_citations()` implementation +- Ensure 'edges' parameter is scalar: `edges <- edges[1]` or similar +- Or iterate if multiple values are intended: `lapply(edges_vec, function(e) sample_last_cit(...))` diff --git a/revdep/examples/rspectral-modularity-issue.R b/revdep/examples/rspectral-modularity-issue.R new file mode 100644 index 00000000000..e0ae10ff12c --- /dev/null +++ b/revdep/examples/rspectral-modularity-issue.R @@ -0,0 +1,56 @@ +# rSpectral modularity calculation issue +# Issue: Modularity values have changed due to automatic weight detection + +library(igraph) + +# Create a test graph +g <- make_full_graph(5) + make_full_graph(5) + make_full_graph(5) +membership <- c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3) + +# Test 1: Modularity without weights +mod1 <- modularity(g, membership, weights = NULL) +mod1 + +# Test 2: Modularity with default (may use weights if present) +mod2 <- modularity(g, membership) +mod2 + +# Add weights to demonstrate the issue +E(g)$weight <- 1.0 +mod3 <- modularity(g, membership) +mod3 + +# Different weights +set.seed(42) +E(g)$weight <- runif(ecount(g)) +mod4 <- modularity(g, membership) +mod4 + +# Test: weights = NULL doesn't disable auto-detection! +mod5 <- modularity(g, membership, weights = NULL) +mod5 # Same as mod4, not mod1! + +# WORKAROUND: Using weights = numeric() to disable auto-detection +mod6 <- modularity(g, membership, weights = numeric()) +mod6 # Matches mod1! + +# Root cause: +# - igraph v2.2.1.9004 added: 'Use "weights" edge attribute in modularity() if available' +# - modularity() now automatically uses edge weights if present +# - weights = NULL doesn't disable this auto-detection +# - numeric() is not NULL (skips auto-detection), but !all(is.na(numeric())) is FALSE, +# so weights gets set to NULL internally + +# Assessment: +# - This is an inadvertent behavior change in igraph +# - Modularity differences are small but significant for exact tests +# - Expected: 0.408, Actual: 0.432 (difference: +0.024) +# - Expected: 0.3776, Actual: 0.3758 (difference: -0.0018) + +# Recommendation for rSpectral: +# 1. Update saved graph objects using upgrade_graph() +# 2. Review whether graphs should have weights or not +# 3. WORKAROUND: Use weights = numeric() to get unweighted modularity +# Example: modularity(g, membership, weights = numeric()) +# 4. Or remove unintended weights: g <- delete_edge_attr(g, 'weight') +# 5. Update expected test values if the new weighted modularity is correct diff --git a/revdep/examples/rspectral-modularity-issue.md b/revdep/examples/rspectral-modularity-issue.md new file mode 100644 index 00000000000..ca934b0836f --- /dev/null +++ b/revdep/examples/rspectral-modularity-issue.md @@ -0,0 +1,55 @@ +# rSpectral modularity calculation issue + +## Issue +Modularity values have changed due to automatic weight detection + +## Reproducible Example + +```r +library(igraph) + +# Create a test graph +g <- make_full_graph(5) + make_full_graph(5) + make_full_graph(5) +membership <- c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3) + +# Test 1: Modularity without weights +mod1 <- modularity(g, membership, weights = NULL) +mod1 +#> [1] 0.6666667 + +# Add weights to demonstrate the issue +set.seed(42) +E(g)$weight <- runif(ecount(g)) +mod2 <- modularity(g, membership) +mod2 +#> [1] 0.6663506 + +# Test: weights = NULL doesn't disable auto-detection! +mod3 <- modularity(g, membership, weights = NULL) +mod3 # Same as mod2, not mod1! +#> [1] 0.6663506 + +# WORKAROUND: Using weights = numeric() to disable auto-detection +mod4 <- modularity(g, membership, weights = numeric()) +mod4 # Matches mod1! +#> [1] 0.6666667 +``` + +## Root Cause +- igraph v2.2.1.9004 added: 'Use "weights" edge attribute in modularity() if available' +- `modularity()` now automatically uses edge weights if present +- `weights = NULL` doesn't disable this auto-detection +- `numeric()` is not NULL (skips auto-detection), but `!all(is.na(numeric()))` is FALSE, so weights gets set to NULL internally + +## Assessment +- This is an inadvertent behavior change in igraph +- Modularity differences are small but significant for exact tests +- Expected: 0.408, Actual: 0.432 (difference: +0.024) +- Expected: 0.3776, Actual: 0.3758 (difference: -0.0018) + +## Recommendation for rSpectral +1. Update saved graph objects using `upgrade_graph()` +2. Review whether graphs should have weights or not +3. WORKAROUND: Use `weights = numeric()` to get unweighted modularity +4. Or remove unintended weights: `g <- delete_edge_attr(g, 'weight')` +5. Update expected test values if the new weighted modularity is correct diff --git a/revdep/examples/sfnetworks-from-issue.R b/revdep/examples/sfnetworks-from-issue.R new file mode 100644 index 00000000000..6b2cb1e707f --- /dev/null +++ b/revdep/examples/sfnetworks-from-issue.R @@ -0,0 +1,30 @@ +# sfnetworks all_shortest_paths() issue +# Issue: from parameter must specify exactly one vertex + +library(igraph) + +# Create a simple graph +g <- make_ring(5) + +# This works - single vertex +paths1 <- all_shortest_paths(g, from = 1, to = 3) +length(paths1$res) + +# This fails - multiple vertices in 'from' +# paths2 <- all_shortest_paths(g, from = c(1, 2), to = 3) +# Error: `from` must specify exactly one vertex + +# Root cause: +# - igraph added stricter validation requiring exactly one vertex for 'from' parameter +# - sfnetworks passes multiple vertices to all_shortest_paths() +# - Previously may have used only the first vertex implicitly + +# Assessment: +# - This is an intentional API tightening in igraph for safety and clarity +# - sfnetworks needs to handle multiple 'from' vertices explicitly +# - The function should iterate or be clear about using only the first vertex + +# Recommendation for sfnetworks: +# - If only first vertex intended: from <- from[1] +# - If all vertices intended: lapply(from, function(f) all_shortest_paths(g, from = f, to = to)) +# - Or provide clear warning/error about multiple vertices diff --git a/revdep/examples/sfnetworks-from-issue.md b/revdep/examples/sfnetworks-from-issue.md new file mode 100644 index 00000000000..d1fa038f1a5 --- /dev/null +++ b/revdep/examples/sfnetworks-from-issue.md @@ -0,0 +1,37 @@ +# sfnetworks all_shortest_paths() issue + +## Issue +`from` parameter must specify exactly one vertex + +## Reproducible Example + +```r +library(igraph) + +# Create a simple graph +g <- make_ring(5) + +# This works - single vertex +paths1 <- all_shortest_paths(g, from = 1, to = 3) +length(paths1$res) +#> [1] 2 + +# This fails - multiple vertices in 'from' +paths2 <- all_shortest_paths(g, from = c(1, 2), to = 3) +#> Error: `from` must specify exactly one vertex +``` + +## Root Cause +- igraph added stricter validation requiring exactly one vertex for 'from' parameter +- sfnetworks passes multiple vertices to `all_shortest_paths()` +- Previously may have used only the first vertex implicitly + +## Assessment +- This is an intentional API tightening in igraph for safety and clarity +- sfnetworks needs to handle multiple 'from' vertices explicitly +- The function should iterate or be clear about using only the first vertex + +## Recommendation for sfnetworks +- If only first vertex intended: `from <- from[1]` +- If all vertices intended: `lapply(from, function(f) all_shortest_paths(g, from = f, to = to))` +- Or provide clear warning/error about multiple vertices diff --git a/revdep/notify-maintainers.sh b/revdep/notify-maintainers.sh new file mode 100755 index 00000000000..8c348f7dd46 --- /dev/null +++ b/revdep/notify-maintainers.sh @@ -0,0 +1,293 @@ +#!/bin/bash +# Script to notify package maintainers about reverse dependency issues. +# For packages hosted on GitHub: creates a GitHub issue via the `gh` CLI. +# For packages not on GitHub: creates an email draft with pre-filled To/Cc fields. +# +# Issues are opened on behalf of the currently authenticated `gh` user. +# All filed issue URLs are recorded in notifications/issue-log.txt. + +set -e + +IGRAPH_CC_EMAIL="igraph-help@igraph.discourse.group" + +# Check if gh CLI is available +if ! command -v gh &> /dev/null; then + echo "Warning: gh CLI not found. Will only create email drafts." + GH_AVAILABLE=0 +else + GH_AVAILABLE=1 +fi + +# Function to check if a GitHub repo exists and is accessible. +# Prints "owner/repo" on success; returns non-zero on failure. +check_github_repo() { + local repo=$1 + if [ $GH_AVAILABLE -eq 0 ]; then + return 1 + fi + + # Extract owner/repo from URL + local owner_repo + owner_repo=$(echo "$repo" | sed 's|https://github.com/||' | sed 's|\.git$||') + + if gh repo view "$owner_repo" &> /dev/null; then + echo "$owner_repo" + return 0 + else + return 1 + fi +} + +# Create a GitHub issue and log the resulting URL. +create_github_issue() { + local package=$1 + local owner_repo=$2 + local title=$3 + local body=$4 + + echo " Creating GitHub issue..." + + local temp_file + temp_file=$(mktemp) + echo "$body" > "$temp_file" + + local issue_url + if issue_url=$(gh issue create \ + --repo "$owner_repo" \ + --title "$title" \ + --body-file "$temp_file" \ + --label "bug" 2>/dev/null); then + echo " ✓ Issue created: $issue_url" + echo "$package: $issue_url" >> "$OUTPUT_DIR/issue-log.txt" + else + echo " ⚠ Failed to create issue (check authentication / permissions)" + fi + + rm -f "$temp_file" +} + +# Create an email draft with To/Cc pre-filled from CRAN metadata. +create_email_draft() { + local package=$1 + local maintainer_email=$2 + local subject=$3 + local body=$4 + local email_file=$5 + + echo " Creating email draft..." + + cat > "$email_file" << EOF +To: $maintainer_email +Cc: $IGRAPH_CC_EMAIL +Subject: $subject + +Dear $package maintainer, + +$body + +Best regards, +The igraph Development Team +EOF + + echo " ✓ Email draft saved to: $email_file" +} + +# High-level helper: determine the right notification channel and call it. +notify_package() { + local package=$1 + local github_url=$2 + local maintainer_email=$3 + local issue_title=$4 + local issue_body=$5 + local email_subject=$6 + + echo "Package: $package" + local owner_repo + if owner_repo=$(check_github_repo "$github_url"); then + echo " ✓ GitHub repository accessible: $owner_repo" + create_github_issue "$package" "$owner_repo" "$issue_title" "$issue_body" + else + echo " ✗ GitHub repository not accessible" + create_email_draft "$package" "$maintainer_email" "$email_subject" "$issue_body" \ + "$OUTPUT_DIR/${package}-email.txt" + fi + echo "" +} + +# Directory for output files +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OUTPUT_DIR="$SCRIPT_DIR/notifications" +mkdir -p "$OUTPUT_DIR" + +echo "=== Notifying Package Maintainers about Reverse Dependency Issues ===" +echo "" + +# -------------------------------------------------------------------------- +# Cascade +# -------------------------------------------------------------------------- +notify_package \ + "Cascade" \ + "https://github.com/fbertran/Cascade" \ + "frederic.bertrand@lecnam.net" \ + "Namespace collision warning with igraph 2.3.0+" \ + 'When loading the Cascade package with igraph 2.3.0 or later, the following warning appears: + +``` +Warning: replacing previous import '"'"'igraph::circulant'"'"' by '"'"'magic::circulant'"'"' when loading '"'"'Cascade'"'"' +``` + +**Root cause**: igraph 2.3.0 added `make_circulant()` and its constructor alias `circulant()`. The `magic` package also exports `circulant()`, creating a namespace collision. + +**Impact**: Minor – a warning is produced but Cascade continues to work correctly. + +**Suggested fix**: Explicitly import `magic::circulant` in your NAMESPACE file: + +```r +importFrom(magic, circulant) +``` + +This ensures `magic::circulant` takes precedence and suppresses the warning. + +--- +*Discovered during reverse dependency checking for igraph. Details: https://github.com/igraph/rigraph*' \ + "Namespace collision warning in Cascade with igraph 2.3.0+" + +# -------------------------------------------------------------------------- +# DiagrammeR +# -------------------------------------------------------------------------- +notify_package \ + "DiagrammeR" \ + "https://github.com/rich-iannone/DiagrammeR" \ + "riannone@me.com" \ + "neighbors() now requires a single vertex ID in igraph 2.3.0+" \ + '`get_leverage_centrality()` fails with igraph 2.3.0 or later: + +``` +Error in `igraph::neighbors()`: +! `vid` must specify exactly one vertex +``` + +**Root cause**: igraph 2.3.0 adds strict validation that `neighbors()` must receive exactly one vertex ID. The call `igraph::neighbors(ig_graph, degree_vals)` passes a vector, which was previously silently reduced to the first element. + +**Suggested fix**: Iterate over vertices individually: + +```r +# In get_leverage_centrality(), wrap the body in a per-node loop, e.g.: +purrr::map(seq_along(degree_vals), function(i) { + nb <- igraph::neighbors(ig_graph, i) + mean((degree_vals[i] - degree_vals[nb]) / (degree_vals[i] + degree_vals[nb])) +}) +``` + +--- +*Discovered during reverse dependency checking for igraph. Details: https://github.com/igraph/rigraph*' \ + "neighbors() requires single vertex in DiagrammeR with igraph 2.3.0+" + +# -------------------------------------------------------------------------- +# jewel +# -------------------------------------------------------------------------- +notify_package \ + "jewel" \ + "https://github.com/annaplaksienko/jewel" \ + "anna@plaxienko.com" \ + "Non-integer niter value passed to rewire() in igraph 2.3.0+" \ + 'The jewel package fails with igraph 2.3.0 or later: + +``` +Error in rewire_impl(rewire = graph, n = niter, mode = mode) : + The value 2.4500000000000002 is not representable as an integer. Invalid value +``` + +**Root cause**: igraph 2.3.0 strictly validates that `niter` is representable as an integer. The value `p * 0.05` (e.g., `49 * 0.05 = 2.45`) is not an integer. + +**Suggested fix**: Wrap the computed value with `ceiling()` or `round()`: + +```r +niter <- ceiling(p * 0.05) # was: niter <- p * 0.05 +``` + +--- +*Discovered during reverse dependency checking for igraph. Details: https://github.com/igraph/rigraph*' \ + "Non-integer niter in jewel causes error with igraph 2.3.0+" + +# -------------------------------------------------------------------------- +# rSpectral +# -------------------------------------------------------------------------- +notify_package \ + "rSpectral" \ + "https://github.com/cmclean5/rSpectral" \ + "lptolik@gmail.com" \ + "Modularity test failures due to automatic weight usage in igraph 2.3.0+" \ + 'rSpectral tests fail with igraph 2.3.0 or later: + +``` +Expected `c$modularity` to equal `exp_mod10`. +Differences: + `actual`: 0.432 +`expected`: 0.408 +``` + +**Root cause**: igraph 2.3.0 changed `modularity()` to automatically use the `"weight"` edge attribute when present. The test graphs have a `"weight"` attribute, so modularity is now computed with weights, giving different values. + +**Options**: + +1. Remove the unintended weight attribute: + ```r + g <- igraph::delete_edge_attr(g, "weight") + ``` + +2. Update test graphs with `igraph::upgrade_graph()` and check whether weights are intentional. + +3. Update expected test values if weighted modularity is the correct behavior. + +--- +*Discovered during reverse dependency checking for igraph. Details: https://github.com/igraph/rigraph*' \ + "Modularity test failures in rSpectral with igraph 2.3.0+" + +# -------------------------------------------------------------------------- +# sfnetworks +# -------------------------------------------------------------------------- +notify_package \ + "sfnetworks" \ + "https://github.com/luukvdmeer/sfnetworks" \ + "luukvandermeer@live.nl" \ + "all_shortest_paths() from argument requires single vertex in igraph 2.3.0+" \ + '`st_network_paths()` fails with igraph 2.3.0 or later: + +``` +Error in `all_shortest_paths(x, from, to, weights = weights, ...)`: +! `from` must specify exactly one vertex +``` + +**Root cause**: igraph 2.3.0 adds strict validation that `all_shortest_paths(from = ...)` must receive exactly one vertex. sfnetworks passes a vector of "from" vertices. + +**Suggested fix**: Iterate over each `from` vertex: + +```r +lapply(from_vertices, function(f) { + igraph::all_shortest_paths(x, from = f, to = to, weights = weights, ...) +}) +``` + +--- +*Discovered during reverse dependency checking for igraph. Details: https://github.com/igraph/rigraph*' \ + "all_shortest_paths() from must be single vertex in sfnetworks with igraph 2.3.0+" + +# -------------------------------------------------------------------------- +# Summary +# -------------------------------------------------------------------------- +echo "=== Summary ===" +echo "Output directory: $OUTPUT_DIR" +echo "" +if [ -f "$OUTPUT_DIR/issue-log.txt" ]; then + echo "GitHub issues filed:" + cat "$OUTPUT_DIR/issue-log.txt" + echo "" +fi +if ls "$OUTPUT_DIR"/*.txt &> /dev/null 2>&1; then + email_files=$(ls "$OUTPUT_DIR"/*-email.txt 2>/dev/null || true) + if [ -n "$email_files" ]; then + echo "Email drafts created (review and send):" + echo "$email_files" + fi +fi diff --git a/revdep/problems-analysis.md b/revdep/problems-analysis.md new file mode 100644 index 00000000000..5b68ad1fe79 --- /dev/null +++ b/revdep/problems-analysis.md @@ -0,0 +1,275 @@ +# Analysis of Reverse Dependency Problems + +This document provides minimal reproducible examples and analysis for packages that now fail their checks compared to the most recent CRAN version. + +**Note**: Runnable R scripts and markdown outputs demonstrating each issue can be found in the `examples/` directory. A draft response to the CRAN review email is in `cran-response-draft.md`. + +## Summary + +Six packages have newly broken checks in our revdepcheck run; two additional packages (rgph, tmap.networks) appeared in the CRAN win-builder check: +1. **Cascade** (v2.3): Namespace collision warning +2. **DiagrammeR** (v1.0.11): `neighbors()` requires exactly one vertex +3. **jewel** (v2.0.2): Error due to strict integer validation +4. **manynet** (v1.7.0): Scalar integer validation in `sample_last_cit()` +5. **rSpectral** (v1.0.0.14): Test failures due to modularity calculation changes +6. **sfnetworks** (v0.6.5): `from` parameter requires exactly one vertex +7. **rgph** (v0.1.0): Under investigation (igraph is `Suggests` only; win-builder failure) +8. **tmap.networks** (v0.1): Cascading failure from sfnetworks + +## 1. Cascade - Namespace Collision Warning + +### Issue +``` +Warning: replacing previous import 'igraph::circulant' by 'magic::circulant' when loading 'Cascade' +``` + +### Root Cause +igraph added `make_circulant()` and its constructor alias `circulant()` in version 2.2.1.9003, creating a namespace collision with `magic::circulant()`. + +### Assessment +**Inadvertent behavior change in igraph, not a bug in Cascade.** + +The `circulant` function is exported as a constructor alias. Users should use `make_circulant()` directly. + +### Recommendation +- **For Cascade**: Explicitly import `magic::circulant` in NAMESPACE +- **For igraph**: Consider unexported the constructor alias or document this known collision + +**Severity**: Low - Warning only, no functionality broken + +--- + +## 2. DiagrammeR - neighbors() Requires Single Vertex + +### Issue +``` +Error in `igraph::neighbors()`: +! `vid` must specify exactly one vertex +``` + +### Root Cause +igraph added stricter validation requiring exactly one vertex for `neighbors()`. DiagrammeR's `get_leverage_centrality()` passes a vector to `neighbors()`, which previously may have used implicit vectorization or only the first element. + +### Assessment +**Intentional API tightening in igraph for safety and clarity.** + +The code `mean(degree_vals[igraph::neighbors(ig_graph, degree_vals)])` attempts to pass a vector where a scalar is expected. + +### Recommendation +**For DiagrammeR**: Iterate over vertices individually: +```r +# Change from: +neighbors(ig_graph, degree_vals) + +# To: +lapply(seq_along(degree_vals), function(i) neighbors(ig_graph, i)) +``` + +**Severity**: High - Package functionality broken + +--- + +## 3. jewel - Integer Validation Error + +### Issue +``` +Error in rewire_impl(rewire = graph, n = niter, mode = mode) : + The value 2.4500000000000002 is not representable as an integer. Invalid value +``` + +### Minimal Reproducible Example +See `examples/jewel-integer-issue.R` and `.md` + +### Root Cause +- `rewire_impl()` converts n with `as.numeric()`, preserving fractional parts +- C code now strictly validates that numeric values are representable as integers +- Previously may have silently truncated, now explicitly rejects + +### Assessment +**This uncovered a bug in the jewel package.** + +The `niter` parameter should logically be an integer (number of iterations). jewel computes `niter <- p * 0.05` which results in non-integer values. + +### Recommendation +**For jewel**: Use integer rounding: +```r +niter <- ceiling(p * 0.05) # or floor() or round() +``` + +**For igraph** (backward compatibility option): Add `as.integer()` in `rewire_keeping_degseq()` before calling `rewire_impl()`. + +**Severity**: High - Package functionality broken + +--- + +## 4. manynet - Scalar Integer Validation + +### Issue +``` +Error in `lastcit_game_impl(...)`: +Expecting a scalar integer but received a vector of length 2. +``` + +### Root Cause +igraph added stricter validation for scalar parameters. The C code now validates that parameters expecting scalars are indeed scalars, not vectors. + +### Assessment +**Stricter type checking in igraph.** + +manynet's `generate_citations()` may be passing a vector where a scalar is expected. + +### Recommendation +**For manynet**: Ensure scalar values: +```r +# If only first value intended: +edges <- edges[1] + +# If multiple values intended, iterate: +lapply(edges_vec, function(e) sample_last_cit(n, edges = e, ...)) +``` + +**Severity**: High - Package functionality broken + +--- + +## 5. rSpectral - Modularity Calculation Changes + +### Issue +Test failures due to different modularity values: +- Expected: 0.408, Actual: 0.432 (difference: +0.024) +- Expected: 0.3776, Actual: 0.3758 (difference: -0.0018) + +### Minimal Reproducible Example +See `examples/rspectral-modularity-issue.R` and `.md` + +### Root Cause +igraph v2.2.1.9004 changed `modularity()` to automatically use the `"weight"` edge attribute if present: + +```r +if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { + weights <- E(graph)$weight +} +``` + +### Hacky workaround (do not recommend to revdep maintainers) +Passing `weights = numeric()` happens to disable auto-detection due to implementation details: + +```r +modularity(g, membership, weights = numeric()) +``` + +This is **not** a robust solution and should **not** be recommended to rSpectral or other downstream maintainers, as it relies on internal behavior that may change. + +### Assessment +**Inadvertent breaking change in igraph. igraph needs to provide a clean fix.** + +### Recommendation +**For igraph** (action required before notifying revdep maintainers): +- Add a proper `weights = NA` mechanism to explicitly disable weight auto-detection, with a documented user-facing contract. +- Document this as a breaking change in NEWS.md. +- Until a clean solution exists, the recommendation to rSpectral maintainers should be to remove unintended weight attributes: `g <- delete_edge_attr(g, "weight")` + +**For rSpectral** (once root cause is understood): +1. Determine whether graphs are *intended* to have a `"weight"` attribute +2. If not: `g <- igraph::delete_edge_attr(g, "weight")` before calling `modularity()` +3. If yes: update expected test values to the weighted modularity +4. Call `igraph::upgrade_graph()` on stored graph objects + +**Severity**: Medium - Tests fail; igraph behavior change needs a clean fix + +--- + +## 6. sfnetworks - from Parameter Requires Single Vertex + +### Issue +``` +Error in `all_shortest_paths(x, from, to, weights = weights, ...)`: +! `from` must specify exactly one vertex +``` + +### Root Cause +igraph added stricter validation requiring exactly one vertex for the `from` parameter in `all_shortest_paths()`. sfnetworks passes multiple vertices, which previously may have used only the first vertex implicitly. + +### Assessment +**Intentional API tightening in igraph for safety and clarity.** + +### Recommendation +**For sfnetworks**: +```r +# If only first vertex intended: +from <- from[1] + +# If all vertices intended, iterate: +lapply(from, function(f) all_shortest_paths(g, from = f, to = to)) + +# Or provide clear warning about multiple vertices +``` + +**Severity**: High - Package functionality broken + +--- + +## 7. rgph (0.1.0) – Under Investigation + +### Issue +Reported as failing in the CRAN win-builder check for igraph 2.3.0. Not reproduced in our Linux-based `revdepcheck` run. + +### Root Cause (preliminary) +rgph lists igraph only as a `Suggests` dependency. Failures likely occur in tests or examples that optionally use igraph. The most probable cause, given the other patterns in this release, is either the `neighbors()` scalar validation or another strict type-checking change. + +### Assessment +Preliminary assessment: likely a downstream issue (using undocumented behavior of igraph). Investigation ongoing. + +### Recommendation +Run targeted checks to reproduce: +```r +install.packages("rgph") +tools::testInstalledPackage("rgph") # or devtools::check() +``` +Once the error is reproduced, contact the rgph maintainer with a targeted fix. + +**Severity**: Unknown – under investigation + +--- + +## 8. tmap.networks (0.1) – Cascading Failure from sfnetworks + +### Issue +tmap.networks imports sfnetworks directly. Since sfnetworks fails due to the `all_shortest_paths()` `from` validation change, tmap.networks fails as a cascade. + +### Root Cause +Not a direct bug in tmap.networks; it inherits the sfnetworks breakage. + +### Assessment +**Cascading failure**: tmap.networks will pass once sfnetworks is fixed. + +### Recommendation +No action needed for tmap.networks directly. Once sfnetworks is updated, inform the tmap.networks maintainer that a new sfnetworks release is available. + +**Severity**: High (package broken), but resolved automatically when sfnetworks is fixed + +--- + +## Conclusion + +| Package | Issue Type | Root Cause | Severity | Action | +|---------|-----------|------------|----------|--------| +| Cascade | Namespace collision | New `circulant()` export | Low | Fix in Cascade NAMESPACE; igraph to reconsider export | +| DiagrammeR | API tightening | `neighbors()` requires scalar | High | DiagrammeR to iterate vertices | +| jewel | Type validation | Stricter integer checking | High | jewel to use `ceiling()` on computed niter | +| manynet | Type validation | Scalar parameter checking | High | manynet to ensure scalar inputs | +| rSpectral | Behavior change | Automatic weight usage | Medium | igraph needs clean fix; rSpectral to check weight intent | +| sfnetworks | API tightening | `from` requires scalar | High | sfnetworks to iterate vertices | +| rgph | Under investigation | Likely API tightening | Unknown | Reproduce and contact maintainer | +| tmap.networks | Cascading | sfnetworks failure | High | Wait for sfnetworks fix | + +### Overall Assessment + +- **1 namespace collision** (Cascade) – Minor impact; inadvertent igraph export +- **3 API tightening changes** (DiagrammeR, manynet, sfnetworks) – Intentional safety improvements; downstream bugs exposed +- **1 uncovered downstream bug** (jewel) – Should use integer values +- **1 behavior change needing igraph fix** (rSpectral) – Automatic weights; igraph must provide a clean `NA` mechanism +- **1 cascading failure** (tmap.networks) – Resolved when sfnetworks is fixed +- **1 under investigation** (rgph) – Likely API tightening + +Most issues stem from igraph's improved type safety and parameter validation. These are generally positive changes. However, the modularity weight auto-detection change (rSpectral) requires a proper igraph-side fix before we can issue clear guidance to downstream maintainers.