Skip to content

feat: add SXSD schema validation to Slides lint - #2103

Merged
BD-ZERO merged 4 commits into
mainfrom
feat/slides-sxsd-lint
Jul 30, 2026
Merged

feat: add SXSD schema validation to Slides lint#2103
BD-ZERO merged 4 commits into
mainfrom
feat/slides-sxsd-lint

Conversation

@BD-ZERO

@BD-ZERO BD-ZERO commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add self-contained SXSD schema validation to the Slides XML lint flow. The validator now reports structured and actionable syntax errors while preserving compatibility with XML returned by the Slides service.

Changes

  • Add SXSD validation for elements, attributes, required fields, value types, enums, patterns, child order, and cardinality.
  • Preserve service-filled round-trip fields during validation.
  • Isolate slide-level SXSD failures so one invalid slide does not block checks for the entire presentation.
  • Deduplicate overlapping legacy and SXSD diagnostics.
  • Support standalone <slide> fragments and valid Slides namespaces:
    • http://www.larkoffice.com/sml/2.0
    • /sml/2.0
    • https://www.larkoffice.com/sml/2.0
  • Continue rejecting missing or unsupported presentation namespaces.
  • Add regression coverage for server readback, self-closing slides, XML comments, rich-text shadow rules, attribute typos, and malformed structures.

Test Plan

  • python3 -m unittest xml_text_overlap_lint_test.py
    • 165 tests passed
  • Real Slides readback verification:
    • Presentation: Q8NSsDHfFlL2ladrkmOc2YnEnJc
    • Flow: slides +xml-get → xml_text_overlap_lint.py
    • Result: error_count=0, release_ready=true

Related Issues

  • None

Summary by CodeRabbit

  • New Features

    • Extended SXSD validation with minLength/maxLength for simple types and stricter double/decimal lexical checking.
    • Improved hinting for missing/incorrect SXSD attributes, including alias and close-match suggestions.
  • Bug Fixes

    • Refined namespace and XML-declaration validation.
    • Improved slide-scoped error reporting and reduced redundant issues when earlier hints can resolve problems.
    • Rejects non-finite numeric scalars (inf, -inf, nan) as SXSD invalid values.
  • Tests

    • Expanded CLI/JSON, namespace, inline union, numeric and pattern/unsupported-pattern coverage, plus updated fixtures.

BD-ZERO added 2 commits July 29, 2026 15:56
- validate SXSD attributes, values, and child structures
- preserve server-filled readback fields
- isolate SXSD failures by slide
- keep valid slides eligible for layout checks
@github-actions github-actions Bot added the size/XL Architecture-level or global-impact change label Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Extends SXSD validation with length facets, strict numeric lexical checks, namespace handling, sanitized document validation, slide-scoped issue reporting, normalized layout parsing, improved hints, and expanded CLI, schema, geometry, and density tests.

Changes

SXSD validation and lint integration

Layer / File(s) Summary
Schema model and scalar validation
skills/lark-slides/scripts/sxsd_validator.py
Adds length constraints and strict double/decimal lexical validation while retaining union, pattern, and namespace diagnostics.
Sanitized document and issue integration
skills/lark-slides/scripts/xml_text_overlap_lint.py
Delegates SXSD validation, removes server-filled fields, reconciles attribute suggestions, normalizes slides, and scopes issues per slide.
SXSD and lint regression coverage
skills/lark-slides/scripts/xml_text_overlap_lint_test.py
Expands tests for CLI output, namespaces, hints, scalar forms, schema behavior, slide validation, geometry, and density fixtures.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • larksuite/cli#1963: Updates SXSD validation and issue reporting in the same linting script.
  • larksuite/cli#2022: Refactors the SXSD validation flow and related server-filled-field handling.

Suggested reviewers: liangshuo-1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding SXSD schema validation to Slides lint.
Description check ✅ Passed The description follows the template with Summary, Changes, Test Plan, and Related Issues, and includes substantive details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/slides-sxsd-lint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread skills/lark-slides/scripts/xml_text_overlap_lint_test.py Dismissed
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@626cf3eb8749ca20e205f4954d4ff1a945aad247

🧩 Skill update

npx skills add larksuite/cli#feat/slides-sxsd-lint -y -g

@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.19%. Comparing base (358cd06) to head (626cf3e).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2103      +/-   ##
==========================================
+ Coverage   75.16%   75.19%   +0.02%     
==========================================
  Files         912      914       +2     
  Lines       96475    96789     +314     
==========================================
+ Hits        72517    72778     +261     
- Misses      18381    18413      +32     
- Partials     5577     5598      +21     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
skills/lark-slides/scripts/sxsd_validator.py (1)

152-157: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Inline attribute type keys depend on id() of live ET elements. Both sites mint/recompute __inline_attribute_{name}_{id(attribute)}, so the producer and consumer only agree while every xs:attribute element remains reachable; unreachable ones can be collected and their id() reused, mapping an attribute to a different simple type. Replace the identity component with a deterministic one on both sides.

  • skills/lark-slides/scripts/sxsd_validator.py#L152-L157: mint the key from stable data (owner complexType/element name plus attribute name, or a monotonically increasing counter recorded on the model).
  • skills/lark-slides/scripts/sxsd_validator.py#L213-L216: derive the lookup key with the exact same deterministic scheme instead of id(attribute).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/sxsd_validator.py` around lines 152 - 157, Replace
the id(attribute)-based key generation in both the inline attribute type
producer at lines 152-157 and its lookup at lines 213-216 with one shared
deterministic scheme, such as owner type/element name plus attribute name.
Ensure both sites derive exactly the same key and preserve correct mapping even
after XML elements become unreachable.
skills/lark-slides/scripts/xml_text_overlap_lint_test.py (1)

2793-2793: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fractional fontSize was edited out of the fixture without covering the new constraint.

Changing fontSize="12.4" to "12" implies the schema pass now rejects fractional font sizes, but nothing asserts that. Add a small case pinning the expected code (sxsd_invalid_scalar / sxsd_value_out_of_range) for fontSize="12.4", otherwise a future loosening or tightening of that facet goes unnoticed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py` at line 2793, Add a
focused test in the fixture validation tests covering a fractional fontSize
value of 12.4, and assert the expected schema error code (sxsd_invalid_scalar or
sxsd_value_out_of_range). Keep the existing integer fontSize fixture unchanged
and ensure the test fails if fractional font sizes are later accepted or
rejected with an unexpected code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/lark-slides/scripts/sxsd_validator.py`:
- Around line 99-104: Update the xs:union handling in the schema parser to
collect nested xs:simpleType children as union members, including their inline
pattern constraints, while retaining declared memberTypes. Resolve local schema
type names through the existing type-resolution path so both named and anonymous
union members are included in validation; ensure BulletSizeType no longer
produces an empty union_members tuple.
- Around line 502-512: Update xsd_pattern_matches to catch ValueError from
python_pattern_for_xsd and re.error from re.fullmatch, returning True so an
untranslatable or invalid schema pattern does not abort validate_sxsd. Apply
matching with a bounded or otherwise non-backtracking approach for non-hardcoded
patterns as well, preserving the existing optimized handling for [\w.-]+[.:]\S*.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py`:
- Around line 3246-3256: Update test_rejects_non_xsd_whitespace_in_color_pattern
so the intentional non-breaking space in the color fixture is represented with
an explicit escape rather than a literal Unicode character. Preserve the test’s
malformed-color input and sxsd_pattern_mismatch assertion.

---

Nitpick comments:
In `@skills/lark-slides/scripts/sxsd_validator.py`:
- Around line 152-157: Replace the id(attribute)-based key generation in both
the inline attribute type producer at lines 152-157 and its lookup at lines
213-216 with one shared deterministic scheme, such as owner type/element name
plus attribute name. Ensure both sites derive exactly the same key and preserve
correct mapping even after XML elements become unreachable.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py`:
- Line 2793: Add a focused test in the fixture validation tests covering a
fractional fontSize value of 12.4, and assert the expected schema error code
(sxsd_invalid_scalar or sxsd_value_out_of_range). Keep the existing integer
fontSize fixture unchanged and ensure the test fails if fractional font sizes
are later accepted or rejected with an unexpected code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 237b452e-6f35-47cc-a163-663e1e5c22f5

