F1 [WARNING] The 'no invisible coverage hole' guard enumerated only
schema.nodes, so MARK attributes silently escaped the value-fuzz completeness
check — link.internal/target/rel/class are never fuzzed and nothing flagged it,
and a new attributed mark would slip through. Added allSchemaMarkAttrKeys() plus a
MARK_ATTR_FUZZED / MARK_ATTR_ALLOWLIST registry and two tests: every schema mark
attr must be in exactly one set (a new one turns it red), and neither set may hold
a stale row.
F2 [WARNING] The ACCEPTED annotation misclassified table colspan/rowspan as
having 'no md representation'. They DO round-trip — a spanned cell makes the
converter emit the whole table as a raw <table> with colspan/rowspan, which the
tiptap parser reads back. They are frozen only because generating a
geometrically-valid spanned table is deferred PR-2 structural work (the flat
generator hardcodes span = 1), not a markdown limit. Reclassified them as
DEFERRED-BUG (distinct from ACCEPTED) so a maintainer does not read them as an
inherent limitation; colwidth / backgroundColor(Name) stay ACCEPTED (the
raw-<table> fallback drops them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Schema-derived, property-based (fast-check) round-trip tests over flat
single-node ProseMirror documents. One test PR — src/ is untouched; the two
real bugs found are pinned as loud it.fails counterexamples, not fixed here.
- attr-arbitraries.ts: per-attribute four-state arbitraries (absent/default/
nonDefault/degenerate), attribute list sourced from schema.nodes[t].spec.attrs;
a documented override table supplies legal domains for constrained attrs and
distinguishes two frozen classes explicitly — ACCEPTED limitations (no md
representation) vs PINNED bugs (representable but dropped, tracked as
counterexamples).
- text-arbitraries.ts: hostile text corpus (ported from the existing property
test's supported-space guarantees).
- node-generators.ts: flat single-node generators + a completeness contract —
every one of the schema's 45 nodes / 12 marks is either generated or listed in
KNOWN_UNCOVERED with a reason.
- flat-roundtrip.property.test.ts: P1 (semantic round-trip via
docsCanonicallyEqual), P2 (second-pass byte fixpoint — anti GS-EDIT-REVERT),
P3 (totality), generator validity via schema.check(), and an explicit
attribute-value-coverage snapshot so the not-fuzzed set can never grow silently.
- counterexamples: column.width (% dropped on parseFloat -> P2 churn) and
orderedList.start (non-1 start renders as '1.' -> P1 loss) pinned as it.fails.
SEED=20250705, NUM_RUNS=300 per property; ~17s, no OOM (union arbitraries).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>