openpencil/crates
Kayshen-X fb2d3b1a8d fix(shell-core): Step 2 codex R2+R3 — empty-pages validate gap
Two iterative tightenings on Document::validate after the R1
fixes landed in 3d291ec8.

# R2 CONCERN-1: empty-pages document silently passed validate

`Document::validate` previously gated the active_page_index
range check on `!pages.is_empty()`, so a `Document { pages:
vec![], active_page_index: 99, ... }` returned `Ok(())` —
inconsistent with the implicit "every Document has at least one
page" invariant that Document::empty() and Document::sample()
both establish.

Fix:
- `validate()` now treats `pages.is_empty()` as the FIRST
  violation it returns. Empty pages is itself an invariant
  violation — `Document::empty()` is the constructor for the
  default single-page shape.
- `active_page_index` range check now fires unconditionally.

New test `document_validate_catches_empty_pages` covers two
sub-cases:
- `pages: vec![], active_page_index: 0` → Err("pages is empty")
- `pages: vec![], active_page_index: 99` → Err (empty check
  fires first, range check short-circuited)

# R3 CONCERN: empty-vs-range ordering not asserted

The R2 second sub-case only asserted `.is_err()` without
proving WHICH violation fired first. Strengthened to:
- assert error contains "pages is empty"
- assert error does NOT contain "active_page_index"
Both asserts carry failure messages so a future regression
points at the cause.

Test count: 34 lib + 21 widgets_static + 6 jian + 4
render_backend = 65 shell-core tests passing.

R4 GO from codex.
2026-05-10 11:05:53 +08:00
..
openpencil-app feat(openpencil-app): skeleton crate (Stage F entry placeholder) 2026-05-03 22:05:00 +08:00
openpencil-shell-core fix(shell-core): Step 2 codex R2+R3 — empty-pages validate gap 2026-05-10 11:05:53 +08:00
openpencil-shell-native fix(shell): Step 2 codex R1 — sentinel + page-scope + clamp + overflow doc 2026-05-10 10:53:59 +08:00
openpencil-shell-web fix(shell): Step 2 codex R1 — sentinel + page-scope + clamp + overflow doc 2026-05-10 10:53:59 +08:00
pen-codegen chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-core chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-engine chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-figma chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-types feat(pen-types): skeleton crate (bucket A) 2026-05-03 22:35:00 +08:00
wasm-libc-shim feat(shell): add wasm-libc-shim crate for wasm32-unknown-unknown skia bundle 2026-05-09 21:07:00 +08:00