Skip to content

Merge main into stable#3458

Merged
harbournick merged 177 commits into
stablefrom
nick/v-1.35.0-main-stable
May 22, 2026
Merged

Merge main into stable#3458
harbournick merged 177 commits into
stablefrom
nick/v-1.35.0-main-stable

Conversation

@harbournick
Copy link
Copy Markdown
Collaborator

No description provided.

christos8333 and others added 30 commits May 7, 2026 18:15
… command parsing

- Updated `launchHost` function to accept an optional `extraArgs` parameter for additional command-line arguments.
- Implemented tests to validate the behavior of the `--request-timeout-ms` flag, ensuring it correctly triggers a timeout error when set to a low value and rejects non-positive integers.
- Introduced `parseHostCommandTokens` function to handle command-line argument parsing, including validation for the `--request-timeout-ms` flag.
- Added end-to-end tests to verify that the SDK correctly propagates the `requestTimeoutMs` option to the host process.
- Enhanced documentation for command usage to include the new timeout option.
…(SD-3212 a0)

The superdoc root entry resolves through four package.json#exports fields
that can diverge:
  - types.import   → dist/superdoc/src/index.d.ts
  - types.require  → dist/superdoc/src/index.d.cts
  - import         → dist/superdoc.es.js
  - require        → dist/superdoc.cjs

This adds a no-growth gate on each source's name set independently, plus a
companion evidence report for the SD-3212 classification pass (PR A1).

What ships:

- snapshot-superdoc-root-exports.mjs: enumerates each source via TS API
  (for .d.ts/.d.cts) or AST/string scan (for the bundled .es.js/.cjs).
- snapshots/superdoc-root-exports.json: canonical baseline. Drift in any
  source name set fails the gate.
- snapshots/superdoc-root-exports.md: human review surface with evidence
  columns per name (presence in each source, fixture import count, JSDoc
  typedef membership, docs/examples/demos mentions, package-boundaries.md
  reference). Regenerated on --write; not a drift gate.
- CI hook in ci-superdoc.yml runs --check after the consumer matrix has
  packed and installed the fixture.

What it gates and what it doesn't:

- gates: drift in any of the four source name sets vs baseline.
- not gates: cross-source mismatches (typed-only / runtime-only / ESM vs
  CJS divergence). Those are reported as evidence for A1, not blockers.
- not gates: docs/examples mention counts. Unrelated docs edits will not
  make the snapshot noisy.

Initial baseline numbers:
  types.import: 200, types.require: 200 (perfect parity)
  import: 41,   require: 41 (perfect parity)
  union: 200
  typed-only: 159 (matches the JSDoc typedef block; classification in A1)
  runtime-only: 0 (no silent shadow on root)

