QuantEcon / QuantEcon/quantecon-plugins.mystmd

Schema coverage gaps and masked negatives left by the contract audit

Open
#23 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
JavaScript
Stars
0
Forks
0
Avg merge
2h 52m
Merged PRs (30d)
7

Description

Nine defects found and deliberately not fixed while landing QuantEcon/quantecon-plugins.mystmd#22, so that the change stayed reviewable. Every one was verified by running the isolation check — deleting the rule a fixture's because names from a scratch copy of the schema and requiring the fixture to then be accepted — rather than taken on report. This is the follow-on to QuantEcon/quantecon-plugins.mystmd#18, which named two of this class; these are the rest, plus the coverage gaps that pass opened.

They are independent of each other. Section 1 is much the largest and is the only one that is a real hole rather than a tidy-up; the rest are coverage gaps and tidy-ups.

1. The anchor-key ban is unenforced on inline content in six of the eight schemas

This is the item worth doing first. The family rule says no node below the root may carry label, identifier or html_id. Measured by stamping the three keys onto text nodes inside each primitive's own valid samples and revalidating:

Schema Inline definition Result
bar-list $defs/noEmbedKeys closed — 0 of 110 positions accept
heatmap $defs/portableNode closed — 0 of 96 positions accept
badges badgeSpan.children has only minItems, no items constraint at all open
chips $defs/inline open
data-table $defs/inlineNode open
delta-list $defs/inlineNode open, 10 of 48 text positions
stacked-bar $defs/inlineNode open
stats $defs/inlineNode open

Fix: mirror heatmap's propertyNames: {not: {enum: [...]}} or bar-list's properties: {label: false, identifier: false, html_id: false} onto the six open definitions. Do not use a bare additionalProperties: false without also admitting key, position and data — the validator's decorate() step stamps those on every node and would then reject every real emitted tree.

One change plus one negative fixture per schema. CONTRACT.md's family rule is already worded so that this binds the emitter wherever the schema does not catch it, so today's prose is honest; what is missing is the coverage.

2. Three masked negatives left in place

Each reads as coverage while a different rule does the work.

samples/data-table.json invalid[22] — "the fallback cell repeats the cell object it renders". The repeated object carries both kind and label, and $defs/tableCell bans each independently. Deleting the kind: false rule leaves it rejected by label: false. Fix: drop the stray label so only the kind ban can fire, then annotate rejectedAt /children/0/children/1/children/0/kind. The knock-on required: header errors suggest the repeated object may also be missing header, which wants fixing in the same edit.

samples/chips.json invalid[4] and invalid[5] — class token order, and a class repeating the qe-dv family token. Both name properties/class/pattern; with that keyword deleted both are still rejected by the variant/class-agreement rule that invalid[8] already owns. Fix: give each a class satisfying the variant-agreement rule so only the ordering or the duplicated token remains, then annotate rejectedAt /class. If the two patterns overlap too far for that, delete both as duplicates of invalid[8] and say so.

samples/chips.json invalid[12] — "items is non-empty but the fallback list has no items". Names $defs/fallbackList's children.minItems; with that deleted it is still rejected by $defs/chipCountFollowsItems. The two rules cannot be separated, because an empty fallback list is always a count disagreement. Fix: re-scope the because onto the count rule and accept the duplication, or delete the fixture — invalid[13] and invalid[14] already cover the count rule, so deletion with a note is the better answer.

3. Coverage gaps opened by the fixes in QuantEcon/quantecon-plugins.mystmd#22

schema/badges.json $defs.dumbInnerNode — re-scoping invalid[17] left identifier: false and html_id: false with no fixture of their own; invalid[16] exercises the rule through label alone. Decide explicitly whether one fixture per anchor key is wanted, or whether label standing for all three is enough, and record the decision either way.

schema/badges.json $defs.badgeSpan.properties.class.pattern — correcting invalid[5]'s class removed its incidental coverage, so nothing now rejects a badge span whose only defect is its class. A fixture cannot carry a meaningful rejectedAt here, because $defs.row's oneOf makes the losing branch report at the same instancePath. Fix: add it unannotated with a because that says why, or narrow that oneOf so branch noise stops colliding.

minLength: 1 on the root anchor keys — added to schema/bar-list.json and schema/chips.json with no fixture in either. One fixture per file: root carrying all three keys with one set to the empty string, annotated rejectedAt /label. Verified isolatable.

4. Two tidy-ups

schema/stats.json carries dependentRequired bare on the root, where every other cross-property invariant in that file is a named $def. Closed as won't-fix, 2026-09-10. Reviewed adversarially while addressing Copilot's anchor findings on QuantEcon/quantecon-plugins.mystmd#22. Both idioms were built out in full and measured: they compile identically under ajv strict, accept and reject every fixture identically, and report a failed dependency at the same instancePath"", the containing object's own path — so the split is unobservable. Normalising it would delete roughly 12,000 characters of per-primitive reasoning in favour of eight copies of one paragraph, and a deliberately degraded copy of a shared $def still passed the suite, so uniformity would not have prevented drift either. The rule now goes wherever a schema already declares the anchor keys, and it is enforced by a behavioural check in scripts/validate-contract.mjs that is idiom-blind and, living outside schema/, is not frozen at v1.0.0.

schema/bar-list.json properties.columns.description uses a bare double hyphen where every other description in the file uses an em dash. One character.

Not a defect, but the backlog behind all of this

Roughly ninety invalid fixtures across the eight sample files still carry no rejectedAt. The validator treats an unannotated fixture exactly as it did before, so nothing is broken — but the annotation is what turns a one-off manual audit into a standing regression guard, and until a fixture has one, nothing stops a later schema edit moving its rejection onto a different rule. Worth working through primitive by primitive rather than in one pass.

One annotation is worth narrowing while doing that: samples/bar-list.json invalid[17] reports forty distinct instancePaths, because a label on the fallback table fails the root oneOf and both branches then report their whole subtree. Its annotation bites, but a regression moving the rejection onto any of the other thirty-nine would pass. The fix there is a narrower fixture, not a better annotation.

🤖 Generated with Claude Code

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the six open inline definitions in schema/.json and their related samples/.json fixtures, then review the isolation check described in the audit. Use scripts/validate-contract.mjs and the named schema fixtures to verify each rule is independently covered. Done means the anchor-key gaps, masked negatives, and selected coverage gaps are resolved or explicitly documented, without changing the closed item.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, json
Domain
testing, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.