📥 Commits

Reviewing files that changed from the base of the PR and between 29a97db and 1c0ab58.

📒 Files selected for processing (3)
  • skills/lark-slides/scripts/sxsd_validator.py
  • skills/lark-slides/scripts/xml_text_overlap_lint.py
  • skills/lark-slides/scripts/xml_text_overlap_lint_test.py

Comment thread skills/lark-slides/scripts/sxsd_validator.py
Comment thread skills/lark-slides/scripts/sxsd_validator.py
Comment thread skills/lark-slides/scripts/xml_text_overlap_lint_test.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
skills/lark-slides/scripts/xml_text_overlap_lint_test.py (1)

3436-3471: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hint assertion is coincidental, not a real check of the boilerplate text.

self.assertIn("unsupported", str(issues[0]["hint"]).lower()) only passes because the fixture's own type is named UnsupportedPatternType, which gets echoed into the hint via f"Extend the SXSD pattern interpreter for {attr_rule.type_name}; ...". The actual boilerplate hint text produced by validate_element_attributes for sxsd_unsupported_pattern never contains the word "unsupported" itself — so this assertion wouldn't catch a regression in the hint wording, and would pass even if the hint became meaningless, as long as the schema fixture keeps that type name.

♻️ Assert against wording that's independent of the fixture's type name
-        self.assertIn("unsupported", str(issues[0]["hint"]).lower())
+        self.assertIn("pattern interpreter", str(issues[0]["hint"]).lower())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py` around lines 3436 -
3471, Update test_reports_unsupported_xsd_pattern_without_crashing so its hint
assertion verifies stable boilerplate wording from the sxsd_unsupported_pattern
diagnostic rather than the fixture-dependent type name. Keep the existing code
and attribute assertions, and assert a meaningful expected phrase that
validate_element_attributes always emits.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py`:
- Around line 3436-3471: Update
test_reports_unsupported_xsd_pattern_without_crashing so its hint assertion
verifies stable boilerplate wording from the sxsd_unsupported_pattern diagnostic
rather than the fixture-dependent type name. Keep the existing code and
attribute assertions, and assert a meaningful expected phrase that
validate_element_attributes always emits.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: be7841be-d01e-48bf-8d22-c767d1a159c2

📥 Commits

Reviewing files that changed from the base of the PR and between 1c0ab58 and be4b7c5.

📒 Files selected for processing (2)
  • skills/lark-slides/scripts/sxsd_validator.py
  • skills/lark-slides/scripts/xml_text_overlap_lint_test.py

Comment thread skills/lark-slides/scripts/sxsd_validator.py
Comment thread skills/lark-slides/scripts/sxsd_validator.py
Comment thread skills/lark-slides/scripts/sxsd_validator.py
Comment thread skills/lark-slides/scripts/sxsd_validator.py Outdated
Comment thread skills/lark-slides/scripts/xml_text_overlap_lint.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
skills/lark-slides/scripts/xml_text_overlap_lint.py (1)

400-503: 🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

Suggestion-based suppression can silently drop real sxsd_missing_required_attr/sxsd_unexpected_child errors on unrelated sibling elements.

suggested_attr_candidates / missing_attrs_by_location / unsupported_tag_locations are all keyed by (path, tag) (no sibling index), same as sxsd_validator.py's own path scheme. Since virtually every slide has multiple <shape> (or other) siblings computing an identical path, a typo on one element can suppress a completely unrelated, genuinely-missing-attribute error on another element with the same tag. Example:

