diff --git a/docs/TRD.md b/docs/TRD.md index 80cec07ac..27f3a6430 100644 --- a/docs/TRD.md +++ b/docs/TRD.md @@ -85,9 +85,9 @@ The Office JSON→DOCX/XLSX/PPTX renderer is **network-free**, **macro-free**, m Protected DOCX fidelity includes informative bounded inline PNG figures under ADR 0022, bounded `rich_paragraph` bold/italic/underline runs under ADR 0023, exact optional `left`/`center`/`right`/`justify` alignment for `paragraph` and `rich_paragraph` under ADR 0024, and the same bounded alignment for `heading` under ADR 0025. Omitted alignment preserves inherited/default Word semantics. -Active PR #137 implements the Proposed ADR 0026 extension for one optional relationship-backed hyperlink on a rich-text run. That active contract deliberately permits only printable-ASCII absolute HTTP(S) targets up to 4,096 characters, rejects local/executable/data/mail/telephone/relative/protocol-relative/credential-bearing/backslash/whitespace-control/non-ASCII targets, preserves the exact accepted target and existing run emphasis, and remains network-free. Until protected integration, this is `implemented_on_active_pr`, not shipped authority. +Accepted ADR 0026 is implemented on protected `main` through protected PR #137. The bounded optional relationship-backed hyperlink contract for rich-text runs permits only printable-ASCII absolute HTTP(S) targets up to 4,096 characters, rejects local/executable/data/mail/telephone/relative/protocol-relative/credential-bearing/backslash/whitespace-control/non-ASCII targets, preserves the exact accepted target and existing run emphasis, and remains network-free. This is `implemented_on_protected_main`. -The protected additive fidelity contracts and active hyperlink proposal do not grant arbitrary style, font, color, spacing, indentation, arbitrary relationship IDs/types, internal/bookmark hyperlinks, field codes, raw OOXML, remote-resource fetching, decorative-image semantics, destination trust validation, or source-format parsing authority. +The protected additive fidelity contracts, including bounded external hyperlinks, do not grant arbitrary style, font, color, spacing, indentation, arbitrary relationship IDs/types, internal/bookmark hyperlinks, field codes, raw OOXML, remote-resource fetching, decorative-image semantics, destination trust validation, or source-format parsing authority. Format-fidelity claims are limited to tested supported constructs. The renderer does not execute macros, scripts, formula calculation, embedded external resources, or Desktop Office automation. A stored external hyperlink relationship is document metadata, not permission to dereference or trust its destination. @@ -141,4 +141,4 @@ Queued, cancelled, skipped-required, absent, stale-head, predecessor-head, statu Protected `main` is the sole shipped implementation baseline. SafeClipboard, cross-engine browser assurance, the security disclosure lifecycle, autosave lifecycle observation, toolbar shortcut accessibility metadata, accessible editor placeholder semantics, SSR/native-form serialization, revision-scoped selection evidence, W3C text-position selector evidence, the React-free text-position-selector subpath, document-transition evidence, envelope identity routing, framework-neutral deterministic Markdown conversion, CSS paged-media print output, DOCX informative PNG figures, bounded rich-text runs, bounded paragraph alignment, bounded heading alignment, and the OIDC-backed unified stable registry release train are `implemented_on_protected_main`. -The bounded DOCX rich-run external hyperlink contract in #137 is `implemented_on_active_pr` under Proposed ADR 0026. Open branches may extend the protected boundary, but no active-PR capability becomes shipped merely because its design, tests, or documentation are complete. +The bounded DOCX rich-run external hyperlink contract in protected PR #137 is `implemented_on_protected_main` under Accepted ADR 0026. Open branches may extend the protected boundary, but no active-PR capability becomes shipped merely because its design, tests, or documentation are complete. \ No newline at end of file diff --git a/docs/doctoring/editor-placeholder-accessibility.md b/docs/doctoring/editor-placeholder-accessibility.md index 600002995..6bbcad054 100644 --- a/docs/doctoring/editor-placeholder-accessibility.md +++ b/docs/doctoring/editor-placeholder-accessibility.md @@ -1,10 +1,10 @@ # Editor placeholder accessibility -Status: Implemented on active PR +Status: Implemented on protected main ## Purpose -Inkspan's visual empty-editor hint is rendered by the TipTap Placeholder extension. The active accessibility change mirrors that same host-supplied placeholder into the ProseMirror textbox's `aria-placeholder` attribute so assistive-technology users can receive equivalent entry guidance without requiring every embedding host to duplicate the text in a separate description element. +Inkspan's visual empty-editor hint is rendered by the TipTap Placeholder extension. The protected accessibility behavior mirrors that same host-supplied placeholder into the ProseMirror textbox's `aria-placeholder` attribute so assistive-technology users can receive equivalent entry guidance without requiring every embedding host to duplicate the text in a separate description element. The placeholder remains **supplemental guidance**, not the editor's accessible name. Inkspan's existing accessible-name precedence remains unchanged: `aria-labelledby` whenever a host supplies a non-blank label reference, otherwise an explicit `aria-label`, otherwise the product fallback label. @@ -22,7 +22,7 @@ Standalone and provider-neutral collaborative surfaces use the same `buildEditor ## Verification -The active test line includes: +The protected verification line includes: - a focused historical RED proving the accessibility builder had no placeholder input or attribute contract; - normalized non-empty placeholder plus `aria-labelledby` name precedence; diff --git a/docs/doctoring/headless-markdown-package.md b/docs/doctoring/headless-markdown-package.md index dbaa8d740..63c1f0a5a 100644 --- a/docs/doctoring/headless-markdown-package.md +++ b/docs/doctoring/headless-markdown-package.md @@ -1,6 +1,6 @@ # Headless deterministic Markdown package -Status: Implemented on active PR +Status: Implemented on protected main ## Purpose @@ -9,14 +9,14 @@ 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 +The protected package surface 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 +The 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 @@ -104,8 +104,8 @@ Permanent tests and packed-artifact checks cover: - 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. +The package line is `implemented_on_protected_main`; protected main and its +packed-artifact verification are the authority for this shipped package surface. ## Rollback diff --git a/docs/package-distribution.md b/docs/package-distribution.md index ddb4df0ee..8e364f7d1 100644 --- a/docs/package-distribution.md +++ b/docs/package-distribution.md @@ -17,7 +17,7 @@ integrations. | `@contextualwisdomlab/cwl-editor/envelope-identity` | Framework-independent identity-only envelope routing for bounded schema identity inspection; migration remains host-owned | | `@contextualwisdomlab/cwl-editor/revision-evidence` | Framework-independent revision evidence and document-transition evidence for local content equality/lineage claims | | `@contextualwisdomlab/cwl-editor/text-position-selector` | `implemented_on_protected_main` — React-free text-position projection core implementing W3C `TextPositionSelector`; interactive capture, revision binding, authorization, persistence, and re-anchoring remain outside this subpath | -| `@contextualwisdomlab/cwl-editor/markdown` | `implemented_on_active_pr` — headless deterministic Markdown/HTML/email/plain-text conversion with the same safe-link and strict inline-raster policies as the editor, without importing the React/TipTap editor graph | +| `@contextualwisdomlab/cwl-editor/markdown` | `implemented_on_protected_main` — headless deterministic Markdown/HTML/email/plain-text conversion with the same safe-link and strict inline-raster policies as the editor, without importing the React/TipTap editor graph | | `@contextualwisdomlab/cwl-editor/styles.css` | Editor layout and theming | | `@contextualwisdomlab/cwl-editor/fonts.css` | Full offline KR/EN/JP/SC/TC/VI font bundle | | `@contextualwisdomlab/cwl-editor/fonts-latin.css` | Smaller Latin/Vietnamese font bundle | diff --git a/src/editorPlaceholderDocumentation.test.ts b/src/editorPlaceholderDocumentation.test.ts index c5402ca5a..799270bd3 100644 --- a/src/editorPlaceholderDocumentation.test.ts +++ b/src/editorPlaceholderDocumentation.test.ts @@ -12,7 +12,7 @@ describe('editor placeholder accessibility documentation', () => { 'docs/doctoring/editor-placeholder-accessibility.md', ); - expect(doctoring).toContain('Status: Implemented on active PR'); + expect(doctoring).toContain('Status: Implemented on protected main'); expect(doctoring).toContain('aria-placeholder'); expect(doctoring).toContain('supplemental guidance'); expect(doctoring).toContain('aria-labelledby'); diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts new file mode 100644 index 000000000..68650e150 --- /dev/null +++ b/src/protectedDocumentationMaturity.test.ts @@ -0,0 +1,104 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { describe, expect, it } from 'vitest'; + +const repositoryFile = (path: string): string => + readFileSync(resolve(process.cwd(), path), 'utf8'); + +describe('protected capability documentation maturity', () => { + it('does not leave merged placeholder accessibility documented as active-PR work', () => { + const doctoring = repositoryFile( + 'docs/doctoring/editor-placeholder-accessibility.md', + ); + + expect(doctoring).toContain('Status: Implemented on protected main'); + expect(doctoring).not.toContain('Status: Implemented on active PR'); + }); + + it('does not leave the merged headless Markdown package documented as active-PR work', () => { + const doctoring = repositoryFile( + 'docs/doctoring/headless-markdown-package.md', + ); + + expect(doctoring).toContain('Status: Implemented on protected main'); + expect(doctoring).not.toContain('Status: Implemented on active PR'); + expect(doctoring).not.toContain('implemented_on_active_pr'); + }); + + it('keeps the shipped Markdown export and package-distribution maturity aligned', () => { + const manifest = JSON.parse(repositoryFile('package.json')) as { + exports?: Record; + }; + const distribution = repositoryFile('docs/package-distribution.md'); + + expect(manifest.exports).toHaveProperty('./markdown'); + expect(distribution).toContain( + '| `@contextualwisdomlab/cwl-editor/markdown` | `implemented_on_protected_main`', + ); + expect(distribution).not.toContain( + '| `@contextualwisdomlab/cwl-editor/markdown` | `implemented_on_active_pr`', + ); + }); + + it('does not describe the protected Markdown package as proposed architecture', () => { + const architecture = repositoryFile('ARCHITECTURE.md'); + const manifest = JSON.parse(repositoryFile('package.json')) as { + exports?: Record; + }; + const adr = repositoryFile( + 'docs/adr/0020-framework-neutral-markdown-package-boundary.md', + ); + + expect(manifest.exports).toHaveProperty('./markdown'); + expect(adr).toContain('Status: Accepted'); + expect(architecture).toContain('Markdown[Protected markdown subpath'); + expect(architecture).toContain('Accepted ADR 0020'); + expect(architecture).not.toContain('Proposed markdown subpath'); + expect(architecture).not.toContain('active PR #114'); + expect(architecture).not.toContain( + 'Until that PR or a verified successor integrates', + ); + }); + + it('does not describe the protected print stylesheet as proposed architecture', () => { + const architecture = repositoryFile('ARCHITECTURE.md'); + const stylesheet = repositoryFile('src/styles.css'); + const adr = repositoryFile( + 'docs/adr/0021-css-paged-media-print-boundary.md', + ); + + expect(stylesheet).toContain('@media print'); + expect(adr).toContain('Status: Accepted'); + expect(architecture).toContain('Accepted ADR 0021'); + expect(architecture).toContain( + 'CSS-only `@media print` presentation boundary governed by Accepted ADR 0021', + ); + expect(architecture).not.toContain('Proposed ADR 0021'); + expect(architecture).not.toContain('active PR #116'); + expect(architecture).not.toContain('Until #116 integrates'); + }); + + it.each([ + '', + '\n\n## Unrelated future capability\n\nActive PR: implemented_on_active_pr. Until protected integration, this capability remains proposed.', + ])('keeps merged DOCX maturity with unrelated proposed text %j', (unrelatedProposal) => { + const docxParagraphs = (repositoryFile('docs/TRD.md') + unrelatedProposal) + .split(/\n\s*\n/u) + .filter((paragraph) => paragraph.includes('ADR 0026') || paragraph.includes('#137')); + expect(docxParagraphs).toHaveLength(2); + const trd = docxParagraphs.join('\n\n'); + const adr = repositoryFile( + 'docs/adr/0026-bounded-docx-external-hyperlinks.md', + ); + + expect(adr).toContain('Status: Accepted'); + expect(trd).toContain('Accepted ADR 0026'); + expect(trd).toContain('protected PR #137'); + expect(trd).toContain('implemented_on_protected_main'); + expect(trd).not.toContain('Active PR #137'); + expect(trd).not.toContain('Proposed ADR 0026'); + expect(trd).not.toContain('implemented_on_active_pr'); + expect(trd).not.toContain('Until protected integration'); + }); +});