Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/package-impact-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Source of truth for which repo paths should trigger CI and release workflows for
- **CI gates compatibility.** A change to SuperDoc core should run the CI of every dependent package — that's how breakage in `@superdoc-dev/react` or `@superdoc-dev/sdk` gets caught before it ships. CI paths follow *compatibility* impact.
- **Release gates artifact changes.** A package should only publish a new version when its own published artifact actually changes. Release paths follow *artifact* impact.

These two are not the same. `template-builder` and `esign` externalize `superdoc` in their builds and declare it as a `peerDependency`, so a core change doesn't change their tarballs → CI broad, release narrow. CLI bundles core into platform binaries, so a core change does change the CLI tarball → both broad.
These two are not the same. `template-builder` and `esign` externalize `superdoc` in their builds and declare it as both a dependency and peer dependency, so a core change inside the declared range doesn't change their tarballs → CI broad, release narrow. CLI bundles core into platform binaries, so a core change does change the CLI tarball → both broad.

## Surfaces

Expand Down Expand Up @@ -49,7 +49,7 @@ These two are not the same. `template-builder` and `esign` externalize `superdoc

## Why each classification

- **`template-builder` and `esign`** externalize `superdoc` in their Vite build (`rollupOptions.external`) and declare it as a `peerDependency`. A SuperDoc core change does not change the wrapper's published bundle — consumers receive the new core through their own `npm install`. Release-on-core is pure version noise; CI-on-core remains necessary to catch breaking API changes.
- **`template-builder` and `esign`** externalize `superdoc` in their Vite build (`rollupOptions.external`) and declare it in **both** `dependencies` and `peerDependencies`. The `dependencies` entry preserves auto-install for customers who use the wrapper as their SuperDoc entrypoint; the `peerDependencies` entry signals the singleton contract for apps that also install SuperDoc directly. A SuperDoc core change inside the declared range does not change the wrapper's published bundle or manifest, so release-on-core is pure version noise; CI-on-core remains necessary to catch breaking API changes.
- **`react`** externalizes `superdoc` in its Vite build the same way, and declares `superdoc` in **both** `dependencies` and `peerDependencies`. The `dependencies` entry preserves auto-install for every consumer (zero-break regardless of package manager); the `peerDependencies` entry signals the singleton contract and aligns the manifest with template-builder/esign. Because the `dependencies` entry still pins via lockfiles, existing consumers only pick up a new core version when react republishes, so release-on-core stays correct *today*. The unlock for release-narrow is to remove `superdoc` from `dependencies` entirely — that is a breaking change and tracked as a separate decision.
- **CLI / SDK** bundle engine behavior into platform-specific native binaries (see `apps/cli/.releaserc.cjs` and `packages/sdk/.releaserc.cjs` — both use `patch-commit-filter.cjs` to expand release analysis into core paths). The published artifact genuinely changes when core changes.
- **MCP** depends on SDK via `workspace:*` and imports engine/session code directly. Its current release trigger (`apps/mcp/**` only) causes it to lag SDK releases. Expand to match SDK's release paths.
Expand All @@ -61,6 +61,6 @@ These two are not the same. `template-builder` and `esign` externalize `superdoc
## Notes

- `packages/ai/**` has been removed from all release and CI triggers. `@superdoc-dev/ai` is being deprecated; npm-side deprecation is a separate operational step.
- When SuperDoc core ships a breaking API change, `template-builder` and `esign` must be manually updated and released. Their `peerDependencies` version bump is the signal; semantic-release won't auto-trigger on upstream changes for them.
- `@superdoc-dev/react` declares `superdoc` in both `dependencies` and `peerDependencies` to preserve zero-break install semantics while still signaling the singleton contract. Removing `superdoc` from `dependencies` is the unlock for release-narrow and is tracked as a separate decision.
- When SuperDoc core ships a breaking API change, `template-builder` and `esign` must be manually updated and released. Their dependency and peer-dependency floor bump is the signal; semantic-release won't auto-trigger on upstream changes for them.
- `@superdoc-dev/react`, `@superdoc-dev/template-builder`, and `@superdoc-dev/esign` declare `superdoc` in both `dependencies` and `peerDependencies` to preserve automatic install semantics while still signaling the singleton contract. Removing `superdoc` from `dependencies` is the unlock for release-narrow and is tracked as a separate decision.
- When editing a release or CI workflow, its `paths:` filter must match the corresponding row in this map. Workflow-lint rules should enforce this.
61 changes: 61 additions & 0 deletions .github/workflows/promote-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
if git merge --no-ff --no-edit "origin/${SOURCE_BRANCH}"; then
MERGE_STATUS="clean"
else
<<<<<<< HEAD
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve conflict markers in promote-stable workflow

This workflow file includes unresolved merge markers, which makes the YAML invalid for GitHub Actions parsing. In that state, promote-stable.yml will not load when triggered, so stable promotion automation is broken until the conflict is resolved.

Useful? React with 👍 / 👎.

# Auto-resolve release artifact conflicts: keep stable's version.
# npm/PyPI already have stable's published version; downgrading to
# main's track would break the next stable release. Mirrors the
Expand All @@ -84,6 +85,62 @@ jobs:
;;
esac
done
=======
normalize_json_without_keys() {
python3 -c 'import json, sys; data = json.load(sys.stdin); [data.pop(key, None) for key in sys.argv[1:]]; sys.stdout.write(json.dumps(data, sort_keys=True, separators=(",", ":")))' "$@"
}

release_artifact_only_conflict() {
local f="$1"
local tmpdir
local status
tmpdir="$(mktemp -d)"

case "$f" in
package.json|*/package.json)
git show ":2:${f}" | normalize_json_without_keys version > "${tmpdir}/ours" || { rm -rf "${tmpdir}"; return 1; }
git show ":3:${f}" | normalize_json_without_keys version > "${tmpdir}/theirs" || { rm -rf "${tmpdir}"; return 1; }
;;
pyproject.toml|*/pyproject.toml)
git show ":2:${f}" | sed -E '/^[[:space:]]*version[[:space:]]*=/d' > "${tmpdir}/ours" || { rm -rf "${tmpdir}"; return 1; }
git show ":3:${f}" | sed -E '/^[[:space:]]*version[[:space:]]*=/d' > "${tmpdir}/theirs" || { rm -rf "${tmpdir}"; return 1; }
;;
version.json|*/version.json)
git show ":2:${f}" | normalize_json_without_keys version sdkVersion > "${tmpdir}/ours" || { rm -rf "${tmpdir}"; return 1; }
git show ":3:${f}" | normalize_json_without_keys version sdkVersion > "${tmpdir}/theirs" || { rm -rf "${tmpdir}"; return 1; }
;;
*)
rm -rf "${tmpdir}"
return 1
;;
esac

cmp -s "${tmpdir}/ours" "${tmpdir}/theirs"
status=$?
rm -rf "${tmpdir}"
return "${status}"
}

# Auto-resolve release artifact conflicts only when the conflict is
# version-only: keep stable's version, but never drop dependency,
# script, export, or package metadata changes from main.
# npm/PyPI already have stable's published version; downgrading to
# main's track would break the next stable release. Mirrors the
# auto-resolve in sync-patches.yml, biased the other direction.
while read -r f; do
case "$f" in
package.json|*/package.json|pyproject.toml|*/pyproject.toml|version.json|*/version.json)
if release_artifact_only_conflict "$f"; then
echo " Auto-resolving $f (version-only; keeping stable's version)"
git checkout --ours "$f"
git add "$f"
else
echo " Leaving $f unresolved (contains non-version changes)"
fi
;;
esac
done < <(git diff --name-only --diff-filter=U)
>>>>>>> origin/stable

if [ -z "$(git diff --name-only --diff-filter=U)" ]; then
MERGE_STATUS="auto_resolved"
Expand Down Expand Up @@ -141,7 +198,11 @@ jobs:

- creates \`${BRANCH_NAME}\` from \`${BASE_BRANCH}\`
- merges \`${SOURCE_BRANCH}\` into the candidate branch
<<<<<<< HEAD
- release artifact conflicts (package.json, pyproject.toml, version.json) auto-resolved to keep stable's published version
=======
- version-only release artifact conflicts (package.json, pyproject.toml, version.json) auto-resolved to keep stable's published version
>>>>>>> origin/stable

---
_Auto-created by promote-stable workflow._
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-react.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ concurrency:

jobs:
release:
# Stable publishes must go through release-stable.yml.
if: ${{ github.event_name != 'workflow_dispatch' || github.ref_name != 'stable' }}
runs-on: ubuntu-24.04
steps:
- name: Generate token
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-superdoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ concurrency:

jobs:
release:
# Stable publishes must go through release-stable.yml; PR previews are still allowed.
if: ${{ github.event_name != 'workflow_dispatch' || github.ref_name != 'stable' || inputs.pr_number }}
runs-on: ubuntu-24.04
steps:
- name: Generate token
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-vscode-ext.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ concurrency:

jobs:
release:
# Stable publishes must go through release-stable.yml.
if: ${{ github.event_name != 'workflow_dispatch' || github.ref_name != 'stable' }}
runs-on: ubuntu-latest
steps:
- name: Generate token
Expand Down
15 changes: 15 additions & 0 deletions demos/contract-templates/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@
*/

import { SuperDoc } from 'superdoc';
<<<<<<< HEAD
import { createSuperDocUI } from 'superdoc/ui';
import 'superdoc/style.css';
import './style.css';
import { attachFieldChip } from './field-chip.js';
=======
import 'superdoc/style.css';
import './style.css';
>>>>>>> origin/stable

type NodeKind = 'block' | 'inline';
type LockMode = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked';
Expand Down Expand Up @@ -199,10 +204,13 @@ const state = {
values: {} as Record<FieldKey, string>,
versions: {} as Record<ClauseId, string>,
expandedClause: null as ClauseId | null,
<<<<<<< HEAD
/** UI controller; created in `initialize`, disposed by `teardown`. */
ui: null as ReturnType<typeof createSuperDocUI> | null,
/** Field-chip detach handle; created in `initialize`, called by `teardown`. */
fieldChipTeardown: null as (() => void) | null,
=======
>>>>>>> origin/stable
};

const statusEl = qs<HTMLElement>('#status');
Expand Down Expand Up @@ -262,6 +270,7 @@ async function initialize(instance: DemoSuperDoc): Promise<void> {
readStateFromDocument();
renderPanels();
refreshSummary();
<<<<<<< HEAD

// Contextual smart-field chip (SD-3157). Plain TS — uses the
// public `superdoc/ui` controller directly, no framework. The chip
Expand All @@ -280,6 +289,8 @@ async function initialize(instance: DemoSuperDoc): Promise<void> {
valueFor: (key) => state.values[key as FieldKey],
});

=======
>>>>>>> origin/stable
setStatus('Ready');
setBusy(false);
}
Expand Down Expand Up @@ -522,6 +533,7 @@ function escapeAttr(s: string): string {
doc: () => state.editor?.doc ?? null,
};

<<<<<<< HEAD
const teardown = () => {
// Order matters: detach the field chip first (it relies on the UI
// controller for `getRect`), then destroy the UI controller, then
Expand All @@ -541,5 +553,8 @@ const teardown = () => {
state.ui = null;
superdoc.destroy();
};
=======
const teardown = () => superdoc.destroy();
>>>>>>> origin/stable
window.addEventListener('beforeunload', teardown);
if (import.meta.hot) import.meta.hot.dispose(teardown);
3 changes: 3 additions & 0 deletions demos/contract-templates/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ input:focus {
color: var(--demo-text-muted);
font-weight: 600;
}
<<<<<<< HEAD

/*
* Contextual smart-field chip (SD-3157). Floats over the active smart-
Expand Down Expand Up @@ -301,3 +302,5 @@ input:focus {
color: var(--demo-text, #18181b);
font-weight: 400;
}
=======
>>>>>>> origin/stable
Loading
Loading