This is PR A0 of the SD-3212 split: inventory + snapshot only. No
src/public/index.ts change, no classification bucket assigned. A1 layers
the bucket column on top of this report; B re-curates the facade; C is
the mechanical root types flip.
PR CI already runs the SD-3212 root snapshot --check. Mirror that into
release-superdoc.yml and release-stable.yml so a release cannot bypass
the new root baseline. Matches the existing SD-3176 legacy-gate pattern
that already runs in all three workflows.
Adds `ui.metadata.getRect({ id })` and `ui.metadata.scrollIntoView({ id })`
keyed on the metadata id (= the value passed to
`editor.doc.metadata.attach`, or the SDT's w:tag underneath). The
handle hides the metadata-id → SDT-node-id → painter-geometry bridge
that the SD-3208 demo had to compose by hand from
`useSuperDocContentControls` + a tag→nodeId map +
`ui.contentControls.getRect`.

Failure mapping reuses the existing ViewportRectResult union:
empty id → `invalid-target`; unknown id (no matching
`properties.tag` in cc.items) → `unresolved`; SDT present but
unpainted → whatever `contentControls.getRect` returns
(`not-mounted` / `not-ready`) propagates as-is.

`scrollIntoView` resolves the id via
`editor.doc.metadata.resolve`, converts the SelectionTarget into a
TextTarget (same-block is one segment, cross-block is two collapsed
endpoints — defensive, since metadata v1 anchors are same-block),
and forwards to `ui.viewport.scrollIntoView`. nodeEdge endpoints
fail with `{ success: false }` rather than approximating.

No `getRects` (`ViewportRectResult.success.rects[]` already
exposes the per-line array), no namespace param (`attach` enforces
globally unique ids), no React hook, no mutation helpers — those
wait for second-customer signal.
…nventory-snapshot

feat(consumer-typecheck): root export 4-source snapshot + drift gate (SD-3212 a0)
…data.* (SD-3204)

Anchored-metadata uses an inline SDT's `w:tag` to mark anchors in
the body, but `w:tag` is not reserved for metadata — an imported
DOCX can carry Word-authored content controls whose tag happens to
match a metadata id. `editor.doc.metadata.resolve` previously
matched on tag alone, so foreign controls would resolve as if they
were metadata anchors and any consumer (including `ui.metadata.*`)
would be steered at an unrelated control.

Fix at the source: `metadataResolveWrapper` now requires both
halves of the anchor — the SDT in the body AND a payload entry in a
customXml part — to agree before returning a non-null result.
Mirrors what `metadata.get` already does for payload reads.

Defensive UI-layer gate: `ui.metadata.getRect` and
`ui.metadata.scrollIntoView` both call `editor.doc.metadata.get`
first and short-circuit on null. Keeps the UI handle symmetrical
for direct callers that bypass `resolve` and protects against the
same class of bug if a future source-side change widens `resolve`.

Tests:
- anchored-metadata-wrappers: foreign SDT with matching w:tag and
  no payload → `metadata.resolve` returns null.
- ui.metadata.getRect / scrollIntoView: same scenario → reports
  `unresolved` / `{ success: false }` without delegating to
  viewport.
`hasMetadataPayload` used `!== null` against an `unknown | null`
structural return type. Production `metadata.get` always returns
null on miss, so this was correct for the runtime path, but a stub
or adapter returning `undefined` would have slipped through.
Switched to `!= null` so both shapes gate the same way.
…adata-geometry

feat(ui): metadata-id geometry on ui.metadata (SD-3204)
Classification of all 200 names in the locked SD-3212 PR A0 root-exports
snapshot. Each name is assigned a bucket with rationale and confidence:

  - supported-root (132): documented public API; first-class root surface.
  - legacy-root (59): real historical API or compat-required type;
    typed for compat, not the recommended root story.
  - internal-candidate (9): accidental implementation leak; not used in
    any exported class/method signature.
  - move-to-subpath (0): no canonical subpath destinations exist today
    for the root candidates (verified during A1).

Rubric: supported-root is documented public API; legacy-root is real
historical API kept typed for compat; internal-candidate is accidental
leak (not in any exported method signature).

Dependency-closure rule applied: a type required to type a supported-root
or legacy-root exported class/method must be at least legacy-root.
Examples surfaced and applied during classification:

  - PresentationEditor.getPages(): LayoutPage[] → LayoutPage,
    LayoutFragment legacy-root.
  - PresentationEditor.onLayoutUpdated payload → LayoutState, Layout,
    LayoutMetrics, FlowBlock, Measure legacy-root.
  - PresentationEditor.getPaintSnapshot() → PaintSnapshot legacy-root.
  - PresentationEditor.setTrackedChangesOverrides → TrackedChangesOverrides
    legacy-root.
  - Editor extends EventEmitter<EditorEventMap> → PaginationPayload,
    ListDefinitionsPayload legacy-root (also EditorConfig.onListDefinitionsChange).
  - Config.layoutEngineOptions: SuperDocLayoutEngineOptions → supported-root.

Out of scope for A1: no src/public/index.ts change, no package.json#exports
change, no root flip. PR B re-curates src/public/index.ts using this
artifact; PR C flips package.json#exports['.'].types after PR B.

Verified locally:
  - All 200 names from the A0 baseline are present in the classification.
  - All buckets are approved enum values.
  - 98 high-confidence, 100 medium-confidence, 0 needs-review.
Enforces the SD-3212 A1 dependency-closure rule mechanically: no
supported-root or legacy-root exported root symbol may reference an
internal-candidate root symbol in its public declared type.

v1 scope (intentionally narrow):
  - Loads emitted root .d.ts via TS compiler API.
  - For each supported-root and legacy-root exported root symbol, walks
    the declared type (properties, union/intersection members, call
    signatures including parameters and return types, type arguments)
    with a bounded-depth visited-set walk.
  - Asserts no referenced root symbol is classified internal-candidate.
  - Supports a manual OVERRIDES set for DOM globals / upstream types /
    generic utility shapes (empty today).

Out of scope for v1:
  - Runtime implementation analysis.
  - Private field walks (TS hides # private fields from getProperties).
  - Cross-package type origins (only asserts within root-exported names).

Caught a real violation on first run:
  SectionMetadata was classified internal-candidate but is the return-
  type member of PresentationEditor.getLayoutSnapshot()
  ({ ..., sectionMetadata: SectionMetadata[] } at line 2744). Manual
  classification missed it because the name also appears in #private
  fields (line 504). Closure walk traced Editor -> PresentationEditor
  -> getLayoutSnapshot return -> SectionMetadata.

Fix applied: SectionMetadata promoted to legacy-root with rationale
referencing the PE.getLayoutSnapshot exposure.

New final v6 distribution:
  supported-root: 132
  legacy-root: 60 (+1 from SectionMetadata)
  internal-candidate: 8 (-1)
  move-to-subpath: 0

Gate wired into ci-superdoc.yml + release-superdoc.yml +
release-stable.yml alongside the existing SD-3176 and SD-3212 A0 gates.

This PR is stacked on the SD-3212 A1 classification PR (#3380) and
should merge after A1.
…rides (SD-3212 a1b)

Per review feedback: the OVERRIDES escape hatch should require a reason
string per entry, not just a symbol name, so any future exception is
reviewable and searchable later via grep.

Changes:
  - OVERRIDES is now Map<symbolName, reasonString> instead of Set<string>.
  - Startup validator aborts (exit 2) if any entry has a missing or too-short
    (<20 chars) reason.
  - When an override is applied during the check, the script logs the symbol
    pair and the reason so the audit trail is visible in CI output.

Empty today. Any addition must land in its own PR with the rationale
visible in the commit message.
…osure-gate

feat(consumer-typecheck): root classification closure gate (SD-3212 a1b)
caio-pizzol and others added 23 commits May 22, 2026 10:40
…changes-remaining-jsdoc

fix(types): type remaining trackChangesHelpers via JSDoc (SD-2980 PR C)
…se 1)

Today, validating the published superdoc public type surface requires
three separate invocations across different working directories:

  pnpm run build:superdoc
  cd tests/consumer-typecheck && node deep-type-audit.mjs --pack --strict-supported-root
  cd tests/consumer-typecheck && node typecheck-matrix.mjs

This wrapper orchestrates them as one command:

  pnpm check:public-contract

Behavior:

- Runs each stage in order, prints a section header per stage
- Fails fast on the first failure so the real error stays visible
- Prints a PASS / FAIL summary with elapsed time at the end
- On FAIL, prints the exact command to re-run the failing stage for
  iteration
- Zero behavior change for the validators themselves; pure DX

Phase 1 of the SD-3256 umbrella (public-contract consolidation +
./super-editor facade curation). Phase 1 deliberately adds no new
enforcement and no config changes; it just collapses three invocations
into one obvious entry point. Phases 2-4 (tier metadata,
./super-editor facade, ratchet) are tracked separately under SD-3256
and need design / team alignment before implementation.

Verified locally: pnpm check:public-contract → PASS (3 stages, 189s)
on post-SD-2980 main. Each stage's exit code is preserved.
…-check-public-contract-wrapper

feat(scripts): add check:public-contract wrapper command (SD-3256 Phase 1)
…e 2)

Adds a `publicContract` section to the canonical
`type-surface.config.cjs`. Every `package.json#exports` subpath now
has a declared tier:

- supported (4): root, ./types, ./ui, ./ui/react — route through src/public/**
- legacy (6): ./converter, ./docx-zipper, ./file-zipper, ./headless-toolbar,
  ./headless-toolbar/react, ./headless-toolbar/vue — route through src/public/legacy/**
- legacy-raw (1): ./super-editor — resolves directly to
  dist/superdoc/src/super-editor.d.ts, NOT through src/public/legacy/**.
  SD-3256 Phase 3 will curate this through src/public/legacy/super-editor.ts
  after team alignment on which exports stay public.
- asset (1): ./style.css — no types
- deprecated (0)

This phase is design-light: it adds metadata + a read-only report
script. NO new enforcement, NO behavior change, NO export changes.
The classification is open for team review before Phase 3 / Phase 4
build on it.

New command:

  pnpm report:public-contract

Prints the tier breakdown plus a cross-check that every
`package.json#exports` subpath has a `publicContract` entry and vice
versa. The cross-check warns but does not fail CI in this phase
(read-only by design); Phase 4 will gate.

Source-of-truth notes in the config explain each tier's intended
policy (supported = strict gate, legacy = no-growth, legacy-raw =
Phase 3 target). Phase 4's ratchet will read these tiers to apply
per-tier enforcement rules.

Verified: pnpm report:public-contract → 12 / 12 entries, cross-check
OK. pnpm check:public-contract → PASS (3 stages, 199s). pnpm run
type-check clean. pnpm run build:superdoc clean.
…-tier-metadata

feat(scripts): add public-contract tier metadata report (SD-3256 Phase 2)
…t (SD-673 Phase 1)

Makes `pnpm check:public-contract` the single official path for
public-type validation, locally and in CI. **No coverage change**:
exactly the same scenarios run; only the orchestration changes.

Wrapper changes:

- Reordered stages so `typecheck-matrix` runs BEFORE `deep-type-audit`.
  Matrix packs and installs the superdoc tarball into the consumer
  fixture as part of its normal run; the audit then reuses that
  install instead of doing a redundant `--pack` of its own. Drops one
  pack + install per invocation. Local full-run time: 199s → 135s.
- New `--skip-build` flag. CI's existing Build step already runs
  `pnpm run build` (= `build:superdoc && type-check`); the wrapper
  must not re-build. Local users get the full from-scratch behavior
  by default.
- Skip output now reports `N ran, M skipped` so CI logs make the
  skip explicit instead of silently dropping a stage.

CI changes (`.github/workflows/ci-superdoc.yml`):

- Removed: `Consumer typecheck (matrix)` step
- Removed: `Deep public-type audit (supported-root strict, SD-3213e)` step
- Added: `Public-contract check (matrix + supported-root strict audit)`
  which runs `pnpm check:public-contract --skip-build`. Single step,
  identical coverage. Net: 10 named CI steps → 9.

Why now: SD-3256 Phase 1 shipped the wrapper but only as a DX tool.
SD-673 Phase 1 makes it the load-bearing CI path so "is the public
contract healthy?" has exactly one command answer, locally and in CI.
Future phases (additional stages, stricter gates) extend one wrapper
instead of duplicating CI yaml.

Verified locally:
  pnpm check:public-contract             → PASS (3 stages, 135.6s)
  pnpm check:public-contract --skip-build → PASS (2 ran, 1 skipped, 97.9s)
Doc-backed operations missing from the CLI response-envelope hint table
were silently serialized under a literal "undefined" property, and the
SDK contract exporter coerced the same missing entry to a null envelope
key, leaking the wrap to SDK callers. Customer hit this on
doc.create.contentControl.

Backfill the four hint maps so every CliExposedOperationId has an entry,
add fail-closed hasOwnProperty guards in both orchestrators and the SDK
exporter, generalize the conformance "no undefined key" check from
doc.history.* to all ops, and add a runtime coverage test (apps/cli does
not run tsc --noEmit in CI, so the type-level exhaustiveness check isn't
enforced).

Follow-up SD-3259 tracks moving from four hand-maintained Records to
derived defaults plus override layers.
Per repo convention, comments shouldn't reference task IDs that go stale.
The why-lines remain; the (SD-XXXX) parentheticals are gone.
…(SD-673 Phase 2)

Disposes the 6 doc-api check scripts that the Phase 0 audit flagged as
"orphan" (not wired into CI). Reading the existing
`packages/document-api/scripts/README.md` reframes the picture: three
of the six are intentionally documented as focused local-debug
variants of `check-contract-outputs` (the per-PR superset). One is
genuinely missing from the wired chain. Two are intended as per-PR
gates but blocked by pre-existing content drift on main.

Phase 2 makes the classification explicit:

- **Per-PR (wired into `docapi:check`)**: `check-contract-parity`,
  `check-contract-outputs`, **`check-examples`** (newly wired here;
  passes on main, low-cost, ensures workflow example headings stay
  documented in `src/README.md`).
- **Focused / manual**: `check-stable-schemas`, `check-agent-artifacts`,
  `check-generated-reference-docs`. Targeted local-debug variants of
  `check-contract-outputs`. Not wired into CI by design (the superset
  already covers their failure modes). Status was already in the
  scripts README; this PR makes the "not in CI by design" framing
  explicit.
- **Per-PR intent, blocked by drift**: `check-doc-coverage` (5 missing
  operation sections in `src/README.md`), `check-overview-alignment`
  (missing alpha + subject-to-change disclaimers in
  `apps/docs/document-api/overview.mdx`). Wire after the drift is
  fixed. Follow-ups: SD-3261, SD-3262.

Net change:

- `docapi:check` now runs 3 scripts (was 2): parity + outputs + examples.
- `packages/document-api/scripts/README.md` gains a "Which checks run
  where" section so the disposition is visible at the call site.
- No deletions; no script removals. The "focused" ones stay as
  documented local-debug tools.

Stacks on #3450 (Phase 1 CI wiring). After #3450 merges and this
lands, "is the doc-api contract healthy?" has exactly one CI-wired
command (`docapi:check`) covering parity + outputs + examples.

Verified: `pnpm run docapi:check` → exit 0 (parity: 403 ops; outputs:
446 files; examples: 5 found).
…check-public-contract-ci

ci(types): collapse matrix + audit CI steps into check:public-contract (SD-673 Phase 1)
The hasOwnProperty guard inside buildEnvelopeData fired AFTER invokeOperation
and any persistence step (exportToPath, writeContextMetadata, session pool
mark-dirty). A drifted hint table would have advanced on-disk state before
the OPERATION_HINT_MISSING error surfaced.

Extract resolveResponseEnvelopeKey into a small shared helper and call it
at the top of both executeMutationOperation and executeReadOperation, before
opening the document. buildEnvelopeData now takes the pre-resolved key
instead of re-indexing the map.

Verified: removing create.contentControl from the hint table now fails the
CLI in ~2ms with no output file written; the prior guard would have
exported the new docx before throwing.
…imers (SD-3262)

`check-overview-alignment.ts` was failing on `main` because the
`apps/docs/document-api/available-operations.mdx` page was missing two
required disclaimers (the script checks for `/\balpha\b/i` and
`/subject to (?:breaking )?changes?/i`). SD-673 Phase 2 deferred this
to a follow-up because it was blocked by content drift.

Changes:

- Add a `<Warning>` block at the top of `available-operations.mdx`:
  "Status: alpha. The Document API surface is stabilizing but is
  still subject to breaking changes between minor versions. Pin a
  version when integrating, and review the changelog before
  upgrading." Honest customer-facing copy (not boilerplate to
  satisfy the script) and satisfies both required patterns.
- Wire `check-overview-alignment` into `docapi:check`. The chain is
  now: parity → outputs → examples → overview-alignment.
- Update `packages/document-api/scripts/README.md`: move
  `check-overview-alignment` from "blocked by drift" to "Per-PR
  (wired)". `check-doc-coverage` stays in the "blocked" bucket but
  the framing is now "blocked by design question" with a pointer to
  SD-3261 (rescoped after the original 5-op drift turned out to be
  348 ops; needs a docs-model decision before wiring).

Note on file path: the original SD-3262 description (and SD-673 Phase
0 audit) referred to `overview.mdx`. The script actually reads
`apps/docs/document-api/available-operations.mdx`; the constant
`OVERVIEW_PATH` in `lib/reference-docs-artifacts.ts` points there.
This PR edits the correct file.

Stacks on #3452 (SD-673 Phase 2). After both merge, `docapi:check`
gates 4 sub-checks; the only remaining orphan is `check-doc-coverage`
under SD-3261.

Verified: `pnpm run docapi:sync:check` → exit 0 (parity 403; outputs
446; examples 5; overview 404 member paths).
Removed alpha status warning from the Document API documentation.
…ew-disclaimers

docs(document-api): wire check-overview-alignment + add status disclaimers (SD-3262)
…docapi-orphans

chore(types): classify doc-api check scripts and wire check-examples (SD-673 Phase 2)
…velope-undefined-key

fix(cli): stop wrapping responses under "undefined" key (SD-3260)
The Document API is live, not in alpha. `check-overview-alignment` was
still requiring `/\balpha\b/i` and `/subject to (?:breaking )?changes?/i`
in `apps/docs/document-api/available-operations.mdx`. That requirement
landed in SD-3262 (#3453) when the API's docs explicitly described it
as alpha. Commit 1154ede removed the alpha warning from the docs
(API is now live), which broke `docapi:check` on main: the script
fails because it expects launch-phase framing that the docs correctly
no longer have.

The fix is to retire the launch-phase requirements, not restore the
warning. Per-PR gates should enforce durable structural correctness
(reference link present, generated markers present, no stale
placeholders, only known `editor.doc.*` paths). They should not force
old product positioning back into the docs.

Changes:

- `check-overview-alignment.ts`: remove `alpha disclaimer` and
  `subject-to-change disclaimer` from `REQUIRED_PATTERNS`. Keep the
  `generated reference link` + generated-marker requirements, the
  forbidden-placeholders list, and the unknown-member-path detection.
- Refresh the file header to describe what the gate actually enforces
  now, and call out that product-status framing is intentionally NOT
  enforced.
- `packages/document-api/scripts/README.md`: update the script-index
  row description so it matches the new behavior (and the correct
  filename `available-operations.mdx`, not `overview.mdx`).

Verified: `pnpm run docapi:sync:check` → exit 0 (parity 403; outputs
446; examples 5; overview 404 member paths).
…up-stop-requiring-alpha

fix(document-api): stop requiring alpha wording in overview check
`check-doc-coverage.ts` required every `OPERATION_ID` (403 today) to
have a dedicated `### \`<opId>\`` section in `packages/document-api/src/README.md`.
That model was wrong for the current architecture:

- Per-operation customer-facing docs are already generated from the
  contract via `pnpm run docapi:sync` → `buildReferenceDocsArtifacts()`
  → `apps/docs/document-api/reference/**` (440 files, 403 per-op pages).
- The generator is unconditional and `OPERATION_REFERENCE_DOC_PATH_MAP`
  has 403 entries with 0 missing.
- `check-contract-outputs` (wired into `docapi:check`) already gates
  that those generated files match the contract.

The manual README catalog only ever covered 50 of 403 operations
(`348 missing` per the script). Closing the gap by hand would duplicate
the generated docs and create two sources of truth. The generated
reference is the catalog; the README is for concepts, workflows, and
contributor notes.

Changes:

- Delete `packages/document-api/scripts/check-doc-coverage.ts`.
- `packages/document-api/scripts/README.md`: remove the script's row
  from the "Script index" table and drop the now-empty "blocked"
  bucket from the "Which checks run where" section (was a 3-bucket
  table; now 2).
- `packages/document-api/src/README.md`: replace the 535-line
  "Operation Reference" section (50 hand-written op subsections plus
  group headers) with a 13-line pointer to the generated reference
  docs, explaining where the catalog lives and how to regenerate.
  README drops from 726 lines to 203.

`check-contract-outputs` remains the per-operation reference-doc
coverage gate.

Verified: `pnpm run docapi:sync:check` → exit 0 (parity 403; outputs
446; examples 5; overview 404 member paths).
…-check-doc-coverage

chore(document-api): retire check-doc-coverage (SD-3261)
@harbournick harbournick self-assigned this May 22, 2026
@harbournick harbournick requested a review from a team as a code owner May 22, 2026 21:07
@github-actions
Copy link
Copy Markdown
Contributor

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@harbournick harbournick merged commit 464faf4 into stable May 22, 2026
83 checks passed
@harbournick harbournick deleted the nick/v-1.35.0-main-stable branch May 22, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants