diff --git a/spsvalidator/pyproject.toml b/spsvalidator/pyproject.toml
index 68db3dc..1e2a1a0 100644
--- a/spsvalidator/pyproject.toml
+++ b/spsvalidator/pyproject.toml
@@ -11,7 +11,7 @@ requires-python = ">=3.11"
dependencies = [
"Flask>=3.0.0",
"lxml>=5.0.0",
- "packtools @ git+https://git@github.com/scieloorg/packtools@4.12.6",
+ "packtools @ git+https://git@github.com/scieloorg/packtools@4.16.5",
"pywebview>=5.1",
"setuptools>=68,<82",
"requests>=2.31.0",
diff --git a/spsvalidator/tests/test_packtools_validations.py b/spsvalidator/tests/test_packtools_validations.py
new file mode 100644
index 0000000..f453e62
--- /dev/null
+++ b/spsvalidator/tests/test_packtools_validations.py
@@ -0,0 +1,322 @@
+"""
+Verifica que validate_sps_zip exercita todos os 36 grupos de validação
+definidos no packtools 4.16.5 xml_validator.py.
+
+Duas camadas de testes:
+ 1. Cobertura — cada função validate_* é chamada por validate_sps_zip.
+ 2. Comportamento — grupos novos no 4.16.x detectam problemas em XML real.
+"""
+from __future__ import annotations
+
+import zipfile
+from textwrap import dedent
+
+import pytest
+
+from spsvalidator.domain.validation import validate_sps_zip
+
+
+# ---------------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------------
+
+
+def _make_zip(tmp_path, xml_content: str) -> str:
+ zip_path = tmp_path / "test.zip"
+ with zipfile.ZipFile(zip_path, "w") as zf:
+ zf.writestr("article.xml", xml_content)
+ return str(zip_path)
+
+
+def _rows_groups(result) -> set[str]:
+ return {r["group"] for r in result["rows"]}
+
+
+def _all_groups(result) -> set[str]:
+ """Grupos em rows E em exceptions (inclui casos onde packtools lança exceção)."""
+ return _rows_groups(result) | {r["group"] for r in result["exceptions"]}
+
+
+def _article(
+ *,
+ body: str = "",
+ back: str = "",
+ extra_meta: str = "",
+ include_permissions: bool = True,
+) -> str:
+ permissions_block = (
+ dedent("""\
+
+
+ Open access.
+
+ """)
+ if include_permissions
+ else ""
+ )
+ default_body = "IntroductionText.
"
+ return (
+ '\n'
+ '\n'
+ "\n"
+ "\n"
+ 'Rev Test\n'
+ ""
+ "Revista de Teste"
+ 'Rev. Teste'
+ "\n"
+ '1234-5678\n'
+ "Publisher\n"
+ "\n"
+ "\n"
+ '10.1234/test.2023.001\n'
+ 'S1234-56782023000100001\n'
+ ""
+ 'Original Article'
+ "\n"
+ "Test Article Title\n"
+ ""
+ ''
+ "SilvaJoão"
+ ""
+ "\n"
+ ''
+ "20230101"
+ "\n"
+ "1\n"
+ "e001\n"
+ + permissions_block + "\n"
+ + extra_meta + "\n"
+ "\n"
+ "\n"
+ "" + (body or default_body) + "\n"
+ "" + back + "\n"
+ "\n"
+ )
+
+
+# ---------------------------------------------------------------------------
+# 1. Cobertura: cada função validate_* é chamada por validate_sps_zip
+# ---------------------------------------------------------------------------
+
+# (group_name, function_name em xml_validations)
+VALIDATION_FUNCTIONS = [
+ ("journal-meta", "validate_journal_meta"),
+ ("bibliographic strip", "validate_bibliographic_strip"),
+ ("article attributes", "validate_article"),
+ ("article-id", "validate_article_ids"),
+ ("article dates", "validate_article_dates"),
+ ("history", "validate_history"),
+ ("article languages (1)", "validate_article_languages"),
+ ("article languages (2)", "validate_metadata_languages"),
+ ("subject", "validate_article_toc_sections"),
+ ("article-type", "validate_article_type"),
+ ("contrib", "validate_article_contribs"),
+ ("aff", "validate_affiliations"),
+ ("author-notes", "validate_author_notes"),
+ ("abstract", "validate_abstracts"),
+ ("open science", "validate_open_science_actions"),
+ ("funding data", "validate_funding_data"),
+ ("id and rid", "validate_id_and_rid_match"),
+ ("fig", "validate_figs"),
+ ("table-wrap", "validate_tablewraps"),
+ ("disp-formula", "validate_equations"),
+ ("inline-formula", "validate_inline_equations"),
+ ("reference", "validate_references"),
+ ("related-article", "validate_related_articles"),
+ ("fn", "validate_fns"),
+ ("reviewer-report", "validate_peer_reviews"),
+ ("accessibility", "validate_accessibility_data"),
+ ("media", "validate_media"),
+ ("app", "validate_app_group"),
+ ("supplementary-material", "validate_supplementary_materials"),
+ ("ext-link", "validate_ext_links"),
+ ("list", "validate_lists"),
+ ("graphic", "validate_graphics"),
+ ("response", "validate_response"),
+ ("sec", "validate_secs"),
+ ("product", "validate_products"),
+ ("permissions", "validate_permissions"),
+]
+
+
+@pytest.mark.parametrize(
+ "group,func_name",
+ VALIDATION_FUNCTIONS,
+ ids=[func for _, func in VALIDATION_FUNCTIONS],
+)
+def test_validation_function_is_called(tmp_path, monkeypatch, group, func_name):
+ """Cada função validate_* do packtools deve ser chamada por validate_sps_zip."""
+ from packtools.sps.validation import xml_validations
+
+ called = {"yes": False}
+ original = getattr(xml_validations, func_name)
+
+ def spy(*args, **kwargs):
+ called["yes"] = True
+ return original(*args, **kwargs)
+
+ monkeypatch.setattr(xml_validations, func_name, spy)
+ validate_sps_zip(_make_zip(tmp_path, _article()))
+
+ assert called["yes"], f"{func_name} não foi chamada por validate_sps_zip"
+
+
+# ---------------------------------------------------------------------------
+# 2. Comportamento: grupos novos no 4.16.x detectam problemas reais
+# ---------------------------------------------------------------------------
+
+
+def test_table_wrap_sem_label_e_caption_reporta_issue(tmp_path):
+ """ sem