Skip to content

RR-55: short-verb API, one validate, Finding schema, stats (core 0.14.0, breaking) - #423

Merged
michaeljgrimm merged 1 commit into
mainfrom
codex/rr-55-short-verb-api
Oct 7, 2026
Merged

michaeljgrimm merged 1 commit into
mainfrom
codex/rr-55-short-verb-api

Conversation

@michaeljgrimm

@michaeljgrimm michaeljgrimm commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Depends-On: OpenPresentation/opf-render#150
Depends-On: OpenPresentation/opf-pptx#196
Depends-On: OpenPresentation/opf-editor#116

Summary

RR-55 for core 0.14.0 (breaking, no deprecation, no aliases, no stubs): the whole API cleanup in one PR.

  • One checker. validate(input, options?) replaces validatePresentation, lintSource, lintPresentation, auditSource and auditPresentation; the report is { valid, findings, counts, schemaValid, checks } with the new Finding format (spec/schemas/finding.schema.json). opf validate is the only checking command (opf lint, opf audit and --strict are gone; --fail-on replaces it). Part A1.
  • stats and one slide context. stats(presentation) and opf stats report neutral facts, never severities. resolveSlideContext(presentation, index, { fonts }) is the one place the slide, deck, theme, font-scheme chain is resolved: core pagination and validate's layout rules use it, so no hand-built chain is left in core. ComposeSlideOptions.fonts is now fontFamilies; deck-level verbs take { fonts }. The package root keeps app-level names: 235 engine names (values and types) moved to /composition and /symbol-font-encodings. Part A3.
  • Short verbs. paginatePresentation is paginate, bundlePresentation is bundle, formatPresentation is format, diffPresentations is diff, mergePresentations is merge, markdownToOpf is fromMarkdown, opfToMarkdown is toMarkdown, createDataContent is importData, excelNumberFormat/numberFormatFromExcel are toExcelNumberFormat/fromExcelNumberFormat, PatchError/PatchValidationError/FormatError are OPFPatchError/OPFPatchValidationError/OPFFormatError. Result fields and options named document (markdown result, applyPatchWithInverse result, /convert option) are presentation. The CLI verbs are also exported from the root. Part A2 (this branch merges A1 and A3 and does the sweep).

No migration guide (there are no users yet): the renamed and removed names are listed in the changes/rr-55-*.md fragments. Validation guide: docs/validate.md. Stats: docs/stats.md.

Ordering

One commit on main after the FA integration (opf#429, eb4a130a). It queues alone, with the lockstep sibling PRs named in the Depends-On lines above; a release-prep PR then cuts 0.14.0 for every package. check:breaking compares only spec/ (the minor bump allows its breaking changes).

Restacked onto the format-audit integration (opf#429)

One commit on top of main after the FA integration squash-merged (eb4a130a, opf#429): RR-55's seven commits squashed (the branch had a merge of A1 and A3), with every FA addition folded in. There is no migration guide: docs/migrations/0.14.0.md is deleted (owner: no users yet) and the renamed and removed names are listed in the changes/rr-55-*.md fragments; the FA fragments are rewritten to the final 0.14 names. The release-prep to-do list that lived in the guide (CLI peer calls, registry-pinned scripts) is in the release-readiness progress log.

FA fold decisions (vetoable):

  1. Placement (FA-EXPORTS). /composition: layoutContent, LAYOUT_BODY_KINDS, resolveColorRoles, isDarkColor, defaultSlideBackground, DARK_BACKGROUND_LUMINANCE, surfaceAltColor, SURFACE_ALT_MIX, SURFACE_ALT_MIN_CONTRAST, timelineMarkerShapes, timelineTextColor, TIMELINE_*, codeHighlightLines, codeHighlightBands, codeHighlightColors, codeLineNumbers, layoutWatermark, chartHighlightMarks, chartHighlightColors, CHART_HIGHLIGHT_*, resolveDesignHints, DESIGN_HINT_KEYS. Root: listBuiltinVariables (BuiltinVariableInfo), normalizeLanguageFamily (LanguageFamilyName), ChartComboSeries. Types follow their module (QuotePhoto, MetricSentiment, CodeHighlight*, WatermarkText*, TimelineStatus, ... on /composition). Internal (no engine imports them, checked in the three restacked siblings): codeHighlightList, codeHighlightSlice, codeHighlightContrast, codeLineCount, CODE_HIGHLIGHT_DIM, CODE_HIGHLIGHT_BAND_TINT, WATERMARK_TEXT_*.
  2. New findings. format errors: opf/variable-unknown-builtin, opf/chart-highlight-unknown-name. references warnings: opf/unknown-beat, opf/slide-theme-dimensions. content warnings: opf/variable-builtin-missing, opf/chart-highlight-adapted, opf/code-highlight-out-of-range, opf/code-highlight-range-reversed, opf/duration-outside-narrative, opf/narrative-duration-range; content info: opf/unused-beat. 64 rules in all; opf/chart-data-source-unresolved is gone with ChartDataSource. Deviation from FA-EXPORTS: chart-highlight-unknown-name is format, not references, because it is a validator error like its sibling chart-mapping-unknown-column (format since A1), and validate(deck, { only: ['format'] }) must keep answering exactly what the format check answered. The FA-04 deck-metadata errors (duplicate speaker or organization id, an organizationId that names nothing, a variable named speakers) stay opf/schema (format) for the same reason. slide-theme-dimensions is references: the document is well-formed, the mismatch only exists through the theme record the slide names.
  3. FA-17 and resolveSlideContext. FA moved titleAlignment, contentAlignment and contentBox out of the composition options: composeSlide resolves them (and every other design hint) from the slide, presentation.design and the layout record and returns them as SlideComposition.design. resolveSlideContext no longer sets them, exactly like FA's own pagination and audit chains, so the renderer, the exporter and the editor get the same options FA's engines built (canvas, layout, presentation, slide index/number/count, font families, darkBackground, social platforms, measurement, date). darkBackground is resolveColorRoles(scheme, { background }).dark, the one rule FA-05 made shared, so it cannot disagree with the card surface the engines take from resolveColorRoles.
  4. FA-05 colour resolution wins. RR-55's own ColorRef background resolution (b489738) is replaced by FA-05's resolveColorRoles/defaultSlideBackground path in rule-design.ts; the duplicate helpers are deleted.
  5. RR-56 /yaml folds into fromYaml + validate. fromYaml(text, { aliases, validate }) returns { presentation, valid, findings, counts, schemaValid, checks } (the validate report of the deck, every finding located in the YAML; validate takes true, false or ValidateOptions, like fromMarkdown). lintYamlSource and yamlLocator are deleted; YamlDiagnostic is YamlFinding; toYaml validates the format only and its invalid-document error carries findings. YAML syntax errors are yaml/<rule> findings of category format.
  6. CLI. opf lint and opf audit stay removed; opf validate reads YAML (a .yaml/.yml name or --input-format yaml) through fromYaml, reports a YAML syntax error as a yaml/<rule> finding and exits 1, like invalid JSON. render, export and import check YAML through the same path. from-yaml takes --fail-on instead of --strict. FA-08's --include-hidden and deck-named output files are kept.
  7. Core packed smoke. packed-install-smoke.mjs installs the release plan's published renderer, editor and PPTX next to the packed core; they target core 0.13 and cannot run with 0.14 (the editor imports PatchError). The smoke now skips them when the plan's core is on an older minor line and says so; test:packed-ecosystem checks the candidate siblings, and the release-prep PR that moves the plan turns the check back on. (Without this, CI's "Verify packed package install" could not pass before release.) Also fixed: the smoke's patch/diff snippet shadowed diff after the rename.

Vetoable calls

Part A2 (integration and sweep):

  1. opf/variable-unfilled is a format rule, not content: an error in a deck that is not a template, a warning in a template. So validate(deck, { only: ['format'] }), assertValid, pagination, patch validation and every write command (opf fill, paginate, edit, create) reject an unfilled required variable exactly as validatePresentation did (new tests in core and the CLI). Consequence: the stray {{token}} warning (a deck that declares no variables but has {{name}} text) is the same rule, so it now shows in format-only runs too, as a warning.
  2. No migration guards. The TypeError guards for a leftover fonts option on composeSlide/layoutX and a top-level textMeasurement on paginate/paginateSlide are deleted, with their tests. Unknown options are not read. Risk: a sibling site that still passes fonts: { heading, body } to composeSlide degrades silently to default families; each sibling PR has to carry a test that composition uses the resolved families.
  3. Types keep Presentation where the short name is taken by the slide type: PresentationPaginationOptions, PresentationPaginationResult, PresentationDiff, PresentationStats. Renamed types: MarkdownToOpf{Options,Result} to FromMarkdown{Options,Result}, OpfToMarkdown{Options,Result} to ToMarkdown{Options,Result}, DataContentOptions to ImportDataOptions.
  4. document to presentation covers result fields and options only (markdown result, applyPatchWithInverse result, ConvertOptions). Not renamed: error codes invalid-document and patch-invalid-document, Finding scope: "document", and parameter names of the generic JSON Pointer helpers.
  5. The CLI's peer-facing calls (prepareNodeFonts, the document key of fromPptx(..., { signals: true })) are unchanged here: the pinned peers (render 0.12.2, PPTX 0.12.4) still have them, and the CLI moves with the peer bump in the release-prep PR.
  6. Registry-pinned scripts keep the old names until the 0.14.0 release-prep; the list is in the release-readiness progress log (2026-10-06 entry).
  7. Internal CLI locals named lint (in opf import/opf render) renamed to check; A1's internal slide-rule type is RuleSlide and its module rule-slides.ts, so SlideContext is only the public resolveSlideContext type.

Part A1 (checker):

  1. only, ignore, severity and ignorePaths take full rule ids, bare names (text-contrast) or category names; an unknown name, option or threshold throws a TypeError, so a typo cannot silently disable a check. Old rule ids (audit/..., json/...) are not accepted.
  2. A string input is strict JSON text only; Markdown and YAML are never sniffed (a hint points at fromMarkdown/fromYaml). Invalid JSON text is an invalid document (exit 1, opf/json-syntax), no longer a usage error.
  3. assertValid(value, options?) takes a parsed presentation; a string is a TypeError. OPFValidationError carries the report and the error findings.
  4. An authored overflow: "error" is a warning in opf/text-overflow, so valid never depends on font metrics; render and export still refuse such a slide.
  5. ValidateFonts.textMeasurement may also be a function of the slide index (per-slide script-aware measurement).
  6. The catalog ids of a kind with an external catalogs.<kind>.source are not judged unless the host passes that source's records (the lint report warned; validatePresentation did not).
  7. checks also states three fixed limits (backgroundPixels: "not-read", imageBytes: "embedded-only", nativeExport: "not-checked").
  8. The four judgment rules (font-family-count, slide-word-count, title-position, small-cell) and their thresholds are deleted; audit/invalid-document is gone (a schema failure reports its format findings with schemaValid: false).

Part A3 (stats and context):

  1. stats reads the JSON only (no layout, no fonts, no validation) and estimates speaking time from notes words at 130 wpm, labelled estimate; values and wordsPerMinute are its options.
  2. resolveSlideContext never throws for an unresolved layout, theme or font scheme (it returns a diagnostic and the documented fallback); it throws a RangeError for an index outside the deck.
  3. The slideOptions hook of pagination is removed (nothing used it).
  4. /composition stays catalog-free; resolveSlideContext lives at the root because it loads catalogs.

Cost

Measured by A1 on bundled decks with Node 24: only: ['format'] 0.06 to 0.64 ms, ['format', 'references'] 0.7 to 7.6 ms, everything 1.7 to 32 ms (6 to 10 slide decks: about 24 to 32 ms full, 0.2 to 0.6 ms format-only); the proposal's "about 11 ms" is not used anywhere. See the table in docs/validate.md.

Test results

Local, Windows, Node 24.21, pnpm 10.33, on 0c488217, with the restacked siblings linked (render 4cf2529, pptx d0dbfee, editor 3f89734):

  • pnpm build: ok. pnpm test: core 2065/2065, CLI 91/91, root checks 26/26 (including check:spec, check:schema-docs and the gallery font checks).
  • pnpm lint: 0 errors (557 warnings). typecheck: ok. check:breaking: exit 0 against 0.13.0. check:examples: 0 failures. test:skills: 6 skills, 17 helper checks. check:release-report: 8/8. test:cli:packed: 92 command checks.
  • packages/javascript/test/packed-install-smoke.mjs: passes (the release plan's published 0.13 siblings are skipped, see the fold section).
  • Full unsharded node scripts/test-package-ecosystem.mjs (every plan step, with OPF_GOLDEN_BASELINE set to scripts/fixtures/opf-examples-png.fa-0-14.sha256.json, the renderer's golden-override that CI adopts): exit 0. Render 86 tests and golden 807 slides / 127 decks, editor 60, pptx 105, font switch matrix 61/61, test:packed-ecosystem (local tarballs) and test:cli:packed pass.
  • node scripts/test-packed-ecosystem.mjs --registry: pass (all five release-plan versions).

New tests: root exports (every CLI verb is a root function and the binding of its subpath, old names absent, presentation result fields), validate uses the resolveSlideContext families and the fonts measurement (and builds no layout for only: ['format']), opf/variable-unfilled is a format error for a deck (core and CLI: validate --only format, create, edit, paginate, fill) and a warning for a template.

🤖 Generated with Claude Code


Note

High Risk
Breaking rename and removal across core, CLI, and docs with no migration aliases; hosts must adopt validate, { fonts }, /composition imports, and lockstep sibling releases.

Overview
RR-55 (core 0.14.0) unifies validation, renames the public API to short verbs, and splits the package surface so app-level helpers stay on the root and layout/composition engines move to subpaths.

One checker: validate(input, options?) replaces validatePresentation, all lint* / audit* entry points, and assertValidPresentation → assertValid. Reports use a shared Finding shape (valid, findings, counts, schemaValid, checks) with stable opf/<rule> ids and six categories. ./lint and ./audit are removed; docs/audit.md is dropped in favor of docs/validate.md and docs/finding-schema-reference.md. CLI: only opf validate (opf lint / opf audit gone; --strict → --fail-on).

Renames (no aliases): paginatePresentation → paginate, bundlePresentation → bundle, formatPresentation → format, diff/merge, fromMarkdown/toMarkdown, importData, Excel format helpers toExcelNumberFormat/fromExcelNumberFormat, error types OPF*Error, and result/option document → presentation. README and docs sweep old names to validate and the new verbs.

New APIs: stats(presentation) / opf stats for structural deck facts; resolveSlideContext centralizes slide/theme/layout/font resolution for pagination and layout rules. ComposeSlideOptions.fonts → fontFamilies; paginate / paginateSlide take { fonts } (not top-level textMeasurement). ~235 composition symbols move from the root to @openpresentation/opf/composition (and symbol encodings to its subpath).

YAML: fromYaml returns the deck plus the same validate report; lint-yaml helpers removed. FA change notes are updated so audit/lint wording points at validate and new rule ids.

Reviewed by Cursor Bugbot for commit 2c6c2eb. Configure here.

michaeljgrimm added a commit to OpenPresentation/opf-pptx that referenced this pull request Oct 7, 2026
…ndings)

Lockstep with OpenPresentation/opf#423. Exported PPTX bytes are unchanged
(414 A/B exports of 138 decks and the determinism fixtures hash identically).

- toPptx takes { fonts } (the fonts handle; core's Fonts) and no longer reads a
  top-level textMeasurement.
- fromPptx(bytes, { signals: true }) resolves to { presentation, signals }.
- OPFPptxError carries findings (and report) or diagnostics, not issues.
- inventoryPptxTypefaces/checkPptxTypefaces are inventoryTypefaces/checkTypefaces
  and checkTypefaces takes { families }; FontSchemeDiagnostic comes from core.
- Each slide is composed with the options of core's resolveSlideContext instead
  of a hand-built copy; an unresolved theme is reported as unresolved-theme.
- validatePresentation becomes validate(x, { only: ['format'] }); the engine
  names import from @openpresentation/opf/composition; the fallbacks for older
  cores (number-format stubs, feature detection, missing script-font and chart
  option paths) are removed.
- New tests: core-features (every core name src imports exists) and
  slide-context (composition uses the deck's resolved font families).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@michaeljgrimm
michaeljgrimm force-pushed the codex/rr-55-short-verb-api branch from 47cd47f to 0c48821 Compare October 7, 2026 06:20
michaeljgrimm added a commit to OpenPresentation/opf-pptx that referenced this pull request Oct 7, 2026
…ndings)

Lockstep with OpenPresentation/opf#423. Exported PPTX bytes are unchanged
(414 A/B exports of 138 decks and the determinism fixtures hash identically).

- toPptx takes { fonts } (the fonts handle; core's Fonts) and no longer reads a
  top-level textMeasurement.
- fromPptx(bytes, { signals: true }) resolves to { presentation, signals }.
- OPFPptxError carries findings (and report) or diagnostics, not issues.
- inventoryPptxTypefaces/checkPptxTypefaces are inventoryTypefaces/checkTypefaces
  and checkTypefaces takes { families }; FontSchemeDiagnostic comes from core.
- Each slide is composed with the options of core's resolveSlideContext instead
  of a hand-built copy; an unresolved theme is reported as unresolved-theme.
- validatePresentation becomes validate(x, { only: ['format'] }); the engine
  names import from @openpresentation/opf/composition; the fallbacks for older
  cores (number-format stubs, feature detection, missing script-font and chart
  option paths) are removed.
- New tests: core-features (every core name src imports exists) and
  slide-context (composition uses the deck's resolved font families).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@michaeljgrimm
michaeljgrimm marked this pull request as ready for review October 7, 2026 06:21
@cursor

cursor Bot commented Oct 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 497e8a3d-702e-4419-9496-e3f91bdae259)

@michaeljgrimm
michaeljgrimm force-pushed the codex/rr-55-short-verb-api branch from 0c48821 to 43d051c Compare October 7, 2026 06:34
@cursor

cursor Bot commented Oct 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 8b944de7-a41d-45d0-89cf-9fa06407e3eb)

….0, breaking), on the FA 0.14 integration

RR-55 for core 0.14.0, restacked onto codex/fa-0-14-integration (opf#429) as one
commit (the branch merged its parts A1 and A3). No deprecations, aliases or
shims, and no migration guide: the renamed and removed names are listed in the
changes/rr-55-*.md fragments.

- One checker: validate(input, options?) with the shared Finding format
  (spec/schemas/finding.schema.json), six categories, opf/<rule> ids; opf
  validate is the only checking command and --fail-on replaces --strict.
- Short verbs: paginate, bundle, format, diff, merge, fromMarkdown, toMarkdown,
  importData, to/fromExcelNumberFormat, OPF*Error classes; result fields and
  options named document are presentation.
- stats and opf stats; resolveSlideContext; the fonts handle
  (ComposeSlideOptions.fonts is fontFamilies); engine names on /composition.
- Unresolved layout, theme and colour scheme ids fall back and never throw.

FA fold:
- FA exports placed per FA-EXPORTS (engine helpers on /composition;
  listBuiltinVariables and normalizeLanguageFamily on the root; FA-13 helpers
  no engine imports stay internal).
- Every FA rule or diagnostic is an opf/<id> finding with a category (64
  rules); opf/chart-data-source-unresolved is gone.
- resolveSlideContext follows FA-17 (design hints are resolved by composeSlide,
  not passed as options) and FA-05 (darkBackground from resolveColorRoles).
- RR-56 /yaml: fromYaml returns the deck and its validate report with located
  findings; lintYamlSource and yamlLocator are folded into fromYaml + validate.
- CLI: opf validate reads YAML; from-yaml takes --fail-on.
- The core packed smoke skips the release plan's published siblings while the
  candidate core is on a newer minor line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michaeljgrimm
michaeljgrimm force-pushed the codex/rr-55-short-verb-api branch from 43d051c to 2c6c2eb Compare October 7, 2026 07:23
@cursor

cursor Bot commented Oct 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 98442b17-2bcd-4f71-bdf4-05508296183a)

@michaeljgrimm
michaeljgrimm added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit f1e7d4c Oct 7, 2026
17 checks passed
michaeljgrimm added a commit to OpenPresentation/opf-pptx that referenced this pull request Oct 7, 2026
…196)

* RR-55: adopt the core 0.14 API (fonts handle, resolveSlideContext, findings)

Lockstep with OpenPresentation/opf#423. Exported PPTX bytes are unchanged
(414 A/B exports of 138 decks and the determinism fixtures hash identically).

- toPptx takes { fonts } (the fonts handle; core's Fonts) and no longer reads a
  top-level textMeasurement.
- fromPptx(bytes, { signals: true }) resolves to { presentation, signals }.
- OPFPptxError carries findings (and report) or diagnostics, not issues.
- inventoryPptxTypefaces/checkPptxTypefaces are inventoryTypefaces/checkTypefaces
  and checkTypefaces takes { families }; FontSchemeDiagnostic comes from core.
- Each slide is composed with the options of core's resolveSlideContext instead
  of a hand-built copy; an unresolved theme is reported as unresolved-theme.
- validatePresentation becomes validate(x, { only: ['format'] }); the engine
  names import from @openpresentation/opf/composition; the fallbacks for older
  cores (number-format stubs, feature detection, missing script-font and chart
  option paths) are removed.
- New tests: core-features (every core name src imports exists) and
  slide-context (composition uses the deck's resolved font families).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* RR-55: no layout record for a layout-less slide; unknown ids never throw

A slide with no layout, or an unknown layout id, composes with no layout record
(automatic composition, as in the preview). Theme, colour scheme, layout, font
scheme and darkBackground come from core's resolveSlideContext; its unresolved-*
diagnostics are forwarded and catalog-resolution-failed is gone. The exporter's
own theme and colour-scheme record resolution and the blank default are removed.

Not yet run against the core commit 7808c7ec (the shared core checkout has to be
rebuilt first).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* RR-55: FA tests follow the 0.14 names (validate, renderSlideSvg, loadFonts, /composition engine names)

The format-audit tests used validatePresentation, the slide-level renderSvg, prepareNodeFonts and root imports of engine names; core-features.mjs also lists the FA core functions the exporter now imports statically.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* RR-55: default font scheme from core in importedCodeFamily; drop the unreachable watermark-not-exported emit

DEFAULTS no longer exists, so importedCodeFamily threw on every fromPptx call; it reads core's DEFAULT_FONT_SCHEME. layoutWatermark returns null only for text that draws nothing, which now skips silently (FA-07 removed the diagnostic).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant