Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
71df1e5
test(package): define headless markdown subpath contract
seonghobae Aug 10, 2026
6d18943
refactor(policy): extract framework-neutral safe link rules
seonghobae Aug 10, 2026
a7a0d8c
refactor(policy): extract framework-neutral inline image rules
seonghobae Aug 10, 2026
5e5eb58
refactor(link): reuse framework-neutral URI policy
seonghobae Aug 10, 2026
9318cc6
refactor(image): reuse framework-neutral raster policy
seonghobae Aug 10, 2026
9128400
feat(markdown): add headless serializer package barrel
seonghobae Aug 10, 2026
15e2cf2
feat(markdown): add public headless serializer entry
seonghobae Aug 10, 2026
17ec8cb
build(markdown): add headless serializer bundle
seonghobae Aug 10, 2026
3fdb0d6
test(package): verify packed headless markdown consumers
seonghobae Aug 10, 2026
3d86270
refactor(markdown): depend on framework-neutral policies
seonghobae Aug 10, 2026
7ddfb35
feat(package): declare headless markdown subpath
seonghobae Aug 10, 2026
c7344c3
docs(package): document headless markdown subpath
seonghobae Aug 10, 2026
63c62e5
docs(markdown): doctor headless package boundary
seonghobae Aug 10, 2026
ff5f79d
test(package): execute headless markdown source barrel
seonghobae Aug 10, 2026
ffcd120
test(package): execute Node HTML conversion from packed markdown
seonghobae Aug 10, 2026
758d50a
docs(traceability): bind active headless markdown package
seonghobae Aug 10, 2026
937a006
fix(package): make packed markdown consumer syntax deterministic
seonghobae Aug 10, 2026
9bad419
build(markdown): include local GFM declaration shim
seonghobae Aug 10, 2026
ca5fcf4
docs(changelog): record headless markdown package
seonghobae Aug 10, 2026
34bef8e
fix(markdown): bundle Turndown's Node parser path
seonghobae Aug 10, 2026
ed248b4
test(package): prove markdown ignores ambient document
seonghobae Aug 10, 2026
1b86978
test(markdown): pin Node parser resolution contract
seonghobae Aug 10, 2026
6bc61db
docs(markdown): record Node parser resolution boundary
seonghobae Aug 10, 2026
bcee16a
test(readme): require headless markdown discovery
seonghobae Aug 10, 2026
6caad45
test(package): keep markdown contract scoped to declared acceptance
seonghobae Aug 10, 2026
c2b47eb
test(package): parse emitted module authority semantically
seonghobae Aug 10, 2026
eb359c2
test(package): cover semantic module-authority scanning
seonghobae Aug 10, 2026
e38f40c
fix(package): ignore loader-shaped comment text
seonghobae Aug 10, 2026
06796c3
test(package): run semantic authority scanner regression
seonghobae Aug 10, 2026
8345885
test(package): bind semantic module-authority verifier
seonghobae Aug 10, 2026
e5bcc6e
test(package): require actionable module-authority diagnostics
seonghobae Aug 10, 2026
a2a90b4
fix(package): surface module-authority specifiers
seonghobae Aug 10, 2026
a678eaa
test(package): require mixed-module bundling for headless Markdown
seonghobae Aug 10, 2026
b3ed9e8
fix(package): transform mixed CommonJS in headless build
seonghobae Aug 10, 2026
33832c7
fix(package): pin Turndown standalone parser entry
seonghobae Aug 10, 2026
728b163
test(package): bind standalone Turndown aliases
seonghobae Aug 10, 2026
5768ff9
fix(package): avoid ambient document access in headless markdown runtime
seonghobae Aug 10, 2026
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ Historical release entries from **0.1.0 through 0.5.27** are preserved verbatim
- Added a fail-closed draft release asset inventory gate that requires exactly one npm tarball, one Office wheel, and `SHA256SUMS`, rejects stale or unexpected draft assets before immutable publication, and verifies every GitHub-reported `sha256:` asset digest against the transferred local file
- Kept SSR document disclosure opt-in through `formFieldName`; hidden-field values remain client-controlled submission data and do not replace host authentication, authorization, tenant isolation, CSRF defenses, server validation, durable concurrency, or persistence controls
- Kept collaborative Yjs document content out of server markup until the host-owned client collaboration lifecycle is bound
- Added packed headless Markdown authority verification that rejects external runtime imports, dynamic module loaders, ambient network/environment credential access, React/TipTap/Yjs runtime coupling, CWL host coupling, and model credential references from the dedicated conversion artifact

### Added
- Added `@contextualwisdomlab/cwl-editor/markdown` as a headless ESM/CommonJS/TypeScript conversion subpath exposing the existing deterministic Markdown/HTML/email/plain-text serializers while sharing framework-neutral safe-link and strict inline-raster policy with the editor instead of importing the React/TipTap extension graph
- Added bounded `inspectDocumentEnvelopeIdentity()` and `inspectDocumentEnvelopeIdentityBytes()` routing metadata plus the framework-independent `envelope-identity` package subpath so hosts can select explicit schema migrations without exposing document bodies, weakening the strict current-schema parser, or moving migration/persistence authority into Inkspan
- Added one optional construction-time `onSnapshotChange` callback to the framework-free autosave queue and durable autosave session so hosts can observe saving, pending, blocked, recovery, idle, and shutdown state without polling or introducing a subscriber collection
- Added privacy-minimized revision-scoped selection evidence through `getSelectionRevisionEvidence()`, binding frozen ProseMirror coordinates to the SHA-256 strong revision of the exact same immutable editor state before asynchronous hashing begins
Expand All @@ -35,6 +37,7 @@ Historical release entries from **0.1.0 through 0.5.27** are preserved verbatim
- Added a dependency-locked Chromium/Firefox/WebKit **cross-engine rich-clipboard release gate** using Playwright 1.62.0, one versioned synthetic adversarial corpus, the actual TipTap/ProseMirror paste path, exact-source-head/lock/browser evidence, hostile-DOM and resource-ceiling cases, bounded performance alarm evidence, and fail-closed three-engine consensus without generic normalization
- Added test-first Node `renderToString` evidence for the missing SSR native value, controlled-over-default selection, escaping, external form ownership, no ProseMirror server construction, and opt-out non-disclosure
- Added browser-DOM handoff tests proving the field retains and updates the selected value before TipTap exists while reset-only unnamed fields remain empty
- Added real packed ESM/CommonJS/strict-TypeScript consumers for the headless Markdown subpath, including browserless Node HTML-to-Markdown execution, strict safe/unsafe-link behavior, plain-text projection, normalization, full-document email language/direction preservation, and artifact authority-boundary checks

### Documentation
- Added browser-assurance doctoring, operability, test-strategy, clipboard-security, and documentation-fitness coverage for the **dependency-locked Chromium/Firefox/WebKit** release boundary, including Playwright 1.62.0 provenance, exact-head/corpus identity, standards-backed difference policy, evidence minimization, fail-closed behavior, and rollback
Expand All @@ -54,6 +57,7 @@ Historical release entries from **0.1.0 through 0.5.27** are preserved verbatim
- Added deterministic documentation contract tests and APA 7th doctoring grounded in RFC 9110, WCAG 2.2, NIST SP 800-204, NIST SP 800-204D, OWASP ASVS 5.0.0, React, current Next.js App Router guidance, and GitHub Actions primary documentation
- Added APA 7th doctoring for the SSR native form field, including the WHATWG hidden-input/form-entry contract, React server/hydration continuity, client-controlled-data boundary, host-owned CSRF and acceptance controls, collaboration exclusion, and rollback
- Added lifecycle-observation doctoring covering bounded callback retention, local-versus-shareable evidence, durable-validator coherence, WCAG 2.2 status-message responsibilities, rollback, and APA 7 references to RFC 9110, WCAG 2.2, and optimistic concurrency research
- Added headless Markdown package doctoring and standards traceability grounded in CommonMark 0.31.2 and Node.js package exports, with explicit active-PR maturity, shared-policy authority, packed artifact verification, and rollback boundaries

## [0.5.29] — 2026-08-05

Expand Down
9 changes: 8 additions & 1 deletion docs/TRACEABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This record maps durable Inkspan product decisions to authoritative standards, p
| Envelope version routing | A bounded identity-only inspector identifies `schemaId`/`schemaVersion` for dispatch while the current parser stays strict and the host owns migration execution | RFC 8259; RFC 7493; RFC 8785 for canonical current-schema bytes | ADR 0015, protected-main `documentEnvelopeIdentity` implementation/tests, envelope guide/doctoring and framework-independent packed consumers | Protected-main evidence proves only bounded routing metadata; identifying a schema generation does not validate that generation's document semantics, authorize migration, or prove durable persistence |
| Canonical document bytes | Deterministic revision evidence is derived from canonicalized validated document content | RFC 8785, JSON Canonicalization Scheme | revision-evidence, transition-evidence, restore tests | A content digest proves equality only, not actor/time/authorization/durable write |
| W3C text-position selector | Revision-scoped annotation interoperability uses a distinct versioned logical-text projection satisfying `0 <= start <= end <= projectedCodePointLength`, with inclusive `start`, exclusive `end`, Unicode-code-point offsets, grapheme-boundary validation, and same-state revision binding instead of relabeling ProseMirror coordinates | W3C Web Annotation Data Model; ProseMirror reference manual; ECMA-402 13th edition | ADR 0018, protected-main text-position selector implementation/tests, packed consumer verifier, selection lifecycle and doctoring | Protected-main evidence proves positions only for the named projection and exact revision; it does not prove actor, authorization, durable annotation acceptance, source IRI policy, or cross-revision re-anchoring |
| Headless deterministic Markdown conversion | The active package line reuses one serializer implementation and one framework-neutral safe-link/inline-raster policy while exposing an explicit ESM/CommonJS/TypeScript subpath whose emitted runtime is self-contained and authority-bounded | CommonMark 0.31.2; Node.js package `exports` documentation | active PR #114 source/policy refactor, packed Node consumers, package-distribution contract, `docs/doctoring/headless-markdown-package.md` | `implemented_on_active_pr`; deterministic conversion does not grant MIME delivery, recipient, auth, tenant, persistence, network, credential, or model authority and is not shipped until protected integration |
| Provenance semantics | Local transition/release evidence keeps content lineage separate from actor/authorization/durable claims | W3C PROV family | transition evidence, release evidence, canonical data model | Inkspan does not claim complete PROV conformance or host audit provenance |
| Accessibility | Native controls, keyboard semantics, shortcut metadata, and host-facing status state support accessible embedding | W3C WCAG 2.2; WAI-ARIA where used | toolbar/accessibility tests, SSR tests, autosave lifecycle data | Component evidence alone is not a full host WCAG conformance claim |
| Browser clipboard behavior | Security-relevant rich HTML handling requires actual paste-pipeline integration and bounded semantic reconstruction before editor state | WHATWG HTML parsing; W3C Clipboard API | protected-main rich-clipboard unit/integration corpus and SafeClipboard ADR | Protected jsdom/TipTap integration success is not universal browser-engine conformance |
Expand All @@ -35,6 +36,10 @@ Ecma International. (2026). *ECMA-402: ECMAScript 2026 internationalization API

Fielding, R., Nottingham, M., & Reschke, J. (Eds.). (2022). *HTTP Semantics* (RFC 9110; STD 97). RFC Editor. https://doi.org/10.17487/RFC9110

MacFarlane, J. (2024, January 28). *CommonMark specification* (Version 0.31.2). CommonMark. https://spec.commonmark.org/0.31.2/

Node.js contributors. (2026). *Modules: Packages*. Node.js documentation. https://nodejs.org/api/packages.html

ProseMirror. (n.d.). *ProseMirror reference manual*. Retrieved August 10, 2026, from https://prosemirror.net/docs/ref/

Rundgren, A., Jordan, B., & Erdtman, S. (2020). *JSON Canonicalization Scheme (JCS)* (RFC 8785). RFC Editor. https://doi.org/10.17487/RFC8785
Expand Down Expand Up @@ -83,8 +88,10 @@ Lower levels may explain intent or history but cannot override a contradictory h

Envelope identity routing, SafeClipboard, W3C text-position selector evidence, and cross-engine browser assurance are implemented on protected `main`. The W3C selector remains revision-scoped and projection-version-scoped and must satisfy `0 <= start <= end <= projectedCodePointLength`; protected integration does not transfer annotation persistence, source identity, authorization, or re-anchoring authority from the host. The browser gate being protected does not let a future release reuse historical browser evidence: the exact release candidate must generate fresh evidence bound to its own source, committed synthetic corpus, package-lock SHA-256, run identity, browser revisions, and packed npm artifact SHA-256.

Headless deterministic Markdown serialization is `implemented_on_active_pr` on PR #114 until protected integration; its package/runtime evidence cannot be promoted to shipped behavior before that merge.

Documentation must not promote Proposed or Planned capabilities to Implemented merely because a PR, issue, or design document is detailed.

## Review cadence

Revalidate this matrix when a public schema, selector projection, security boundary, supported runtime/browser line, Office format contract, collaboration/provider contract, accessibility interaction, release workflow, or authoritative external standard materially changes. Prefer explicit supersession over silent historical rewrite.
Revalidate this matrix when a public schema, selector projection, serialization/package boundary, security boundary, supported runtime/browser line, Office format contract, collaboration/provider contract, accessibility interaction, release workflow, or authoritative external standard materially changes. Prefer explicit supersession over silent historical rewrite.
128 changes: 128 additions & 0 deletions docs/doctoring/headless-markdown-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Headless deterministic Markdown package

Status: Implemented on active PR

## Purpose

Inkspan's protected root package already exposes deterministic Markdown, HTML,
email-HTML, and plain-text conversion. The root package also evaluates the
interactive React/TipTap editor graph, which is unnecessary for server, worker,
CLI, and other headless consumers that only need deterministic conversion.

The active package line therefore exposes the same conversion behavior through
`@contextualwisdomlab/cwl-editor/markdown` while keeping the interactive editor,
collaboration provider, persistence, transport, credentials, and model authority
outside the subpath.

## Decision boundary

The new subpath does not implement a second serializer. Existing conversion
functions remain the single behavioral authority. Safe hyperlink and inline
raster source checks are extracted into framework-neutral policy modules and are
shared by both the serializer and the TipTap extensions. This prevents a
headless package from weakening the editor trust boundary or creating divergent
security policy.

The package exports:

- `markdownToHtml()`;
- `htmlToMarkdown()`;
- `normalizeMarkdown()`;
- `markdownToEmailHtml()`;
- `markdownToPlainText()`;
- `htmlToPlainText()`; and
- their public option types.

It deliberately does not export editor construction, TipTap extensions, Yjs,
autosave, revision evidence, annotation capture, transport, persistence, or
model integration.

## Standards and compatibility

CommonMark 0.31.2 remains the current published CommonMark specification. Inkspan
uses Marked with GFM enabled and keeps its product-specific fail-closed rules for
raw HTML, hyperlink targets, and raster data-URI images on top of that parsing
behavior. This package change does not expand the set of supported or trusted
Markdown constructs.

Node.js package `exports` is the public encapsulation boundary. The subpath has
explicit ESM, CommonJS, and TypeScript declaration targets; consumers are not
expected to reach internal source files. Adding the subpath is additive, while a
future removal or incompatible signature change is a semantic-versioning event.

Turndown 7.2.4 publishes separate browser and Node entrypoints. Its package
metadata maps the normal ESM/CommonJS files to browser variants through the
`browser` field, while the Node entry depends on `@mixmark-io/domino`. Turndown's
security guidance states that standalone string parsing uses its custom Domino
parser and that this parser does not execute scripts or download external
resources. Vite's client-oriented default `resolve.mainFields` prefers
`browser` before `module`; therefore a nominally headless library build can
silently select Turndown's browser entry and require a global `document` unless
the build resolution policy is explicit.

The dedicated Markdown build excludes `browser` from its resolution order and
bundles Turndown's Node path, including the non-fetching parser, into the package
artifact. This is a product authority decision rather than a build-performance
tweak: browser-field selection previously caused the packed Node consumer to
fail with `ReferenceError: document is not defined` at the actual
HTML-to-Markdown boundary.

## Runtime authority and security

The dedicated build produces a self-contained JavaScript artifact. Packed
consumer verification rejects:

- external runtime imports or re-exports;
- dynamic `import()` and `require()` loaders;
- ambient `fetch`, XHR, WebSocket, EventSource, or environment-backed credential
access;
- React/React DOM, TipTap, Yjs, naruon, contextual-orchestrator, or model
credential references.

HTML-to-Markdown remains usable in Node without a browser DOM through the
bundled non-fetching parser path. The packed ESM and CommonJS consumers install a
throwing `globalThis.document` accessor before loading/calling the Markdown
subpath, so successful conversion proves that the artifact does not silently
fall back to ambient browser-document authority. The package performs no network
request and does not own MIME delivery, recipients, authentication,
authorization, tenancy, durable persistence, retention, or audit. Full-document
email output is deterministic content only.

## Verification

Permanent tests and packed-artifact checks cover:

- public export-map discovery;
- explicit non-browser Vite main-field resolution;
- ESM and CommonJS consumers outside the source tree;
- strict TypeScript declarations;
- browserless Node HTML-to-Markdown with hostile ambient `document` access;
- safe and rejected hyperlinks;
- plain-text projection;
- normalization;
- full-document email language/direction preservation;
- absence of forbidden runtime authority; and
- repository-wide exact 100% owned production coverage.

The package line remains `implemented_on_active_pr` until the unchanged exact
head passes all applicable CI/security/review gates and reaches protected main.

## Rollback

Before protected integration, rollback removes the additive export/build/verifier
and restores serializer imports to the existing editor policy modules. After
integration, rollback may remove only the additive subpath in a versioned
breaking release; it must not fork or silently weaken the shared hyperlink/image
policies.

## References — APA 7th

MacFarlane, J. (2024, January 28). *CommonMark specification* (Version 0.31.2). CommonMark. https://spec.commonmark.org/0.31.2/

Mixmark-io. (2026). *Turndown* (Version 7.2.4) [Computer software]. GitHub. https://github.com/mixmark-io/turndown

Mixmark-io. (2026). *Turndown security policy*. GitHub. https://github.com/mixmark-io/turndown/security

Node.js contributors. (2026). *Modules: Packages*. Node.js documentation. https://nodejs.org/api/packages.html

Vite contributors. (2026). *Shared options: resolve.mainFields*. Vite documentation. https://vite.dev/config/shared-options.html
Loading
Loading