From cbd14a851481b0de5ba9fa0ee441b1f83d55ccbb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 04:49:15 +0900 Subject: [PATCH 01/23] test(docs): catch stale protected capability maturity --- src/protectedDocumentationMaturity.test.ts | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/protectedDocumentationMaturity.test.ts diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts new file mode 100644 index 000000000..8dd2bf59b --- /dev/null +++ b/src/protectedDocumentationMaturity.test.ts @@ -0,0 +1,28 @@ +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'); + }); +}); From c57e72335aacaa693165552f608b21be033d9d8e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 04:59:26 +0900 Subject: [PATCH 02/23] docs: mark placeholder accessibility protected --- docs/doctoring/editor-placeholder-accessibility.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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; From 2ee5b7662b5338a060bc734876ed45b1f5344fb2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 05:01:32 +0900 Subject: [PATCH 03/23] docs: mark headless markdown package protected --- docs/doctoring/headless-markdown-package.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 From 536f12ad4322e1290ec658ec620f5bdef9193256 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 05:06:31 +0900 Subject: [PATCH 04/23] test(docs): align placeholder maturity contract --- src/editorPlaceholderDocumentation.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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'); From 4041413c052f8a77907b96f600a7b43bef3e030c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 04:22:04 +0900 Subject: [PATCH 05/23] docs: restore protected capability maturity repairs --- .../editor-placeholder-accessibility.md | 6 ++-- docs/doctoring/headless-markdown-package.md | 10 +++---- src/editorPlaceholderDocumentation.test.ts | 2 +- src/protectedDocumentationMaturity.test.ts | 28 +++++++++++++++++++ 4 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 src/protectedDocumentationMaturity.test.ts 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/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..8dd2bf59b --- /dev/null +++ b/src/protectedDocumentationMaturity.test.ts @@ -0,0 +1,28 @@ +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'); + }); +}); From c86c08bc81d601c0f05c08de81909b3fc5937af2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 04:24:31 +0900 Subject: [PATCH 06/23] test(docs): bind Markdown export to protected maturity --- src/protectedDocumentationMaturity.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index 8dd2bf59b..b1b244185 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -25,4 +25,19 @@ describe('protected capability documentation maturity', () => { 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`', + ); + }); }); From 45d1c78c7ab4ec94d15eafffb1a9accb4dd6e251 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 05:00:26 +0900 Subject: [PATCH 07/23] docs: align Markdown package maturity with protected main --- docs/package-distribution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 | From 69cd3d671102f0a83ea7d4d13d7f97bed21d028f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 13:41:10 -0700 Subject: [PATCH 08/23] test(docs): expose stale protected architecture maturity --- src/protectedDocumentationMaturity.test.ts | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index b1b244185..7ab27191e 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -40,4 +40,40 @@ describe('protected capability documentation maturity', () => { '| `@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('protected PR #116'); + expect(architecture).not.toContain('Proposed ADR 0021'); + expect(architecture).not.toContain('active PR #116'); + expect(architecture).not.toContain('Until #116 integrates'); + }); +}); \ No newline at end of file From 5de95663afae33a88fb35f0b3c979481fc6e7dbe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 13:42:26 -0700 Subject: [PATCH 09/23] docs(architecture): mark protected package and print contracts shipped --- ARCHITECTURE.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a42dd5a5e..a8a642acf 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -43,7 +43,7 @@ flowchart TB Selector[Protected text-position-selector subpath] Autosave[Protected autosave subpath] Converter[Protected converter subpath] - Markdown[Proposed markdown subpath\nactive PR #114] + Markdown[Protected markdown subpath\nAccepted ADR 0020] SharedPolicy[Shared deterministic URI / inline-image policy] Serializers[Shared deterministic serializers] @@ -58,7 +58,7 @@ flowchart TB Markdown --> Serializers ``` -The active `@contextualwisdomlab/cwl-editor/markdown` work in PR #114 is governed by Proposed ADR 0020. Its purpose is dependency isolation, not a second serializer authority. Until that PR or a verified successor integrates, the proposed subpath is unshipped and the protected root package remains the public authority for those serializers. +The protected `@contextualwisdomlab/cwl-editor/markdown` package subpath is governed by Accepted ADR 0020. Protected PR #114 integrated this dependency-isolation boundary without creating a second serializer authority: the root package and the headless subpath share the same deterministic serializers and framework-neutral safe-link/inline-raster policy. The subpath is shipped on protected `main`; release candidates must regenerate its packed-artifact ESM, CommonJS, strict TypeScript, and runtime-authority evidence on the exact candidate head. A framework-independent subpath must prove its declared dependency boundary from the packed npm artifact under ESM, CommonJS, and strict TypeScript consumers. Source-level import shape alone is insufficient release evidence. @@ -66,15 +66,15 @@ A framework-independent subpath must prove its declared dependency boundary from Inkspan owns the CSS rules it ships for its editor. It does not own the operating system print spooler, printer, browser's pagination implementation, downstream PDF storage, or host disclosure policy. -Protected `main` currently remains the stylesheet authority. Proposed ADR 0021 and active PR #116 define a CSS-only `@media print` boundary intended to: +Protected `main` includes the CSS-only `@media print` boundary defined by Accepted ADR 0021 and integrated by protected PR #116. It: -- remove Inkspan-owned screen-only scroll/max-height clipping; -- hide toolbar, collaboration status, remote caret/cursor-label, and placeholder UI from printed document output; -- preserve authored document structures and links; -- use conservative paged-fragmentation hints; and -- keep links distinguishable without relying on color alone. +- removes Inkspan-owned screen-only scroll/max-height clipping; +- hides toolbar, collaboration status, remote caret/cursor-label, and placeholder UI from printed document output; +- preserves authored document structures and links; +- uses conservative paged-fragmentation hints; and +- keeps links distinguishable without relying on color alone. -This proposed presentation line does **not** create a JavaScript print mode, PDF service, page-number/header/footer authority, timestamp/signature claim, persistence layer, network requirement, credential, or model dependency. Until #116 integrates, the new print behavior is not shipped. +This protected presentation line does **not** create a JavaScript print mode, PDF service, page-number/header/footer authority, timestamp/signature claim, persistence layer, network requirement, credential, or model dependency. Browser/user-agent pagination and any durable print/PDF artifact remain host/user authority even though Inkspan's own print CSS is shipped. ## Modular MSA composition @@ -188,4 +188,4 @@ An acquisition reviewer should be able to verify the product without receiving p Local-only or restricted evidence includes production documents, selected quote text, conflict bodies, Yjs updates, awareness metadata, provider credentials, authorization claims, private model prompts/outputs, tenant-scoped validators, private print/export destinations, and security findings that expose exploitable deployment detail. -The authoritative naruon composition guide is [`docs/naruon-compose-ui-panel.md`](docs/naruon-compose-ui-panel.md). Standards, claim boundaries, and decision history are recorded in the doctoring and ADR corpus under `docs/doctoring/` and `docs/adr/`. +The authoritative naruon composition guide is [`docs/naruon-compose-ui-panel.md`](docs/naruon-compose-ui-panel.md). Standards, claim boundaries, and decision history are recorded in the doctoring and ADR corpus under `docs/doctoring/` and `docs/adr/`. \ No newline at end of file From 4bcd4aba4697dfccd1127b4a435b125801531188 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 20:16:11 -0700 Subject: [PATCH 10/23] test(docs): bind release inventory to protected workflow --- src/protectedDocumentationMaturity.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index 7ab27191e..f1782713b 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -76,4 +76,23 @@ describe('protected capability documentation maturity', () => { expect(architecture).not.toContain('active PR #116'); expect(architecture).not.toContain('Until #116 integrates'); }); + + it('keeps canonical release inventory text aligned with the protected workflow', () => { + const workflow = repositoryFile('.github/workflows/release.yml'); + const contracts = repositoryFile('docs/CONTRACTS.md'); + const operability = repositoryFile('docs/OPERABILITY.md'); + const testStrategy = repositoryFile('docs/TEST_STRATEGY.md'); + const doctoring = repositoryFile( + 'docs/doctoring/release-draft-asset-inventory.md', + ); + + expect(workflow).toContain('expected_asset_count=4'); + expect(workflow).toContain('release/inkspan.spdx.json'); + + for (const document of [contracts, operability, testStrategy, doctoring]) { + expect(document).toContain('exactly four regular top-level files'); + expect(document).toContain('inkspan.spdx.json'); + expect(document).not.toContain('exactly three regular top-level files'); + } + }); }); \ No newline at end of file From 4ee14e922a600d9ae23d626a28cf59666bd63a67 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 20:22:29 -0700 Subject: [PATCH 11/23] docs(release): align contracts with protected SBOM inventory --- docs/CONTRACTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CONTRACTS.md b/docs/CONTRACTS.md index 52ab1bdf0..5f1d00499 100644 --- a/docs/CONTRACTS.md +++ b/docs/CONTRACTS.md @@ -138,7 +138,7 @@ Expected degraded states are explicit rather than mapped to false success: A public release binds one exact integrated protected source head to package/artifact identity, applicable CI/security/accessibility/document-fidelity evidence, owned production coverage, public-docstring evidence, SBOM/provenance/reproducibility where configured, formal review requirements, rollback guidance, and post-publication smoke verification. -Before immutable publication, the canonical draft inventory is **exactly three regular top-level files**: exactly one npm tarball, exactly one Inkspan Office wheel, and `SHA256SUMS`. Missing, stale, unexpected, duplicate, non-regular, incompletely uploaded, or digest-mismatched assets fail closed. After upload and before publication, the authenticated paginated GitHub Releases API inventory must equal the local release directory by exact asset name, every remote asset must report an uploaded state, and every GitHub-reported `sha256:` digest must equal the digest of the exact transferred local file. The workflow does not silently delete an unexpected remote asset to make an ambiguous draft look clean. +Before immutable publication, the protected-main canonical draft inventory is **exactly four regular top-level files**: exactly one npm tarball, exactly one Inkspan Office wheel, `inkspan.spdx.json`, and `SHA256SUMS`. The aggregate `inkspan.spdx.json` is the protected-main SBOM artifact; any open proposal to split package-specific SBOMs is not release authority until protected integration updates this contract and executable gate together. Missing, stale, unexpected, duplicate, non-regular, incompletely uploaded, or digest-mismatched assets fail closed. After upload and before publication, the authenticated paginated GitHub Releases API inventory must equal the local release directory by exact asset name, every remote asset must report an uploaded state, and every GitHub-reported `sha256:` digest must equal the digest of the exact transferred local file. The workflow does not silently delete an unexpected remote asset to make an ambiguous draft look clean. Rollback must preserve readable canonical documents and must not require silently reinterpreting persisted schema or selector-projection semantics. Host-owned migrations, persistence rollback, annotation re-anchoring, tenant recovery, and deployment rollback remain host responsibilities unless a future versioned contract explicitly assigns them to Inkspan. @@ -154,7 +154,7 @@ Rollback must preserve readable canonical documents and must not require silentl | Office rendering | deterministic bounded JSON→artifact conversion | file destination policy, downstream distribution, tenant authorization | | naruon composition | stable local package/module boundary | authenticated compose transport, tenancy, provider/model policy | | model assistance | deterministic proposal acceptance boundary | provider, prompt/data policy, credentials, human approval | -| release evidence | exact three-file draft inventory, package/artifact/digest verification and repository evidence | downstream deployment and operational rollout | +| release evidence | exact four-file draft inventory including the protected aggregate SBOM, package/artifact/digest verification and repository evidence | downstream deployment and operational rollout | ## Related canonical documents From 93ef53334d390c8927f13d09d01cbd8e7b107f8e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 20:25:01 -0700 Subject: [PATCH 12/23] docs(release): align operability inventory with workflow --- docs/OPERABILITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index e96d053c4..b188a6583 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -60,7 +60,7 @@ Release publication occurs only from an exact integrated protected head. The rel Before publication: 1. fetch the current protected `main` ref and require the release tag event commit SHA to equal that exact integration tip, not merely be an ancestor of it; -2. build exactly three regular top-level release files: exactly one npm tarball, exactly one Inkspan Office wheel, and `SHA256SUMS`; +2. build **exactly four regular top-level files** in the release directory: exactly one npm tarball, exactly one Inkspan Office wheel, `inkspan.spdx.json`, and `SHA256SUMS`; the aggregate `inkspan.spdx.json` is the current protected-main SBOM artifact, while any open proposal for package-specific split SBOMs is non-authoritative until protected integration updates both this runbook and the executable release gate; 3. reject missing, duplicate, non-regular, stale, or unexpected local entries and verify the local digests; 4. after upload, query the authenticated paginated GitHub Releases API and require the resumed remote draft asset-name set to equal the local release directory exactly; 5. require every remote asset state to be uploaded and every GitHub-reported `sha256:` digest to equal the exact transferred local file digest; From ab1cc11ddf9148b9db668918fd6057e0b444acc3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 12:27:43 +0900 Subject: [PATCH 13/23] test(docs): keep release inventory ownership separate --- docs/CONTRACTS.md | 4 ++-- docs/OPERABILITY.md | 2 +- src/protectedDocumentationMaturity.test.ts | 20 +------------------- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/docs/CONTRACTS.md b/docs/CONTRACTS.md index 5f1d00499..52ab1bdf0 100644 --- a/docs/CONTRACTS.md +++ b/docs/CONTRACTS.md @@ -138,7 +138,7 @@ Expected degraded states are explicit rather than mapped to false success: A public release binds one exact integrated protected source head to package/artifact identity, applicable CI/security/accessibility/document-fidelity evidence, owned production coverage, public-docstring evidence, SBOM/provenance/reproducibility where configured, formal review requirements, rollback guidance, and post-publication smoke verification. -Before immutable publication, the protected-main canonical draft inventory is **exactly four regular top-level files**: exactly one npm tarball, exactly one Inkspan Office wheel, `inkspan.spdx.json`, and `SHA256SUMS`. The aggregate `inkspan.spdx.json` is the protected-main SBOM artifact; any open proposal to split package-specific SBOMs is not release authority until protected integration updates this contract and executable gate together. Missing, stale, unexpected, duplicate, non-regular, incompletely uploaded, or digest-mismatched assets fail closed. After upload and before publication, the authenticated paginated GitHub Releases API inventory must equal the local release directory by exact asset name, every remote asset must report an uploaded state, and every GitHub-reported `sha256:` digest must equal the digest of the exact transferred local file. The workflow does not silently delete an unexpected remote asset to make an ambiguous draft look clean. +Before immutable publication, the canonical draft inventory is **exactly three regular top-level files**: exactly one npm tarball, exactly one Inkspan Office wheel, and `SHA256SUMS`. Missing, stale, unexpected, duplicate, non-regular, incompletely uploaded, or digest-mismatched assets fail closed. After upload and before publication, the authenticated paginated GitHub Releases API inventory must equal the local release directory by exact asset name, every remote asset must report an uploaded state, and every GitHub-reported `sha256:` digest must equal the digest of the exact transferred local file. The workflow does not silently delete an unexpected remote asset to make an ambiguous draft look clean. Rollback must preserve readable canonical documents and must not require silently reinterpreting persisted schema or selector-projection semantics. Host-owned migrations, persistence rollback, annotation re-anchoring, tenant recovery, and deployment rollback remain host responsibilities unless a future versioned contract explicitly assigns them to Inkspan. @@ -154,7 +154,7 @@ Rollback must preserve readable canonical documents and must not require silentl | Office rendering | deterministic bounded JSON→artifact conversion | file destination policy, downstream distribution, tenant authorization | | naruon composition | stable local package/module boundary | authenticated compose transport, tenancy, provider/model policy | | model assistance | deterministic proposal acceptance boundary | provider, prompt/data policy, credentials, human approval | -| release evidence | exact four-file draft inventory including the protected aggregate SBOM, package/artifact/digest verification and repository evidence | downstream deployment and operational rollout | +| release evidence | exact three-file draft inventory, package/artifact/digest verification and repository evidence | downstream deployment and operational rollout | ## Related canonical documents diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index b188a6583..e96d053c4 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -60,7 +60,7 @@ Release publication occurs only from an exact integrated protected head. The rel Before publication: 1. fetch the current protected `main` ref and require the release tag event commit SHA to equal that exact integration tip, not merely be an ancestor of it; -2. build **exactly four regular top-level files** in the release directory: exactly one npm tarball, exactly one Inkspan Office wheel, `inkspan.spdx.json`, and `SHA256SUMS`; the aggregate `inkspan.spdx.json` is the current protected-main SBOM artifact, while any open proposal for package-specific split SBOMs is non-authoritative until protected integration updates both this runbook and the executable release gate; +2. build exactly three regular top-level release files: exactly one npm tarball, exactly one Inkspan Office wheel, and `SHA256SUMS`; 3. reject missing, duplicate, non-regular, stale, or unexpected local entries and verify the local digests; 4. after upload, query the authenticated paginated GitHub Releases API and require the resumed remote draft asset-name set to equal the local release directory exactly; 5. require every remote asset state to be uploaded and every GitHub-reported `sha256:` digest to equal the exact transferred local file digest; diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index f1782713b..965b2f469 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -77,22 +77,4 @@ describe('protected capability documentation maturity', () => { expect(architecture).not.toContain('Until #116 integrates'); }); - it('keeps canonical release inventory text aligned with the protected workflow', () => { - const workflow = repositoryFile('.github/workflows/release.yml'); - const contracts = repositoryFile('docs/CONTRACTS.md'); - const operability = repositoryFile('docs/OPERABILITY.md'); - const testStrategy = repositoryFile('docs/TEST_STRATEGY.md'); - const doctoring = repositoryFile( - 'docs/doctoring/release-draft-asset-inventory.md', - ); - - expect(workflow).toContain('expected_asset_count=4'); - expect(workflow).toContain('release/inkspan.spdx.json'); - - for (const document of [contracts, operability, testStrategy, doctoring]) { - expect(document).toContain('exactly four regular top-level files'); - expect(document).toContain('inkspan.spdx.json'); - expect(document).not.toContain('exactly three regular top-level files'); - } - }); -}); \ No newline at end of file +}); From 3db0ab998472f9611565d6e8e834fa7a1f1c55cf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 14:44:48 -0700 Subject: [PATCH 14/23] test(docs): pin protected DOCX hyperlink maturity --- src/protectedDocumentationMaturity.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index 965b2f469..f4d28481d 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -77,4 +77,17 @@ describe('protected capability documentation maturity', () => { expect(architecture).not.toContain('Until #116 integrates'); }); + it('does not describe merged DOCX external hyperlinks as active-PR work', () => { + const trd = repositoryFile('docs/TRD.md'); + const adr = repositoryFile( + 'docs/adr/0026-bounded-docx-external-hyperlinks.md', + ); + + expect(adr).toContain('Status: Accepted'); + expect(adr).toContain('protected `main` through PR #137'); + expect(trd).toContain('Accepted ADR 0026'); + expect(trd).toContain('implemented_on_protected_main'); + expect(trd).not.toContain('Active PR #137'); + expect(trd).not.toContain('implemented_on_active_pr under Proposed ADR 0026'); + }); }); From b0e62eadedc5f064d17c82826ae4efbfd1664cec Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 14:47:35 -0700 Subject: [PATCH 15/23] chore(docs): restore documentation-maturity writer scope --- src/protectedDocumentationMaturity.test.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index f4d28481d..965b2f469 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -77,17 +77,4 @@ describe('protected capability documentation maturity', () => { expect(architecture).not.toContain('Until #116 integrates'); }); - it('does not describe merged DOCX external hyperlinks as active-PR work', () => { - const trd = repositoryFile('docs/TRD.md'); - const adr = repositoryFile( - 'docs/adr/0026-bounded-docx-external-hyperlinks.md', - ); - - expect(adr).toContain('Status: Accepted'); - expect(adr).toContain('protected `main` through PR #137'); - expect(trd).toContain('Accepted ADR 0026'); - expect(trd).toContain('implemented_on_protected_main'); - expect(trd).not.toContain('Active PR #137'); - expect(trd).not.toContain('implemented_on_active_pr under Proposed ADR 0026'); - }); }); From 3949217c6dee889feaf1eb6d69b92abfcc664adf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 19:13:33 -0700 Subject: [PATCH 16/23] test(docs): bind protected DOCX hyperlink maturity --- src/protectedDocumentationMaturity.test.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index 965b2f469..b48ba8c2c 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -77,4 +77,20 @@ describe('protected capability documentation maturity', () => { expect(architecture).not.toContain('Until #116 integrates'); }); -}); + it('does not describe merged DOCX hyperlinks as active-PR technical scope', () => { + const trd = repositoryFile('docs/TRD.md'); + 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'); + }); + +}); \ No newline at end of file From e78d6d840694a5a7511a2763801ec3bad11ff386 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 19:15:31 -0700 Subject: [PATCH 17/23] docs(trd): reconcile protected DOCX hyperlink maturity --- docs/TRD.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 9bb37062315e4c7518d0f1133dbff327943ebdbd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 21:50:34 -0700 Subject: [PATCH 18/23] test(docs): preserve newer architecture while enforcing maturity truth --- ARCHITECTURE.md | 12 +++++++----- src/protectedDocumentationMaturity.test.ts | 5 +++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a8a642acf..9073caac0 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -25,6 +25,8 @@ The protected standalone product provides: - dependency-locked Chromium/Firefox/WebKit rich-clipboard release assurance; and - a network-free Office renderer for deterministic DOCX, XLSX, and PPTX output. +A named editor-chrome theme-token catalog and Storybook inventory for repeating toolbar/editor objects are Active PR / Proposed. Hosts override `--cwl-*` on `.cwl-editor`; Inkspan does not own Figma Variables, brand certification, or design-tool sync. + Hosts own transport, authorization, tenant isolation, persistence, credentials, migration, retention, and model-use policy. They also own authentication, deployment, durable audit, print destination policy, and any durable PDF/print-service authority; persistence includes durable storage and commit authority. Inkspan therefore never opens a production collaboration connection, chooses a tenant, stores a provider secret, creates a durable database transaction, decides a retention schedule, authorizes an AI operation, or claims that a browser print destination constitutes a durable authorized export. A standalone adopter can provide those capabilities directly; a CWL host can provide them through shared platform services. @@ -43,7 +45,7 @@ flowchart TB Selector[Protected text-position-selector subpath] Autosave[Protected autosave subpath] Converter[Protected converter subpath] - Markdown[Protected markdown subpath\nAccepted ADR 0020] + Markdown[Protected markdown subpath] SharedPolicy[Shared deterministic URI / inline-image policy] Serializers[Shared deterministic serializers] @@ -58,7 +60,7 @@ flowchart TB Markdown --> Serializers ``` -The protected `@contextualwisdomlab/cwl-editor/markdown` package subpath is governed by Accepted ADR 0020. Protected PR #114 integrated this dependency-isolation boundary without creating a second serializer authority: the root package and the headless subpath share the same deterministic serializers and framework-neutral safe-link/inline-raster policy. The subpath is shipped on protected `main`; release candidates must regenerate its packed-artifact ESM, CommonJS, strict TypeScript, and runtime-authority evidence on the exact candidate head. +Protected `main` includes the framework-independent `@contextualwisdomlab/cwl-editor/markdown` subpath under Accepted ADR 0020. It isolates dependency topology without creating a second serializer authority: the root package and the subpath share the same deterministic serializer and URI/inline-image policy modules. Packed ESM, CommonJS, and strict-TypeScript consumers are required evidence that the subpath remains browserless and independent of React, TipTap UI, Yjs, network, credential, and model authority. A framework-independent subpath must prove its declared dependency boundary from the packed npm artifact under ESM, CommonJS, and strict TypeScript consumers. Source-level import shape alone is insufficient release evidence. @@ -66,7 +68,7 @@ A framework-independent subpath must prove its declared dependency boundary from Inkspan owns the CSS rules it ships for its editor. It does not own the operating system print spooler, printer, browser's pagination implementation, downstream PDF storage, or host disclosure policy. -Protected `main` includes the CSS-only `@media print` boundary defined by Accepted ADR 0021 and integrated by protected PR #116. It: +Protected `main` includes the CSS-only `@media print` presentation boundary governed by Accepted ADR 0021. The shipped boundary: - removes Inkspan-owned screen-only scroll/max-height clipping; - hides toolbar, collaboration status, remote caret/cursor-label, and placeholder UI from printed document output; @@ -74,7 +76,7 @@ Protected `main` includes the CSS-only `@media print` boundary defined by Accept - uses conservative paged-fragmentation hints; and - keeps links distinguishable without relying on color alone. -This protected presentation line does **not** create a JavaScript print mode, PDF service, page-number/header/footer authority, timestamp/signature claim, persistence layer, network requirement, credential, or model dependency. Browser/user-agent pagination and any durable print/PDF artifact remain host/user authority even though Inkspan's own print CSS is shipped. +This protected presentation line does **not** create a JavaScript print mode, PDF service, page-number/header/footer authority, timestamp/signature claim, persistence layer, network requirement, credential, or model dependency. Browser print output remains a presentation representation; durable print/PDF authorization, storage, provenance, retention, and distribution remain host responsibilities. ## Modular MSA composition @@ -188,4 +190,4 @@ An acquisition reviewer should be able to verify the product without receiving p Local-only or restricted evidence includes production documents, selected quote text, conflict bodies, Yjs updates, awareness metadata, provider credentials, authorization claims, private model prompts/outputs, tenant-scoped validators, private print/export destinations, and security findings that expose exploitable deployment detail. -The authoritative naruon composition guide is [`docs/naruon-compose-ui-panel.md`](docs/naruon-compose-ui-panel.md). Standards, claim boundaries, and decision history are recorded in the doctoring and ADR corpus under `docs/doctoring/` and `docs/adr/`. \ No newline at end of file +The authoritative naruon composition guide is [`docs/naruon-compose-ui-panel.md`](docs/naruon-compose-ui-panel.md). Standards, claim boundaries, and decision history are recorded in the doctoring and ADR corpus under `docs/doctoring/` and `docs/adr/`. diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index b48ba8c2c..6192d6020 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -71,7 +71,9 @@ describe('protected capability documentation maturity', () => { expect(stylesheet).toContain('@media print'); expect(adr).toContain('Status: Accepted'); expect(architecture).toContain('Accepted ADR 0021'); - expect(architecture).toContain('protected PR #116'); + 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'); @@ -92,5 +94,4 @@ describe('protected capability documentation maturity', () => { expect(trd).not.toContain('implemented_on_active_pr'); expect(trd).not.toContain('Until protected integration'); }); - }); \ No newline at end of file From 405ee78c39e5f8461d9e1471ebf9397b2ad92724 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 02:26:47 +0900 Subject: [PATCH 19/23] test(ci): cover event-specific Python matrix Signed-off-by: Seongho Bae --- office/tests/test_python_support_contract.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/office/tests/test_python_support_contract.py b/office/tests/test_python_support_contract.py index 7104fd661..209f48454 100644 --- a/office/tests/test_python_support_contract.py +++ b/office/tests/test_python_support_contract.py @@ -50,10 +50,14 @@ def test_python_support_range_matches_classifiers_and_ci_matrix() -> None: office_job = _workflow_job_block(workflow, "office") assert "runs-on: ubuntu-24.04" in office_job assert "runs-on: ubuntu-latest" not in office_job - matrix_match = re.search(r'python-version:\s*\[([^\]]+)\]', office_job) + matrix_match = re.search(r"python-version:\s*(.+)", office_job) assert matrix_match is not None - matrix_versions = tuple(re.findall(r'"(3\.\d+)"', matrix_match.group(1))) - assert matrix_versions == SUPPORTED_PYTHON_VERSIONS + pull_request_versions, push_versions = ( + tuple(re.findall(r'"(3\.\d+)"', versions)) + for versions in re.findall(r"fromJSON\('(\[[^']+\])'\)", matrix_match.group(1)) + ) + assert pull_request_versions == (SUPPORTED_PYTHON_VERSIONS[-1],) + assert push_versions == SUPPORTED_PYTHON_VERSIONS def test_python_support_documentation_matches_the_fixed_ci_environment() -> None: From c75a8b85c00d696e1f5a9171faa8cfad408ef144 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 02:32:54 +0900 Subject: [PATCH 20/23] test(ci): bind Python matrix to event Signed-off-by: Seongho Bae --- office/tests/test_python_support_contract.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/office/tests/test_python_support_contract.py b/office/tests/test_python_support_contract.py index 209f48454..a52ddec39 100644 --- a/office/tests/test_python_support_contract.py +++ b/office/tests/test_python_support_contract.py @@ -50,11 +50,16 @@ def test_python_support_range_matches_classifiers_and_ci_matrix() -> None: office_job = _workflow_job_block(workflow, "office") assert "runs-on: ubuntu-24.04" in office_job assert "runs-on: ubuntu-latest" not in office_job - matrix_match = re.search(r"python-version:\s*(.+)", office_job) + matrix_match = re.search( + r"python-version:\s*\$\{\{\s*github\.event_name\s*==\s*'pull_request'" + r"\s*&&\s*fromJSON\('(\[[^']+\])'\)\s*\|\|\s*" + r"fromJSON\('(\[[^']+\])'\)\s*\}\}", + office_job, + ) assert matrix_match is not None pull_request_versions, push_versions = ( tuple(re.findall(r'"(3\.\d+)"', versions)) - for versions in re.findall(r"fromJSON\('(\[[^']+\])'\)", matrix_match.group(1)) + for versions in matrix_match.groups() ) assert pull_request_versions == (SUPPORTED_PYTHON_VERSIONS[-1],) assert push_versions == SUPPORTED_PYTHON_VERSIONS From 8306b775fc0ce9023594704727f3e4a4ab3b51ee Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:18:01 +0900 Subject: [PATCH 21/23] revert(ci): restore Office contract owner Remove the duplicated Python support contract changes from this documentation-maturity branch. PR #405 remains the single writer while this branch keeps its canonical documentation delta. Signed-off-by: Seongho Bae Commit-Message-Assisted-by: Claude (via Claude Code) --- office/tests/test_python_support_contract.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/office/tests/test_python_support_contract.py b/office/tests/test_python_support_contract.py index a52ddec39..7104fd661 100644 --- a/office/tests/test_python_support_contract.py +++ b/office/tests/test_python_support_contract.py @@ -50,19 +50,10 @@ def test_python_support_range_matches_classifiers_and_ci_matrix() -> None: office_job = _workflow_job_block(workflow, "office") assert "runs-on: ubuntu-24.04" in office_job assert "runs-on: ubuntu-latest" not in office_job - matrix_match = re.search( - r"python-version:\s*\$\{\{\s*github\.event_name\s*==\s*'pull_request'" - r"\s*&&\s*fromJSON\('(\[[^']+\])'\)\s*\|\|\s*" - r"fromJSON\('(\[[^']+\])'\)\s*\}\}", - office_job, - ) + matrix_match = re.search(r'python-version:\s*\[([^\]]+)\]', office_job) assert matrix_match is not None - pull_request_versions, push_versions = ( - tuple(re.findall(r'"(3\.\d+)"', versions)) - for versions in matrix_match.groups() - ) - assert pull_request_versions == (SUPPORTED_PYTHON_VERSIONS[-1],) - assert push_versions == SUPPORTED_PYTHON_VERSIONS + matrix_versions = tuple(re.findall(r'"(3\.\d+)"', matrix_match.group(1))) + assert matrix_versions == SUPPORTED_PYTHON_VERSIONS def test_python_support_documentation_matches_the_fixed_ci_environment() -> None: From 69aa4e4cf71eb4f7428d8838e4ee2ffeb2f069e3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:53:06 +0900 Subject: [PATCH 22/23] test(docs): reproduce unrelated proposal maturity rejection Signed-off-by: Seongho Bae --- src/protectedDocumentationMaturity.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index 6192d6020..f4d189ef8 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -79,8 +79,11 @@ describe('protected capability documentation maturity', () => { expect(architecture).not.toContain('Until #116 integrates'); }); - it('does not describe merged DOCX hyperlinks as active-PR technical scope', () => { - const trd = repositoryFile('docs/TRD.md'); + 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 trd = repositoryFile('docs/TRD.md') + unrelatedProposal; const adr = repositoryFile( 'docs/adr/0026-bounded-docx-external-hyperlinks.md', ); @@ -94,4 +97,4 @@ describe('protected capability documentation maturity', () => { expect(trd).not.toContain('implemented_on_active_pr'); expect(trd).not.toContain('Until protected integration'); }); -}); \ No newline at end of file +}); From 302cdf981090e48ae1361569c29030346180528b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:53:41 +0900 Subject: [PATCH 23/23] fix(docs): scope hyperlink maturity to its own records Signed-off-by: Seongho Bae --- src/protectedDocumentationMaturity.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/protectedDocumentationMaturity.test.ts b/src/protectedDocumentationMaturity.test.ts index f4d189ef8..68650e150 100644 --- a/src/protectedDocumentationMaturity.test.ts +++ b/src/protectedDocumentationMaturity.test.ts @@ -83,7 +83,11 @@ describe('protected capability documentation maturity', () => { '', '\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 trd = repositoryFile('docs/TRD.md') + 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', );