<shape type="text" topLeftXX="80" topLeftY="80" width="300" height="60"/>   <!-- typo -->
<shape type="text" topLeftY="80" width="300" height="60"/>                  <!-- genuinely missing topLeftX -->

Both compute path slide/data/shape. Shape 1's typo suggests topLeftX; the schema validator reports topLeftX missing on both shapes (identical (path, tag, attr) triple); the ambiguity-resolution logic treats this as "exactly one candidate resolves the ambiguity" and suppresses every matching schema issue — including shape 2's real, unrelated error.

A correct fix needs per-element correlation (hard, since _validate_sxsd_schema_constraints runs the schema validator on a separately deep-copied tree). A cheap, safe mitigation is to only allow suppression when the (path, tag) location is unique in the document:

🛡️ Guard suppression behind a uniqueness check
 def validate_sxsd_document(xml: str, root: ET.Element) -> list[dict[str, Any]]:
     tag_attributes = load_sxsd_tag_attributes()
     supported_tags = set(tag_attributes)
     issues: list[dict[str, Any]] = []
     suggested_attr_candidates: dict[tuple[str, str], list[set[str]]] = {}
+    location_occurrence_counts: dict[tuple[str, str], int] = {}

     def visit(element: ET.Element, ancestors: list[str], path: str) -> None:
         if should_skip_sxsd_subtree(element, ancestors):
             return

         tag_name = xml_local_name(element.tag)
         current_path = f"{path}/{tag_name}" if path else tag_name
+        location_occurrence_counts[(current_path, tag_name)] = (
+            location_occurrence_counts.get((current_path, tag_name), 0) + 1
+        )
         parent_name = ancestors[-1] if ancestors else None
         ...
     suggested_attrs: set[tuple[str, str, str]] = set()
     for location, candidate_groups in suggested_attr_candidates.items():
+        if location_occurrence_counts.get(location, 0) != 1:
+            continue  # multiple elements share this tag/path; don't cross-suppress
         missing_attrs = missing_attrs_by_location.get(location, set())
         ...

The same non-uniqueness applies to the sxsd_unexpected_child suppression via unsupported_tag_locations. Please also add a regression test with two same-tag siblings (one with a typo, one genuinely missing a different/unrelated required attribute) to lock this in.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py` around lines 400 - 503,
Prevent suggestion-based suppression in validate_sxsd_document from applying to
ambiguous repeated elements: track the number of elements for each (path, tag)
location during traversal and only suppress suggested missing-attribute or
unexpected-child schema issues when that location occurs exactly once. Apply the
same uniqueness guard to suggested_attrs and unsupported_tag_locations, and add
a regression test with same-tag siblings where one has a typo and another is
genuinely missing a required attribute, preserving both errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@skills/lark-slides/scripts/xml_text_overlap_lint.py`:
- Around line 400-503: Prevent suggestion-based suppression in
validate_sxsd_document from applying to ambiguous repeated elements: track the
number of elements for each (path, tag) location during traversal and only
suppress suggested missing-attribute or unexpected-child schema issues when that
location occurs exactly once. Apply the same uniqueness guard to suggested_attrs
and unsupported_tag_locations, and add a regression test with same-tag siblings
where one has a typo and another is genuinely missing a required attribute,
preserving both errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f5ce725-cb88-435f-ade0-37ad5e71fa13

📥 Commits

Reviewing files that changed from the base of the PR and between be4b7c5 and 626cf3e.

📒 Files selected for processing (3)
  • skills/lark-slides/scripts/sxsd_validator.py
  • skills/lark-slides/scripts/xml_text_overlap_lint.py
  • skills/lark-slides/scripts/xml_text_overlap_lint_test.py

@BD-ZERO
BD-ZERO merged commit 6e5308a into main Jul 30, 2026
31 of 43 checks passed
@BD-ZERO
BD-ZERO deleted the feat/slides-sxsd-lint branch July 30, 2026 12:04
@liangshuo-1 liangshuo-1 mentioned this pull request Jul 30, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants