From 6701af5cc9d3966924ca87952590804ccd0ae4d2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 03:08:54 +0900 Subject: [PATCH 1/2] docs: rebase canonical acquisition baseline onto current main --- AGENTS.md | 16 + CHANGELOG.md | 3 + CLAUDE.md | 42 ++ docs/CONTRACTS.md | 138 +++++++ docs/DATA_MODEL.md | 125 ++++++ docs/DOCUMENTATION_FITNESS.md | 101 +++++ docs/OPERABILITY.md | 96 +++++ docs/PRD.md | 132 ++++++ docs/README.md | 51 +++ docs/TEST_STRATEGY.md | 77 ++++ docs/THREAT_MODEL.md | 87 ++++ docs/TRACEABILITY.md | 79 ++++ docs/TRD.md | 110 +++++ docs/UML.md | 382 ++++++++++++++++++ docs/adr/0001-product-host-authority.md | 41 ++ docs/adr/0002-document-revision-authority.md | 41 ++ docs/adr/0003-safe-rich-clipboard.md | 41 ++ docs/adr/0004-durable-validator-autosave.md | 44 ++ .../0005-revision-scoped-review-evidence.md | 41 ++ docs/adr/0006-ssr-native-form-boundary.md | 42 ++ .../0007-provider-neutral-collaboration.md | 41 ++ .../0008-deterministic-office-rendering.md | 41 ++ docs/adr/0009-naruon-modular-composition.md | 41 ++ docs/adr/0010-release-evidence-authority.md | 41 ++ ...terministic-vs-model-assisted-authoring.md | 43 ++ .../adr/0012-spreadsheet-formula-injection.md | 43 ++ docs/adr/0013-atomic-file-publication.md | 43 ++ docs/adr/0014-local-assets-font-licensing.md | 45 +++ .../0015-envelope-schema-migration-routing.md | 68 ++++ .../0016-cross-engine-browser-assurance.md | 72 ++++ .../adr/0017-security-disclosure-lifecycle.md | 80 ++++ docs/adr/README.md | 49 +++ ...nicalDocumentationDecisionCoverage.test.ts | 108 +++++ src/canonicalProductDocumentation.test.ts | 290 +++++++++++++ 34 files changed, 2694 insertions(+) create mode 100644 CLAUDE.md create mode 100644 docs/CONTRACTS.md create mode 100644 docs/DATA_MODEL.md create mode 100644 docs/DOCUMENTATION_FITNESS.md create mode 100644 docs/OPERABILITY.md create mode 100644 docs/PRD.md create mode 100644 docs/README.md create mode 100644 docs/TEST_STRATEGY.md create mode 100644 docs/THREAT_MODEL.md create mode 100644 docs/TRACEABILITY.md create mode 100644 docs/TRD.md create mode 100644 docs/UML.md create mode 100644 docs/adr/0001-product-host-authority.md create mode 100644 docs/adr/0002-document-revision-authority.md create mode 100644 docs/adr/0003-safe-rich-clipboard.md create mode 100644 docs/adr/0004-durable-validator-autosave.md create mode 100644 docs/adr/0005-revision-scoped-review-evidence.md create mode 100644 docs/adr/0006-ssr-native-form-boundary.md create mode 100644 docs/adr/0007-provider-neutral-collaboration.md create mode 100644 docs/adr/0008-deterministic-office-rendering.md create mode 100644 docs/adr/0009-naruon-modular-composition.md create mode 100644 docs/adr/0010-release-evidence-authority.md create mode 100644 docs/adr/0011-deterministic-vs-model-assisted-authoring.md create mode 100644 docs/adr/0012-spreadsheet-formula-injection.md create mode 100644 docs/adr/0013-atomic-file-publication.md create mode 100644 docs/adr/0014-local-assets-font-licensing.md create mode 100644 docs/adr/0015-envelope-schema-migration-routing.md create mode 100644 docs/adr/0016-cross-engine-browser-assurance.md create mode 100644 docs/adr/0017-security-disclosure-lifecycle.md create mode 100644 docs/adr/README.md create mode 100644 src/canonicalDocumentationDecisionCoverage.test.ts create mode 100644 src/canonicalProductDocumentation.test.ts diff --git a/AGENTS.md b/AGENTS.md index 1de8ae17..108387c3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,21 @@ # AGENTS.md +## Canonical product and architecture authority + +Protected `main` is Inkspan's implementation authority. Before changing product behavior, public contracts, architecture, security boundaries, tests, release behavior, or integration guidance, start from `docs/README.md` and keep `docs/PRD.md`, `docs/TRD.md`, and `docs/CONTRACTS.md` aligned with the current implementation and accepted ADRs. + +Inkspan owns deterministic editor/conversion behavior, versioned document/evidence contracts, local autosave coordination, accessibility metadata, package behavior, and provider-neutral adapters. Hosts retain transport, authentication, authorization, tenant isolation, durable persistence, credentials, migration execution, retention, deployment, durable audit, collaboration-provider authority, and model-use policy unless an accepted versioned contract explicitly changes that boundary. + +Do not infer shipped behavior from conversation history, PR bodies, model output, or predecessor-head evidence. Keep Proposed/Active-PR behavior distinct from implementation on Protected `main`, preserve fail-closed security and deterministic conversion boundaries, and update the smallest affected canonical documents plus tests when a durable contract changes. + +## Autonomous maintenance execution discipline + +When an external scheduler or autonomous maintainer is operating on Inkspan, repository work is **work-conserving**: after every mutation, proof, merge, closure, review/check observation, or defer decision, select the next highest-value safe Inkspan action while practical execution budget remains. A blocked PR blocks only that lane; queued CI, reviewer latency, provider cooldown, a read-only dependency, or missing approval must not freeze unrelated work. + +A status report, prompt update, documentation assessment, green check, PR creation, review request, or one completed product slice is an intermediate result rather than repository completion while another safe action exists. Before ending an autonomous run, re-scan open PRs/issues, protected `main`, changed branches, reviews/checks/security findings, documentation fitness, release evidence, and buyer-visible gaps; continue if any executable item remains. + +The external scheduler remains the execution authority for cadence and run continuation. These repository instructions define Inkspan-specific writer, evidence, product, and safety discipline only; they do not make scheduling or autonomous orchestration an Inkspan runtime capability. + ## Code-owner review gates — disabled (on hold) As of 2026-08-04, code-owner review requirements (`require_code_owner_reviews` in branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 8129f3a4..cdc35f43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,9 @@ Historical release entries from **0.1.0 through 0.5.27** are preserved verbatim - Added browser-DOM handoff tests proving the field retains and updates the selected value before TipTap exists while reset-only unnamed fields remain empty ### Documentation +- Added a canonical acquisition documentation spine covering product requirements, technical requirements, public interface/integration contracts, Mermaid UML, a conceptual data/evidence model, a threat model, test strategy, operability/recovery, standards/evidence traceability, and seventeen linked architecture decision records without inventing Inkspan-owned persistence or host authority; the newest decisions make envelope schema identity/host-owned migration routing, cross-engine browser-semantic release assurance, and the protected security-disclosure lifecycle first-class while keeping unimplemented capabilities explicitly planned +- Added machine-checkable canonical-documentation decision coverage that keeps required files, ADR index links and completeness, migration-routing and browser-assurance UML/data-model/traceability evidence, physical-ERD non-applicability, browser-security evidence, offline font provenance/no-runtime-font-egress, standards references, rollback sections, host-vs-Inkspan authority boundaries, implemented-vs-active-PR status, and work-conserving autonomous-maintenance guidance synchronized +- Documented work-conserving autonomous-maintenance governance in `AGENTS.md` and `CLAUDE.md`: a blocked PR blocks only its lane, status/report/prompt/documentation milestones are intermediate while safe work remains, and the external scheduler owns cadence rather than becoming an Inkspan runtime capability - Added a repository-native security disclosure and vulnerability-handling policy with supported pre-1.0 release lines, private GitHub Security Advisory routing and safe public fallback, minimized evidence guidance, explicit Inkspan-versus-host ownership boundaries, no-SLA and non-conformance claim limits, deterministic documentation tests, and APA 7th doctoring grounded in current ISO/IEC 29147:2018, ISO/IEC 30111:2019, final NIST SP 800-218 SSDF 1.1, the draft-status boundary for SSDF 1.2, and GitHub primary documentation - Documented that revision-scoped selection evidence contains no selected text or complete document envelope, remains valid only for its exact document revision, is not a W3C `TextPositionSelector`, and leaves durable comments, authorization, persistence, collaborative anchoring, and cross-revision re-anchoring to the host - Added an APA 7th doctoring record for the selection/revision atomicity, privacy, rollback, and interoperability boundaries grounded in ProseMirror, RFC 9110, and the W3C Web Annotation Data Model diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..6b725289 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,42 @@ +# CLAUDE.md + +## Repository authority + +Protected `main` is Inkspan's implementation authority. Start architecture, product, interface, security, testing, and operability work from the canonical graph in `docs/README.md`; in particular, keep `docs/PRD.md`, `docs/TRD.md`, and `docs/CONTRACTS.md` aligned with shipped behavior and accepted ADRs rather than reconstructing product intent from conversations or pull-request prose. + +## Product boundary + +Inkspan owns deterministic Markdown/HTML authoring and conversion behavior, versioned document/evidence contracts, local autosave coordination, accessibility metadata, package behavior, and provider-neutral adapters. The embedding host owns authenticated transport, authorization, tenant isolation, durable persistence, credentials, migration execution, retention, deployment, durable audit, collaboration-provider authority, and model-use policy unless an accepted versioned contract explicitly changes that boundary. + +Keep deterministic conversion separate from model-assisted authoring. Model output is an untrusted proposal and cannot bypass editor/document/clipboard/Office validation, host authorization, user approval, or durable concurrency controls. + +## Security and reliability invariants + +- Preserve strict fail-closed handling for untrusted clipboard HTML, links, image sources, document envelopes, Office structures, host callbacks, collaboration updates, and model proposals. +- Office rendering remains network-free, macro-free, model-free, Desktop-Office-free, bounded, formula-injection-safe, and race-safe at publication. +- Local SHA-256 document revisions are equality evidence, not authorization, signatures, tenant identity, server time, or durable-write receipts. +- Host/server-selected strong validators remain the durable compare-and-swap authority. +- Do not place credentials, complete document bodies, tenant identifiers, prompts/model output, durable validators, or private exception causes into generic diagnostics or telemetry. +- Do not move transport, persistence, tenancy, credential, retention, provider, or deployment ownership into Inkspan merely to make a local feature or test easier. + +## Change and evidence discipline + +Use test-first changes for product behavior and permanent contracts where practical. Preserve exact owned production statement/branch/function/line coverage and public-docstring requirements enforced by repository CI. Validate public package behavior from packed artifacts, not source imports alone. + +For architectural changes, update the smallest affected canonical records and ADRs. Keep status language explicit: implemented on protected main, active PR/proposed, accepted architecture, planned, research only, superseded, or out of scope. + +A queued, pending, cancelled, skipped-required, stale-head, predecessor-head, status-only, comment-only, author-only, or synthetic-merge result is not acceptance evidence. Formal review, automated review, repository checks, host authorization, and release evidence remain distinct authorities. + +## Autonomous maintenance execution discipline + +When an external scheduler or autonomous maintainer drives Inkspan work, execution is **work-conserving**. After each mutation, proof, merge, closure, review/check observation, or defer decision, choose the next highest-value safe Inkspan item while practical execution budget remains. A blocked PR blocks only that lane; do not let queued CI, reviewer latency, provider cooldown, a read-only dependency, or missing approval freeze unrelated source, documentation, operability, or product work. + +Do not use a status report, prompt update, documentation assessment, green check, PR creation, review request, or one completed product slice as a stopping condition while another safe action exists. Before ending an autonomous run, re-scan open PRs/issues, protected `main`, changed branches, review/check/security evidence, canonical-document fitness, release readiness, and buyer-visible gaps; continue when an executable item remains. + +The external scheduler remains the execution authority for cadence and continuation. This file constrains repository-specific writer/evidence/product/safety behavior and does not make scheduling or autonomous orchestration part of Inkspan runtime architecture. + +## Integration discipline + +Inkspan must remain independently usable. naruon and other CWL hosts compose it through narrow host-owned boundaries; they are not required runtime dependencies. Central `.github`, contextual-orchestrator, and other repositories are external bounded contexts and must not be locally patched around when they own a shared control-plane defect. + +Before changing public behavior, inspect `docs/README.md`, `docs/PRD.md`, `docs/TRD.md`, `docs/CONTRACTS.md`, `ARCHITECTURE.md`, the relevant ADRs, and current tests. Prefer the smallest root-cause-changing change with explicit rollback and compatibility evidence. diff --git a/docs/CONTRACTS.md b/docs/CONTRACTS.md new file mode 100644 index 00000000..54ddaef6 --- /dev/null +++ b/docs/CONTRACTS.md @@ -0,0 +1,138 @@ +# Inkspan Interface and Integration Contracts + +Status: Proposed canonical baseline + +This document is the discoverable contract index for Inkspan's public product boundaries. Protected `main` remains the implementation authority. Open pull requests may describe Proposed behavior, but they are not shipped until protected integration and exact-head acceptance. + +## Contract principles + +Inkspan owns deterministic editor state, versioned document-envelope semantics, local revision/evidence primitives, local autosave ordering, deterministic conversion, accessibility metadata, and provider-neutral adapter behavior. The embedding **host** owns transport, authentication, authorization, tenant isolation, durable persistence, credentials, migrations, retention, deployment, durable audit storage, collaboration-provider lifecycle, and model-use policy. + +Every contract is versioned or otherwise compatibility-bounded, deterministic where the domain permits it, fail closed on malformed or unsupported inputs, and explicit about degraded behavior. No contract silently promotes generated output, model output, a local digest, a browser field, a collaboration update, or a workflow status into authorization or durable-persistence authority. + +## Public package and module contract + +The root package may expose React/TipTap integration plus framework-independent helpers. Framework-independent subpaths must remain consumable without React, React DOM, TipTap UI, ProseMirror view, Yjs, browser globals, network access, database clients, credentials, or model SDKs unless that subpath's versioned contract explicitly requires them. + +Package exports, declaration files, runtime support, and packed ESM/CommonJS/strict-TypeScript consumers are release evidence. A source file being present is not sufficient proof that a public contract is packaged correctly. + +## Document envelope and revision contract + +The **document envelope** is a versioned boundary carrying schema identity and supported document content. Current-schema parsing validates strict JSON/UTF-8 and active schema requirements before canonicalization or hashing. Unknown schema migration remains host-owned. + +Canonical revision evidence is content-equality metadata only. It is not an actor identity, tenant identity, authorization grant, signature, timestamp, durable write receipt, or bearer credential. When a future identity-only migration-routing helper is implemented, it may expose bounded schema identity without becoming a permissive parser or migration executor. + +Compatibility rule: a new schema or incompatible interpretation requires an explicit new versioned contract and migration story; existing persisted semantics are not silently redefined. + +## Editor authoring contract + +Markdown/HTML authoring uses the supported TipTap/ProseMirror editor surface as the deterministic editing authority. Import/export adapters must state which constructs are supported, lossy, or rejected. Rendered or exported representations do not silently become the canonical source document. + +The editor surface may expose callbacks and handles for local state, revision capture, selection evidence, autosave coordination, and accessibility metadata. Callbacks are ordinary host code and cannot be trusted to preserve product invariants; Inkspan isolates callback failures at documented boundaries. + +## Event and evidence contract + +Local evidence records describe narrowly scoped facts such as revision identity, document transition, selection coordinates, lifecycle state, conversion warnings, or publication outcome. Evidence classes remain separate: + +- local deterministic evidence; +- workflow/check evidence; +- formal review evidence; +- host authorization evidence; +- durable persistence evidence; and +- release/publication evidence. + +No single status collapses those authorities. Ordinary evidence must avoid embedding complete document bodies, credentials, tenant identifiers, prompts, model outputs, or private exception causes unless a separate authorized contract explicitly requires them. + +## Autosave contract + +Autosave is a local coordination surface. It provides bounded single-flight ordering, bounded pending work, explicit blocked/conflict/failure state, optional lifecycle observation, and host callback handoff. The host performs authenticated transport and durable persistence. + +A server-selected strong validator is the durable concurrency authority. It advances only from a validated successful host result. Conflict, timeout ambiguity, malformed callback output, host failure, promise/reflection failure, or observer failure never fabricates durable success. + +Lifecycle observation emits only distinct externally visible document-free states. Construction and no-op operations do not manufacture events. The explicit local `getSnapshot()` coordination API may expose bounded active/pending/last-saved strong-validator fields defined by the autosave contract; those values remain confidential local concurrency metadata and are not generic diagnostics or telemetry. + +Degraded behavior: when host persistence is unavailable or ambiguous, Inkspan preserves local state and explicit blocked/failure evidence rather than inventing a durable save. + +## Collaboration contract + +Inkspan collaboration is **provider-neutral**. A host may supply Yjs-compatible document, awareness, and provider bindings, but Inkspan does not own production room admission, user identity, authorization, provider credentials, reconnect policy, update persistence, retention, encryption, or durable audit. + +Collaboration document content and awareness state are untrusted tenant data. Awareness presence is not authorization. Provider outage is a host-owned degraded mode; Inkspan must not synthesize remote durability or identity from local Yjs state. + +No secret is required by the framework-independent collaboration contract itself. Provider credentials remain host-owned and must not be embedded in editor configuration or document content. + +## Naruon modular composition contract + +A naruon host may compose Inkspan through narrow `compose` / `ui.panel` integration while Inkspan remains independently usable. The host supplies serializable non-secret configuration, authenticated API transport, durable validators, provider lifecycle, tenant/document identity, accessible conflict/recovery UX, and optional model routing. + +Inkspan does not directly access a naruon application database, credential store, tenant table, message bus, or deployment control plane. Cross-document mounts require explicit host identity/state so local editor/autosave state cannot bleed between documents. + +No database is owned by this composition contract. Durable host data models and migrations stay outside Inkspan unless a future accepted ADR explicitly transfers ownership. + +## Model-assisted authoring contract + +Model-assisted authoring is separate from deterministic conversion and validation. A host may submit selected or derived context to an approved model path and receive a proposal. The proposal is untrusted content until the host/user accepts it and Inkspan's deterministic document/clipboard/conversion validators accept the resulting state. + +The host owns model/provider selection, credentials, external-data-use approval, redaction, prompt retention, model logging, tenancy, authorization, human approval, and audit. No model may authorize a save, bypass deterministic validation, or redefine a revision/durable-validator contract. + +## Deterministic Office conversion contract + +Office rendering accepts versioned bounded JSON and produces supported DOCX/XLSX/PPTX artifacts without model, network, macro, or Desktop Office dependency. Inputs must satisfy XML 1.0, size/depth/container/cycle, spreadsheet, worksheet-name, freeze-pane, supported-structure, and formula-injection rules before publication. + +File publication is explicit about overwrite semantics and uses race-safe publication behavior. Partial or failed writes are not successful artifacts. Reproducible metadata and package/document-fidelity tests define supported claims; unsupported Office constructs are rejected or documented as unsupported rather than silently approximated. + +## Security and data authority contract + +Untrusted clipboard HTML, DOM capabilities, native form values, Office structures, host callbacks, collaboration updates, and model proposals are data, not instruction or authority. Active content, external resource fetching, macros, formula execution, malicious descriptors/accessors, and malformed structures remain behind explicit fail-closed boundaries. + +No secret, credential, durable validator, tenant identifier, complete document body, or model prompt belongs in generic public diagnostics or telemetry. Hosts own authenticated transport, authorization, encryption, retention, tenant isolation, durable audit, incident response, and deployment controls. + +## Versioning and schema evolution + +Public types, schema identifiers, event/evidence shapes, package subpaths, and host integration behavior must evolve compatibly or under a new explicit version. Breaking behavior requires documented migration and rollback guidance. Unknown versions fail closed unless a specifically bounded identity-routing contract allows inspection without interpretation. + +A compatibility claim must be backed by packed-package consumers, schema fixtures, migration/rollback evidence where relevant, and the supported runtime/version matrix. + +## Failure and degraded-mode contract + +Expected degraded states are explicit rather than mapped to false success: + +- host persistence unavailable or ambiguous -> local save state remains blocked/failed; +- collaboration provider unavailable -> local editing may continue only according to host policy, with no remote-durability claim; +- unsupported or malformed document schema -> fail closed or route through an explicit host migration path; +- conversion validation/publication failure -> no successful artifact claim; +- model/provider unavailable -> deterministic authoring/conversion remains authoritative and the model proposal path is unavailable; +- review/check/release evidence missing -> merge/release remains blocked even if source-local tests pass. + +## Release and rollback contract + +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. + +Rollback must preserve readable canonical documents and must not require silently reinterpreting persisted schema semantics. Host-owned migrations, persistence rollback, tenant recovery, and deployment rollback remain host responsibilities unless a future versioned contract explicitly assigns them to Inkspan. + +## Contract-to-authority map + +| Contract surface | Inkspan authority | Host authority | +| --- | --- | --- | +| Markdown/HTML editing | deterministic editor state and supported import/export semantics | application workflow, document ownership, authorization | +| document envelope/revision | schema validation, canonical bytes, local equality evidence | migration orchestration, durable storage, signatures, tenant binding | +| autosave | local ordering/state, callback contract, validator validation | transport, durable CAS, retry/offline policy, persistence | +| collaboration | provider-neutral editor/Yjs binding | provider lifecycle, rooms, identity, authorization, persistence, awareness privacy | +| 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 | + +## Related canonical documents + +- `docs/PRD.md` — buyer/user requirements and accepted outcomes. +- `docs/TRD.md` — technical invariants and runtime boundaries. +- `ARCHITECTURE.md` — protected-main implementation architecture. +- `docs/UML.md` and `docs/DATA_MODEL.md` — interaction and conceptual data/evidence views. +- `docs/THREAT_MODEL.md` — threat analysis and trust boundaries. +- `docs/TEST_STRATEGY.md` — machine evidence required for these contracts, including the exact release draft inventory/digest gate. +- `docs/OPERABILITY.md` — failure, recovery, rollback, incident ownership, and release draft reconciliation. +- `docs/TRACEABILITY.md` — standards, research, and implementation evidence traceability. +- `docs/adr/README.md` — detailed architectural decisions. diff --git a/docs/DATA_MODEL.md b/docs/DATA_MODEL.md new file mode 100644 index 00000000..b2aaa0fd --- /dev/null +++ b/docs/DATA_MODEL.md @@ -0,0 +1,125 @@ +# Inkspan Conceptual Data and Evidence Model + +Status: Proposed canonical baseline + +Inkspan does **not** own an application database in the current architecture. This document is a conceptual/logical model of runtime value objects, conversion/release evidence, and host-owned boundaries. It must not be read as physical DDL. Entities marked host-owned may be persisted by an embedding product, but their physical schema is outside Inkspan authority. + +## Logical ERD + +```mermaid +erDiagram + document_envelope ||--|| document_revision : derives + document_envelope ||--o| document_schema_identity : may_identify + editor_session ||--o{ document_revision : observes + document_revision ||--o{ document_transition : previous_or_resulting + document_revision ||--o{ selection_evidence : binds + document_revision ||--o{ autosave_revision : queues + autosave_revision }o--|| autosave_snapshot : appears_in + durable_validator ||--o{ autosave_snapshot : accompanies + + collaboration_document ||--o{ provider_binding : supplied_through + collaboration_document ||--o{ awareness_state : exposes + provider_binding }o--|| host_capability : requires + collaboration_document }o--o{ editor_session : coordinates + document_schema_identity }o--|| host_capability : routes_migration_through + + clipboard_policy ||--o{ document_envelope : constrains_input + clipboard_policy ||--o{ browser_assurance_evidence : verified_by + browser_assurance_evidence ||--o{ browser_difference_allowance : may_admit + + document_envelope ||--o{ conversion_request : source_for + conversion_request ||--o| conversion_artifact : produces + conversion_request ||--o{ render_warning : records + conversion_artifact ||--o{ audit_event : host_may_record + + release_artifact }o--|| release_evidence : verified_by + release_evidence ||--o{ browser_assurance_evidence : may_require + release_evidence ||--o{ audit_event : host_or_release_system_records +``` + +## Inkspan-owned in-memory or package value objects + +- `document_envelope`: versioned, strictly validated, canonicalizable complete document value. It can contain the complete document body. Inkspan constructs/validates it; a host may persist it under its own policy. +- `document_revision`: SHA-256 equality evidence derived from one exact canonical envelope. It is not authorization, tenant identity, actor identity, timestamp, signature, or proof of a durable write. +- `editor_session`: conceptual local editor/runtime lifetime. It binds one mounted editor state to local evidence and host callbacks. It is not a durable account/session record and has no authentication authority. +- `document_transition`: previous/resulting revision pair plus changed classification. It deliberately omits the document body from ordinary evidence. +- `selection_evidence`: ProseMirror structural coordinates bound to one exact revision. It is a local evidence value, not a durable cross-revision anchor. +- `autosave_revision`: detached immutable revision evidence accepted by the local single-flight autosave coordinator. +- `autosave_snapshot`: frozen document-free queue/session lifecycle metadata such as idle/saving/blocked/closing/closed and bounded pending state. The explicit in-process snapshot may also carry the bounded active/pending/last-saved strong-validator fields defined by the autosave contract; those fields are confidential local concurrency metadata rather than generic telemetry. +- `clipboard_policy`: bounded local policy describing the supported semantic rich-paste boundary. It grants no host network or tenant authority. +- `conversion_request`: versioned deterministic conversion intent. It identifies the supported source representation, requested target such as Markdown/HTML/DOCX/XLSX/PPTX, explicit output/publication options, and validated render configuration. It is a runtime value, not a durable job record. +- `conversion_artifact`: completed deterministic conversion result or artifact identity produced only after validation/build/publication succeeds. A partial/failed output is not a `conversion_artifact` success. +- `render_warning`: bounded structured warning or limitation attached to a supported conversion request/result when the contract permits warning-level evidence. It must not contain secrets or uncontrolled complete document bodies in generic telemetry. +- `release_artifact`: package/wheel/checksum or other expected artifact considered for release. +- `release_evidence`: exact-source artifact inventory/digest/provenance/verification evidence used before publication. + +## Planned value and evidence objects + +The following **planned value and evidence objects** make already accepted future boundaries visible in the conceptual ERD without claiming that their APIs or persistence exist on protected `main`: + +- `document_schema_identity`: proposed frozen identity-only routing metadata containing the bounded envelope `schemaId` and `schemaVersion`. It contains no document body and does not validate unknown-version document semantics. ADR 0015 and Issue #74 define the planned public routing aid; the host owns migration selection/execution and persistence. +- `browser_assurance_evidence`: proposed exact-head release evidence from the same committed rich-clipboard corpus executed in required Chromium, Firefox, and WebKit projects. It records bounded public fixture/corpus, browser-revision, package-lock, platform and source identity rather than tenant clipboard data. +- `browser_difference_allowance`: proposed reviewed explanation for one standards-permitted browser serialization difference. It is attached only to focused evidence with threat analysis and rollback; it never acts as a generic normalization rule or approval substitute. + +These values may remain ephemeral or release-artifact metadata. Their presence in the logical model does not create an Inkspan application database or transfer host authority. + +## Host-owned conceptual entities and boundaries + +- `durable_validator`: host/server-selected strong HTTP entity tag used for durable compare-and-swap. Inkspan validates/coordinates the value but does not select durable server state. +- `collaboration_document`: host-owned Yjs-compatible collaborative document state supplied to Inkspan. Room identity, tenant membership, persistence and retention remain host responsibilities. +- `awareness_state`: host/provider-governed ephemeral collaboration presence metadata. It can contain sensitive tenant information and is not authorization evidence. +- `provider_binding`: host-created connection/binding between an Inkspan collaboration adapter and a collaboration provider/document. Inkspan does not own credentials, reconnect policy, provider creation/destruction, or durable update storage. +- `host_capability`: conceptual set of explicitly supplied host capabilities such as durable save callback, authenticated API, collaboration provider, external model proposal surface, file-output authority, migration registry, or naruon panel composition. Absence of a capability means Inkspan must not invent it. +- `audit_event`: host/release-system owned durable event for actor, authorization, durable save, migration, provider, security, or release activity. Inkspan local revisions/transitions/snapshots do not substitute for authenticated durable audit records. + +## Ownership and lifecycle matrix + +| Entity | Current physical persistence owner | Typical lifecycle | Contains complete document body? | Authority claim | +|---|---|---|---|---| +| `document_envelope` | host if persisted | document revision | yes | deterministic document value only | +| `document_schema_identity` | none required; planned | one routing inspection | no | schema-route metadata only | +| `document_revision` | local or host metadata by policy | derived per exact content | no | equality only | +| `editor_session` | none required | mounted editor runtime | may reference local state | no auth/session authority | +| `document_transition` | none required; host may store | change evidence | no | content-lineage evidence only | +| `selection_evidence` | none required | review/selection capture | no | exact-revision coordinates only | +| `autosave_revision` | none required | queued local save evidence | envelope-bearing evidence may be retained boundedly by queue | local save ordering only | +| `autosave_snapshot` | none required | lifecycle observation/coordination | no | local machine state only; validator fields remain confidential metadata | +| `durable_validator` | host | durable version | no | host concurrency evidence, not authorization | +| `collaboration_document` | host/provider | collaborative room/document | yes, as Yjs state | host/provider authority | +| `awareness_state` | host/provider | ephemeral presence | not normally document body | no authorization | +| `provider_binding` | host | mount/connection lifecycle | no | host connection capability | +| `host_capability` | host/runtime configuration | component mount | no | explicit capability only | +| `conversion_request` | none required | one deterministic conversion | may reference/contain requested source content | conversion intent only | +| `conversion_artifact` | caller/host filesystem or artifact store | successful render/export | yes, rendered form | successful deterministic output only | +| `render_warning` | none required; host may log under policy | conversion result | no by default | warning/limitation only | +| `browser_assurance_evidence` | release system if retained; planned | one exact-head browser gate | public fixture metadata only | release assurance evidence only | +| `browser_difference_allowance` | release system if retained; planned | focused engine difference | no tenant content | reviewed safe-difference rationale only | +| `audit_event` | host/release system | durable operational history | should avoid complete body unless policy requires | authenticated host/release evidence | +| `release_artifact` | release system | build/release | package content | candidate artifact only | +| `release_evidence` | release system | exact-head publication | no tenant document content | artifact/source verification only | + +## Temporal, tenant, provenance, and version dimensions + +The current Inkspan runtime does not create a tenant database, but products embedding it depend on temporal/version provenance boundaries: + +- `document_envelope` carries an explicit schema/version contract; unknown versions require host-owned migration routing rather than permissive parsing. +- the planned `document_schema_identity` may expose only bounded routing metadata; it never validates or migrates the unsupported document body and never creates a durable version claim. +- `document_revision`, selection, transition, and autosave evidence bind to one exact content state; they do not add actor/time/tenant claims not present in the source contract. +- `durable_validator` is temporally ordered by the host's atomic persistence service and must advance only after validated durable success. +- `collaboration_document`, `provider_binding`, `awareness_state`, `host_capability`, and `audit_event` can be tenant-scoped in a host, but Inkspan does not define or infer that tenant key. +- `browser_assurance_evidence` and any focused `browser_difference_allowance` bind to one exact source, corpus, package lock and browser revision set; predecessor or different-browser evidence does not transfer silently. +- `release_evidence` binds package artifacts to one exact protected source generation; predecessor evidence does not transfer after source movement. + +## Privacy and minimum-disclosure rules + +Ordinary lifecycle/selection/transition/schema-identity evidence should remain document-free. Revision/entity tags, durable validators, provider metadata, awareness state, schema identity, browser evidence, and host identifiers can still be tenant-confidential or release-sensitive metadata and **must never** become public high-cardinality metric labels or unauthenticated logs. Any separate sharing is limited to authenticated, purpose-bound, minimum-disclosure channels under host policy; such a channel does not create an exception for public metrics or unauthenticated logging. Complete document envelopes, Yjs state, conversion inputs/artifacts, prompts/model outputs, credentials, and host authorization claims follow the host's purpose, encryption, retention, and access policy. + +## Persistence non-applicability and future change + +No Inkspan-owned relational schema is required by the current architecture, so no physical database ERD or migration set is invented here merely to satisfy documentation completeness. Planned identity-routing and browser-assurance values are logical API/evidence objects, not database tables. If Inkspan later introduces durable persistence, that is a material architecture change requiring: + +1. an Accepted ADR defining why persistence moved into Inkspan; +2. a physical database ERD with descriptive multiword `snake_case` object names; +3. tenant, temporal, provenance, retention, encryption, authorization, and audit semantics; +4. migrations, backup/restore and rollback/recovery design; and +5. a revised threat model, test strategy, operability runbook, and acquisition evidence package. diff --git a/docs/DOCUMENTATION_FITNESS.md b/docs/DOCUMENTATION_FITNESS.md new file mode 100644 index 00000000..5843c088 --- /dev/null +++ b/docs/DOCUMENTATION_FITNESS.md @@ -0,0 +1,101 @@ +# Inkspan Documentation Fitness Matrix + +Status: Proposed canonical baseline + +This document answers a narrower acquisition question than the PRD or architecture: **can an independent reviewer reconstruct Inkspan's product, technical, security, data/evidence, operating, and release decisions from GitHub without relying on chat history or pull-request prose?** + +Protected `main` remains the implementation authority. This documentation branch is reviewable evidence, but it is **not yet a protected-main canonical baseline** until normal protected integration succeeds. Requirements implemented only on another active branch are not shipped merely because this matrix references them. + +## Classification vocabulary + +Documentation-family fitness uses exactly these values: + +- `present_current` — the canonical document exists on the source generation being reviewed and matches the reviewed product boundary. +- `present_stale` — a document exists but conflicts with current implementation or accepted decisions. +- `partial` — useful authoritative material exists, but one or more required decisions or views are not yet covered. +- `missing` — no adequate canonical artifact exists. +- `not_applicable` — omission is deliberate and the reason is explicit. +- `owned_by_separate_active_pr` — another non-overlapping active branch owns the canonical artifact; this branch must reference rather than duplicate it. + +Implementation maturity uses exactly these values: + +- `implemented_on_protected_main` +- `implemented_on_active_pr` +- `partial` +- `accepted_architecture` +- `planned` +- `research_only` +- `superseded` +- `out_of_scope` + +Document fitness and implementation maturity are independent. A `present_current` PRD may describe both `implemented_on_protected_main` and `planned` requirements so long as those states are explicit. + +## Canonical documentation fitness + +| Family | Canonical artifact | Documentation fitness | Implementation / authority interpretation | Acquisition conclusion | +| --- | --- | --- | --- | --- | +| PRD | `docs/PRD.md` | `present_current` | Mix of `implemented_on_protected_main`, `implemented_on_active_pr`, and `planned`, explicitly labeled | Product promise, users, JTBD, non-goals, security, accessibility and release acceptance are reconstructable. | +| TRD | `docs/TRD.md` | `present_current` | Same mixed maturity discipline; protected `main` remains runtime authority | Technical invariants, failure semantics, package boundaries and release evidence are reconstructable. | +| ARCHITECTURE | `ARCHITECTURE.md` | `present_current` | `implemented_on_protected_main` for the bounded standalone/modular architecture | Host-vs-Inkspan authority and modular CWL composition are explicit. | +| Public/API/schema/plugin contracts | `docs/CONTRACTS.md` | `present_current` | Protected-main contracts plus explicitly proposed extensions | Integration authority and degraded behavior are not hidden in implementation details. | +| ADR | `docs/adr/README.md` and detailed ADRs | `present_current` | Decisions distinguish current, proposed and future work; ADR 0017 records the protected security-disclosure lifecycle without duplicating its root policy | Alternatives, consequences, recovery, migration, verification and supersession are reviewable. | +| UML | `docs/UML.md` | `present_current` | Diagrams include protected-main and clearly proposed flows | Component, sequence, state, deployment, degraded-mode and authority flows are visible as diagram-as-code. | +| DATA_MODEL / ERD | `docs/DATA_MODEL.md` | `present_current` | Current logical evidence/domain model; host persistence remains outside Inkspan | The model distinguishes document/evidence/conversion/release values from host-owned entities. | +| physical relational ERD | none by design | `not_applicable` | `out_of_scope` while Inkspan owns no application database | No fake database is invented merely to satisfy an ERD checklist; a physical ERD becomes mandatory if persistence authority moves into Inkspan. | +| SECURITY disclosure policy | root `SECURITY.md` plus ADR 0017 | `present_current` | `implemented_on_protected_main`; the root policy is protected authority and ADR 0017 records its durable architecture/process decision | Private reporting, evidence minimization, supported release-line binding, ownership limits, coordinated disclosure, and explicit no-SLA/no-certification claim boundaries are reconstructable. | +| Autosave lifecycle observation | PRD, TRD, `docs/document-autosave.md`, lifecycle doctoring and protected autosave package/session source | `present_current` | `implemented_on_protected_main`; protected main exposes the bounded construction-time observer contract | Buyers can reconstruct saving/blocked/recovery/idle/shutdown observation, document-free snapshots, observer-failure isolation, and durable-validator coherence without treating it as an active-PR promise. | +| SSR/native-form serialization | PRD, TRD, `docs/server-rendering.md`, SSR doctoring and protected editor/form source | `present_current` | `implemented_on_protected_main`; protected main includes the explicit server-value handoff and synchronous hydrated mirror | Buyers can reconstruct opt-in server serialization, hydration continuity, client-controlled submission semantics, reset behavior and host-owned auth/CSRF/persistence boundaries. | +| Toolbar shortcut accessibility metadata | PRD/TRD accessibility requirements, accessibility guide/doctoring and protected toolbar source | `present_current` | `implemented_on_protected_main`; shipped bold/italic/link/undo/redo shortcuts expose truthful `aria-keyshortcuts` metadata | Accessibility metadata is tied to actual repository-level keyboard behavior rather than extension-local assumptions. | +| Revision-scoped selection evidence | selection lifecycle guide, doctoring and protected public handle/type contract | `present_current` | `implemented_on_protected_main`; protected main atomically binds structural selection coordinates to the exact revision | Atomic selection+revision evidence, privacy minimization and host-owned re-anchoring are reconstructable as shipped behavior without overstating cross-revision authority. | +| Document-transition evidence | transition doctoring, public framework-independent contract and protected revision-evidence package | `present_current` | `implemented_on_protected_main`; protected main exposes object/JSON and strict UTF-8 transition evidence | Previous/resulting revision lineage and privacy/provenance boundaries are reconstructable as shipped local evidence without implying actor/time/durable-write provenance. | +| THREAT_MODEL | `docs/THREAT_MODEL.md` | `present_current` | Covers current and explicitly proposed trust boundaries | Clipboard, Office, SSR/form, Yjs, model, host-authority and supply-chain threats are reconstructable. | +| TEST_STRATEGY | `docs/TEST_STRATEGY.md` | `present_current` | Current deterministic evidence plus `planned` cross-engine acceptance where dependency order requires it | Test authority and claim limits are explicit rather than inferred from CI badges. | +| OPERABILITY | `docs/OPERABILITY.md` | `present_current` | Current local/product responsibilities plus host-owned recovery boundaries | Conflict, collaboration, conversion and release recovery/rollback ownership are explicit. | +| Release / rollback / provenance | TRD, OPERABILITY and release ADRs | `present_current` | Mix of `implemented_on_protected_main` and active hardening | Exact-source release authority, stale-evidence rejection and rollback are reconstructable. | +| Envelope schema identity / migration routing | ADR 0015, PRD, TRD, DATA_MODEL and Issue #74 | `present_current` | Identity-only routing capability is `planned`; strict current-schema parsing and host migration ownership remain authoritative | The architecture distinguishes bounded schema identification from host-owned migration execution without calling the planned API shipped. | +| Cross-engine browser-semantic release assurance | ADR 0016, UML, TEST_STRATEGY, TRACEABILITY and Issue #66 | `present_current` | Differential Chromium/Firefox/WebKit release gate is `planned` behind PR #65 | Browser-realistic security assurance is a durable release decision even though its implementation remains dependency-ordered future work. | +| TRACEABILITY | `docs/TRACEABILITY.md` | `present_current` | Links standards/research/requirements to decisions and evidence with scoped claims | Acquisition reviewers can distinguish evidence from aspiration. | +| Contributor/agent authority | `AGENTS.md`, `CLAUDE.md`, `docs/README.md` | `present_current` | Protected-main-first decision discipline | Agents are directed back to the same canonical graph rather than parallel private memory. | +| Autonomous maintenance governance | `AGENTS.md`, `CLAUDE.md` plus the external scheduler | `present_current` | `out_of_scope` as Inkspan runtime behavior; the external scheduler owns cadence/continuation | Work-conserving execution, lane-local waiting, no-report-as-completion, and the scheduler-vs-product authority boundary are reconstructable without pretending automation is an Inkspan API. | + +## Conversation-to-GitHub reconciliation + +The canonical graph must retain durable product decisions from the project conversation only when they agree with live implementation or are explicitly labeled as target architecture. The reviewed baseline currently covers: + +- Markdown/HTML WYSIWYG authoring and deterministic source/document authority; +- strict link, image, clipboard, envelope and revision/evidence boundaries; +- bundled local/offline font licensing and air-gapped asset behavior; +- deterministic email/document conversion boundaries and independently reusable Office rendering; +- provider-neutral collaboration with host-owned Yjs provider, room, persistence and awareness authority; +- bounded protected-main autosave lifecycle observation with document-free distinct snapshots and observer-failure isolation; +- protected-main SSR/native-form serialization with opt-in document disclosure, controlled hydration handoff, synchronous native-field mirroring, and host-owned acceptance controls; +- protected-main toolbar shortcut metadata that reflects the actual cross-platform bold, italic, link, undo and redo bindings; +- protected-main revision-scoped selection and document-transition evidence with local equality/lineage semantics and host-owned re-anchoring/occurrence provenance; +- naruon `compose` / `ui.panel` modular integration without making naruon a standalone runtime dependency; +- model assistance as untrusted proposed content rather than conversion or persistence authority; +- accessibility, keyboard, print/export and document-fidelity evidence boundaries; +- host ownership of transport, authentication, authorization, tenant isolation, persistence, credentials, migration, retention, deployment, durable audit and model policy; +- protected-main private vulnerability reporting and coordinated disclosure with explicit evidence-minimization and no-SLA/no-certification boundaries; +- strict current-schema parsing plus planned identity-only envelope routing, while migration execution remains host-owned; +- real Chromium/Firefox/WebKit differential evidence as a release gate for browser-semantic clipboard security rather than a jsdom conformance claim; and +- exact-head/package/security/provenance/release evidence as separate authorities from comments, model verdicts and historical checks. + +Autonomous commercial-maintenance scheduling and the no-early-stop execution discipline are **control-plane governance, not a shipped Inkspan product capability**. The external scheduler is the execution authority for cadence and continuation; repository guidance records writer leases, work-conserving queue behavior, lane-local waiting, evidence hierarchy and protected-main authority without pretending the automation prompt is a runtime API or architectural feature. + +Where an older conversation, PR body, or plan conflicts with protected `main`, it is historical rather than canonical. Where a requirement is only on an active PR, this documentation may describe it as `implemented_on_active_pr` but never as shipped. + +## Remaining documentation and product gaps + +The documentation pack itself is substantially complete for acquisition review, but **repository closure is not documentation closure**. The remaining gaps are intentionally represented rather than hidden: + +1. Issue #74 remains `planned`: the identity-only migration-routing API must still be implemented test-first while the current parser remains strict. Its architectural decision is now present rather than hidden in issue prose. +2. Issue #66 remains `planned` behind PR #65: the dependency-locked Chromium/Firefox/WebKit differential suite must still be implemented before the rich-clipboard release line. Its release-assurance decision is now present rather than hidden in issue prose. +3. SafeClipboard (PR #65) remains `implemented_on_active_pr` until protected integration. Autosave lifecycle observation, security disclosure, toolbar shortcut accessibility metadata, SSR/native-form serialization, revision-scoped selection evidence, and document-transition evidence are now `implemented_on_protected_main` and must not be described as active-only work. +4. The canonical documentation branch must still reconcile its ancestry with the latest protected `main` before protected merge; protected-main identity must be refetched rather than hard-coded as timeless architecture. +5. Documentation becoming mergeable or protected-merged is not a reason for the commercial loop to stop; the next safe product, release, security, accessibility or interoperability lane must continue. + +## Sufficiency decision + +For this active documentation branch, PRD, TRD, Architecture, ADR, UML, conceptual ERD/data model, contracts, threat model, test strategy, operability, security disclosure, and traceability are `present_current` for the durable product and accepted/planned architecture decisions reconstructed from the conversation and live repository. Autosave lifecycle observation, the security disclosure lifecycle, toolbar shortcut accessibility metadata, SSR/native-form serialization, revision-scoped selection evidence, and document-transition evidence are `implemented_on_protected_main`. SafeClipboard remains active-PR work. Envelope migration routing and cross-engine browser assurance are explicit Proposed ADR decisions while their implementations remain `planned`. A physical relational ERD is `not_applicable` because Inkspan deliberately owns no application persistence. + +No material product architecture decision identified by this review remains only in chat or issue prose. Accordingly, the **documentation design is sufficient as a proposed acquisition baseline**, but the repository is **not yet a protected-main canonical baseline** until this graph is reconciled with the latest protected source generation and passes normal protected integration. Product/release readiness must continue to be evaluated independently of documentation completeness. diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md new file mode 100644 index 00000000..482bdf0c --- /dev/null +++ b/docs/OPERABILITY.md @@ -0,0 +1,96 @@ +# Inkspan Operability and Recovery + +Status: Proposed canonical baseline + +## Operational model + +Inkspan is a library/product module, not a host control plane. It owns deterministic editor, conversion, evidence, local autosave ordering, package, and provider-neutral adapter behavior. A host owns network transport, authentication, authorization, tenant isolation, persistence, credentials, migrations, retention, deployment, durable audit storage, collaboration-provider lifecycle, and model-use policy. + +This distinction controls incident ownership: an Inkspan incident is a deterministic package/editor/conversion/evidence defect or a violated public boundary. A host outage, database failure, provider outage, tenant authorization failure, or model-provider failure is a host incident unless Inkspan caused or amplified it through an explicit adapter contract. + +Protected `main` is the shipped implementation authority. Open PRs are not operational authority until protected integration. + +## Health and evidence + +Inkspan itself does not expose a network health endpoint. Operational health is proven through exact-head CI and package-consumer evidence, deterministic runtime outcomes, bounded public errors, security scans, package/release verification, and host-observable local state. + +Hosts may derive UI or private local observability from bounded lifecycle/snapshot state, but complete document bodies, revision/entity tags, durable validators, provider metadata, awareness state, tenant identifiers, schema identity, browser evidence, prompts, model outputs, or comparable tenant-confidential/release-sensitive values must never become public high-cardinality metric labels or unauthenticated logs. Any separate sharing is authenticated, purpose-bound, minimum-disclosure, and host-authorized; that separate channel does not redefine public telemetry as safe. + +## Autosave operations + +The local autosave queue is single-flight with bounded pending work. Durable sessions use a host/server-selected strong HTTP entity tag for compare-and-swap. A local SHA-256 document revision is not a durable validator. + +Operational states are `idle`, `saving`, `blocked`, `closing`, and `closed`. Blocked conflict or ambiguous failure requires explicit recovery. No-op lifecycle operations do not manufacture synthetic state changes. Observer exceptions are presentation/telemetry failures and must not change persistence ordering. The explicit in-process `getSnapshot()` coordination surface may contain bounded active/pending/last-saved validator fields; operators must classify those values as confidential local concurrency metadata rather than generic diagnostics. + +Host operators should treat: + +- conflict as a durable concurrency event requiring authenticated compare/merge/fork/reload policy; +- ambiguous transport failure as unknown durable state until the host proves the result; +- malformed or missing strong validators as a fail-closed host integration defect; +- repeated blocked state as a host-visible error requiring user/operator recovery rather than automatic silent retries. + +## Collaboration operations + +The host creates, authorizes, monitors, and destroys the Yjs-compatible provider. Inkspan must not own provider credentials, reconnect policy, room authorization, awareness retention, tenant admission, or durable update storage. When the provider is unavailable, the embedding host decides whether local editing remains available, becomes read-only, or blocks. Inkspan does not invent durable collaboration success. + +## Deterministic conversion operations + +Markdown/HTML/editor conversion and Office rendering are deterministic local operations. Office rendering runs without model, external network, macro, or Desktop Office authority. Inputs are bounded and validated before publication. On validation or publication failure, the operation fails with bounded diagnostics; operators must not reuse a partial artifact as successful output. + +File publication must follow the documented atomic/non-overwrite behavior. A caller-requested overwrite remains explicit. A failed write or validation does not authorize cleanup of unrelated host files. + +## Release operations + +Release publication occurs only from an exact integrated protected head. Release evidence includes package artifacts, deterministic checksums, CI/security/package/provenance results, required review, zero valid unresolved findings, and repository-policy acceptance. The normative inventory and digest rules are defined by the `docs/CONTRACTS.md` Release and rollback contract. + +Before publication: + +1. build exactly three regular top-level release files: exactly one npm tarball, exactly one Inkspan Office wheel, and `SHA256SUMS`; +2. reject missing, duplicate, non-regular, stale, or unexpected local entries and verify the local digests; +3. 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; +4. require every remote asset state to be uploaded and every GitHub-reported `sha256:` digest to equal the exact transferred local file digest; +5. fail closed on stale/unexpected/incomplete/digest-mismatched assets rather than deleting them automatically to manufacture a clean draft; +6. verify SBOM/provenance/signing or attestation gates where configured; +7. verify package/wheel consumers and the supported runtime matrix; and +8. publish only after exact-head required review and protection gates pass. + +After publication, verify artifact availability, checksums/provenance, package metadata, install/consumer smoke evidence, and release notes. Rollback of a bad release uses a new reviewed corrective release or repository-supported withdrawal/yank policy; immutable published evidence is not rewritten to pretend the release never existed. + +## Incident classes and first response + +### Security input bypass + +Stop publication of affected versions, reproduce on exact source, preserve a minimized synthetic regression, classify whether the defect is inside Inkspan or host policy, patch test-first, rerun security/package/release evidence, and follow the protected-main root `SECURITY.md` private-reporting and coordinated-disclosure lifecycle. Do not publish proof-of-concept customer data. + +### Data-loss or false durable-success risk + +Fail closed. Preserve the host's last known durable validator and local evidence. Do not mark an ambiguous write as saved. Require authenticated host reconciliation before resuming. Never substitute a content digest for server durable state. + +### Package or conversion corruption + +Do not publish or reuse the artifact. Rebuild from exact source with deterministic dependencies, verify the package tree and digest, and compare to the known-good contract. If a published artifact is affected, use the release recovery procedure rather than silently replacing immutable evidence. + +### Browser/parser divergence + +When a browser-specific clipboard/security difference is found, add it to the cross-engine corpus. Accept a difference only with explicit standards basis and threat analysis. Do not normalize a security-relevant difference away solely to regain parity. + +### Dependency or workflow incident + +Treat central `.github` or other dedicated-loop defects as read-only dependencies. Do not create an Inkspan workaround that weakens exact-head evidence, coverage, security, approval, or release policy. Continue independent Inkspan work that does not rely on the defective dependency. + +## Backup, migration, and retention + +Inkspan owns no application database on the current architecture, so database backup/restore and tenant retention are host responsibilities. Versioned document envelopes and public package schemas must remain backward/migration-aware; host migration implementation and schema registry remain host-owned. If Inkspan later owns durable persistence, that is a material architecture change requiring a physical ERD, migration/rollback design, backup/restore runbook, retention model, threat review, and ADR. + +## Rollback + +Rollback is boundary-specific: + +- editor feature: remove or revert the feature while preserving canonical document readability; +- autosave/observer feature: fall back to explicit `getSnapshot()`/host coordination without rewriting durable state; +- collaboration adapter: detach the adapter without destroying the host provider or Yjs document; +- Office renderer change: revert the deterministic renderer behavior and rebuild artifacts; do not modify host files outside the explicit output target; +- documentation: supersede inaccurate decisions with an ADR and synchronized canonical docs rather than deleting history; +- release: issue a verified corrective release or supported withdrawal action; preserve provenance and incident evidence. + +Every rollback requires fresh exact-head tests and must not weaken authorization, tenant isolation, release provenance, or host ownership boundaries. diff --git a/docs/PRD.md b/docs/PRD.md new file mode 100644 index 00000000..1f538cf2 --- /dev/null +++ b/docs/PRD.md @@ -0,0 +1,132 @@ +# Inkspan Product Requirements + +Status: Proposed canonical baseline + +## Product definition + +Inkspan is a standalone Markdown/HTML rich-text authoring and deterministic document-conversion product that can also be embedded as a modular CWL component. Protected `main` is the implementation authority for what Inkspan actually provides today. Lifecycle observation, security disclosure, toolbar shortcut accessibility metadata, SSR/native-form serialization, revision-scoped selection evidence, and document-transition evidence are implemented on protected `main`. This canonical PRD also records Active PR / Proposed requirements such as safe rich clipboard handling; such requirements are not shipped claims until protected integration. Across current and proposed scope, Inkspan keeps host transport, authentication, authorization, tenant isolation, durable persistence, credentials, migrations, retention, deployment, audit storage, and model-use policy outside the product boundary unless a future accepted versioned contract explicitly changes that division. + +The product promise is: **author, convert, collaborate, and prove document changes without hiding authority inside the editor.** + +## Users and buyers + +- Application developers embedding an editor into web, SSR, worker, or desktop-shell products. +- Enterprise platform teams requiring deterministic conversion, evidence, accessibility, security boundaries, reproducible packages, and explicit operational ownership. +- Authors and reviewers who need accessible Markdown/HTML editing, safe paste, import/export, and predictable recovery from save conflicts. +- CWL products such as naruon that compose Inkspan through `compose` / `ui.panel` while retaining host identity, persistence, collaboration-provider, and model policy. +- Acquisition, security, and operations reviewers who need a bounded source-of-truth graph for architecture, threats, tests, rollback, release evidence, and supported claims. + +## Jobs to be done + +1. Author and revise Markdown/HTML through one deterministic editor surface without losing canonical document identity. +2. Paste rich content while rejecting or sanitizing unsafe active, hidden, or resource-bearing input before it becomes editor state. +3. Import, export, and convert supported content deterministically, including independently reusable network-free Office DOCX/XLSX/PPTX rendering. +4. Detect exact content equality and stale review/selection state without copying complete document bodies into ordinary evidence metadata. +5. Save through a host-controlled durable boundary that prevents lost updates and makes conflict/ambiguous failure recoverable instead of silently successful. +6. Embed safely in SSR/native-form products while treating browser-submitted values as untrusted host input. +7. Add real-time collaboration without locking Inkspan to one provider or moving room authorization, awareness privacy, persistence, or tenant identity into the editor. +8. Compose Inkspan inside naruon or another CWL host without making those hosts required runtime dependencies for standalone adopters. +9. Offer model-assisted authoring only as host-approved untrusted proposals; deterministic editor/conversion validation remains authoritative. +10. Produce reviewable package, security, compatibility, accessibility, SBOM/provenance, and release evidence tied to one exact protected source head. +11. Give security researchers a discoverable private vulnerability-reporting and coordinated-disclosure path without promising unsupported SLAs, bounties, certification, or legal safe harbor. + +## Required outcomes + +### Authoring and source fidelity + +- Markdown and HTML authoring use a deterministic TipTap/ProseMirror surface. +- Supported import/export behavior is explicitly versioned and testable; unsupported or lossy constructs are not silently advertised as lossless. +- Canonical document envelopes preserve exact schema/version identity and strict JSON/UTF-8 boundaries. +- Source and rendered/exported representations must have documented authority; generated output never silently becomes a new source of truth. + +### Clipboard and external content + +- Untrusted rich clipboard content is processed through the actual editor paste pipeline, not a disconnected helper. +- Active, hidden, executable, externally fetching, malformed, or over-limit input fails closed or is removed only under a documented semantic allowlist. +- Browser-parser differences that can change security semantics require real Chromium, Firefox, and WebKit differential acceptance before the relevant release line. + +### Evidence and concurrency + +- SHA-256 document revisions are equality evidence only, never authentication, tenant identity, authorization, signature, timestamp, or durable-write proof. +- Selection and transition evidence bind to one exact immutable editor state and omit document bodies from ordinary metadata. +- Autosave remains single-flight with bounded active/pending work and explicit conflict/failure recovery. +- Durable saves use a host/server-selected strong validator; conflict or ambiguous failure never silently advances it. +- Lifecycle observation emits only distinct externally visible document-free state transitions; construction and no-op operations do not manufacture notifications. + +### SSR and native forms + +- Server rendering never creates a browser editor view. +- Optional native-form serialization survives SSR/hydration and remains synchronized once the editor is authoritative. +- Controlled `value` precedes `defaultValue` for the explicitly configured server-rendered field, and the native value is restored from the editor after document-changing transactions and native reset processing. +- Hidden/native field values are client-controlled data and never replace host authentication, authorization, CSRF controls, request validation, tenant isolation, or durable concurrency. + +### Collaboration + +- Collaboration remains provider-neutral and compatible with host-supplied Yjs surfaces. +- Hosts own provider creation/destruction, room authorization, identities, awareness privacy, reconnect policy, update persistence, retention, encryption, and durable audit. +- Provider outage or degraded collaboration does not permit Inkspan to invent durable collaboration success or broader host authority. + +### Deterministic Office conversion + +- JSON→DOCX/XLSX/PPTX rendering is deterministic, network-free, macro-free, model-free, and Desktop-Office-free. +- Inputs are bounded and validated for XML 1.0, container/depth/cycle limits, worksheet/freeze-pane constraints, supported structures, and spreadsheet formula-injection boundaries. +- File publication is race-safe and explicit about overwrite behavior. +- Format-fidelity claims are limited to tested supported constructs; generated artifacts are verified through realistic package/document tests. + +### Modular composition and model assistance + +- Standalone Inkspan does not require naruon, contextual-orchestrator, a database, a provider SDK, or a model credential. +- A naruon host may compose Inkspan through a narrow client panel while host services retain authenticated transport, tenant persistence, collaboration provider, conflict UX, and model policy. +- Model output is untrusted proposed content. The host owns provider choice, credentials, redaction, external-data-use approval, logging, retention, and human approval; deterministic Inkspan validation remains the acceptance boundary. + +### Accessibility, print, and export + +- Native controls, focus behavior, keyboard parity, truthful `aria-keyshortcuts` metadata, non-color status semantics, and host-facing lifecycle state support WCAG-oriented embedding. +- Toolbar shortcut metadata must reflect repository-level shipped behavior, including host/editor bindings such as link editing, rather than only extension-local defaults. +- Application-visible saving/conflict/recovery messages must be derivable from programmatic state without Inkspan prescribing untranslated user-facing copy. +- Export/print surfaces must not rely on color alone or inaccessible interaction-only state where the corresponding product surface exists. + +### Security disclosure and vulnerability handling + +- Root `SECURITY.md` is the protected-main authority for supported release lines, private vulnerability reporting, minimized evidence, coordinated disclosure, and explicit claim limits. +- Private GitHub vulnerability reporting / Security Advisory intake is preferred when available. Public issues must not contain vulnerability details, proof-of-concept payloads, secrets, or customer data; they may only request a private reporting route when necessary. +- Supported pre-1.0 security lines remain bound to package manifests by deterministic tests rather than copied as unverified prose. +- The policy does not promise a response-time SLA, bounty, legal safe harbor, certification, or complete secure-development-framework conformance without separate evidence and authority. +- Host-owned incidents remain host responsibilities unless an Inkspan-owned defect contributes to the issue. + +## Non-goals + +Inkspan is not an identity provider, tenant database, durable document store, collaboration authorization server, deployment platform, credential manager, retention engine, model router, durable audit service, application migration owner, or merge/release authority for host products. + +Inkspan does not promise universal HTML/Office round-trip fidelity, arbitrary executable document content, implicit network fetching, model-generated content as trusted source, silent migration of unknown document schemas, a security bounty, legal safe harbor, fixed vulnerability-response SLA, or certification from repository documentation alone. + +## Security and privacy requirements + +- Untrusted HTML, DOM capabilities, clipboard configuration, form values, Office structures, host callbacks, collaboration updates, and model proposals fail closed at documented boundaries. +- Active or hidden rich content must not bypass the supported semantic clipboard policy. +- Spreadsheet formula-significant input must not silently become executable formulas unless a future explicit trusted formula contract says otherwise. +- Document bodies, revision/entity tags, provider metadata, tenant identifiers, prompts, and model outputs must not enter generic public metrics or unauthenticated logs. +- Vulnerability reporting must prefer private channels and minimized/synthetic evidence; public fallback must not disclose vulnerability details, secrets, proof-of-concept payloads, or customer data. +- Host applications remain responsible for authentication, authorization, CSRF, tenant isolation, persistence, encryption, retention, audit storage, provider admission, and external-model policy. + +## Reliability and operability requirements + +- Autosave is bounded, single-flight, idempotency-aware at its local contract, and explicit about blocked/conflict/failure state. +- Observer failures cannot change save ordering, result classification, or durable-validator handoff. +- Async revision/selection capture binds to one immutable editor state. +- Conversion/publication never reports a partial artifact as successful output. +- Package/release operations fail closed on stale source, ambiguous artifact inventory, digest mismatch, missing required evidence, or stale review/check state. +- Security policy support rows and reporting mechanics must remain synchronized with package manifests and current repository capabilities. +- Boundary-specific rollback must preserve canonical document readability and host-owned durable state. + +## Packaging and acquisition acceptance + +A release is acceptable only from an exact integrated protected head with applicable CI/security checks, exact owned production statement/branch/function/line coverage, complete public docstrings where required, package-consumer compatibility, real browser/document-fidelity evidence, accessibility evidence, SBOM/provenance/reproducibility, zero valid unresolved findings, actually required independent non-author review, rollback guidance, and verified published artifacts. + +Shareable acquisition evidence excludes production tenant content and credentials. Protected `main`, exact-head machine evidence, formal reviews, and canonical product documentation outrank historical PR bodies, comments, local-only results, or predecessor-head status. + +## Current, proposed, and planned scope + +Protected `main` is the sole implemented baseline. Open PRs may describe Proposed or Active work but are not shipped contracts until protected integration. Canonical documentation must state when a requirement is target architecture rather than current implementation. + +The root security disclosure lifecycle, lifecycle observation, toolbar shortcut accessibility metadata, SSR/native-form serialization, revision-scoped selection evidence, and document-transition evidence are implemented on protected `main`. Current open development lines include SafeClipboard and canonical documentation reconciliation. Cross-engine browser assurance remains planned behind SafeClipboard, and a future envelope-identity migration-routing API is tracked separately and must preserve host migration ownership. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..b5a783d6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,51 @@ +# Inkspan canonical documentation + +Status: Proposed canonical baseline + +This directory is the discoverable index for Inkspan's product, technical, security, operability, and architecture records. Protected `main` is the implementation authority. Proposed or active-PR behavior must remain labeled as such until it reaches protected `main` with the required evidence. + +## Canonical graph + +| Document | Authority | +| --- | --- | +| [`../ARCHITECTURE.md`](../ARCHITECTURE.md) | Current protected-main implementation architecture and bounded-context ownership | +| [`../SECURITY.md`](../SECURITY.md) | Protected-main private vulnerability reporting, supported security lines, coordinated disclosure, and claim limits | +| [`DOCUMENTATION_FITNESS.md`](DOCUMENTATION_FITNESS.md) | Acquisition completeness matrix, implementation maturity, deliberate non-applicability and remaining canonical gaps | +| [`PRD.md`](PRD.md) | Product users, jobs, buyer outcomes, non-goals, acceptance and claim boundaries | +| [`TRD.md`](TRD.md) | Technical invariants, runtime boundaries, failure semantics and release evidence | +| [`CONTRACTS.md`](CONTRACTS.md) | Public package/API/event/schema/plugin/collaboration and host-integration contracts | +| [`UML.md`](UML.md) | Component, sequence, state and authority-flow diagrams | +| [`DATA_MODEL.md`](DATA_MODEL.md) | Conceptual evidence/domain model and persistence ownership | +| [`THREAT_MODEL.md`](THREAT_MODEL.md) | Trust boundaries, abuse cases, security/privacy controls and residual risks | +| [`TEST_STRATEGY.md`](TEST_STRATEGY.md) | Unit/integration/browser/Office/security/accessibility/package evidence strategy | +| [`OPERABILITY.md`](OPERABILITY.md) | Failure handling, recovery, incident ownership, rollback and release operations | +| [`TRACEABILITY.md`](TRACEABILITY.md) | Requirements/standards/research-to-decision/test evidence traceability | +| [`adr/README.md`](adr/README.md) | Status-bearing architectural decision index, including ADR 0017 for the protected security-disclosure lifecycle | + +Root `SECURITY.md` is now implemented on protected `main` and remains the normative reporting/disclosure policy. ADR 0017 records the durable architecture/process decision and ownership/claim boundaries without duplicating policy prose. `CHANGELOG.md` records shipped/reviewable change history, while `AGENTS.md` and `CLAUDE.md` point contributors back to this canonical graph rather than becoming parallel architecture specifications. + +## Status discipline + +Use these terms consistently: + +- **Implemented on protected main** — code and behavior exist on the current protected branch and have applicable acceptance evidence. +- **Active PR / Proposed** — reviewable work exists but is not shipped authority. +- **Accepted architecture** — a durable decision is approved by the governing ADR but implementation may still be incomplete. +- **Planned** — bounded future work with no protected implementation claim. +- **Research only** — evidence or alternatives under evaluation, not a product commitment. +- **Superseded** — retained only for history; a newer decision or implementation is authoritative. +- **Out of scope** — deliberately excluded from Inkspan ownership. + +The exact machine-oriented documentation-fitness and implementation-maturity vocabulary is defined in [`DOCUMENTATION_FITNESS.md`](DOCUMENTATION_FITNESS.md). Human-facing labels above must map to those states without combining evidence qualifiers, PR numbers, or temporary execution status into the status value itself. + +Never use a PR body, check status, model verdict, local test, or conversation as a substitute for protected-main implementation authority. + +## Ownership boundary + +Inkspan owns deterministic editor/conversion behavior, versioned document/evidence contracts, local autosave coordination, accessibility metadata, package behavior, and provider-neutral adapters. The embedding host owns authenticated transport, authorization, tenant isolation, durable persistence, credentials, migration execution, retention, deployment, durable audit, collaboration-provider authority, and model-use policy unless a future accepted versioned contract explicitly changes that division. + +## Change discipline + +A material contract change should update the smallest affected set of PRD/TRD/CONTRACTS/Architecture/ADR/UML/data-model/security/test/operability/traceability records and corresponding machine-checkable documentation contracts. If a document is unaffected, preserve it rather than performing churn solely for consistency optics. + +Stable architectural documents should avoid embedding transient PR heads, workflow run IDs, or temporary provider states. Put dated operational evidence in bounded doctoring/evidence records instead. diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md new file mode 100644 index 00000000..0e40aefa --- /dev/null +++ b/docs/TEST_STRATEGY.md @@ -0,0 +1,77 @@ +# Inkspan Test Strategy + +Status: Proposed canonical baseline + +## Purpose + +Inkspan testing must prove deterministic authoring/conversion behavior, fail-closed security boundaries, package compatibility, accessibility semantics, and release evidence without conflating local correctness with host authorization or durable persistence. Protected `main` remains the implementation authority; open feature branches are not shipped evidence. + +## Test pyramid and evidence classes + +### Pure deterministic unit tests + +Exercise document-envelope validation, canonicalization, UTF-8 handling, revision derivation, transition evidence, selection capture, autosave queue/state transitions, link/image policy, clipboard reconstruction, conversion primitives, Office renderer validation, and error normalization. Inputs include hostile objects, proxies/accessors, malformed JSON/UTF-8, deep or large structures, invalid identifiers, and boundary values. + +### Component and integration tests + +Exercise TipTap/ProseMirror integration, actual paste-pipeline installation, SSR/hydration, native form serialization, provider-neutral Yjs bindings, editor lifecycle, autosave durable-session handoff, and deterministic conversion packages. Tests must use the actual public adapter path instead of invoking an unregistered configuration field or bypassing the integration layer. + +### Package-consumer tests + +Install or consume the packed artifacts rather than source-tree aliases. Verify ESM, CommonJS, and strict TypeScript consumers. Framework-independent subpaths must compile and run without accidental React, TipTap UI, ProseMirror view, Yjs, DOM, network, or credential dependencies when the public contract excludes them. + +### Browser differential tests + +Where browser fragment parsing or serialization participates in a security boundary, use dependency-locked Playwright coverage across Chromium, Firefox, and WebKit. The same adversarial corpus must run in all required engines. Differences are not normalized away merely to produce parity; every reviewed allowlisted difference requires a standards basis and threat analysis. Missing/skipped browsers are not successful release evidence. + +### Office artifact tests + +Exercise realistic DOCX, XLSX, and PPTX fixtures and inspect the generated package structure. Cover XML 1.0 validity, formula-injection neutralization, worksheet naming and freeze panes, bounds/depth/cycles, deterministic metadata, publication races, overwrite semantics, and wheel/package contents. No macro, network, model, or Desktop Office execution is required by the deterministic renderer. + +### Security and static analysis + +Run repository security scanning, Semgrep/SAST, dependency and supply-chain checks, secret scanning where configured, package/license inspection, immutable workflow/source checks, and hostile-input regressions. Scanner success does not replace behavioral tests. + +### Accessibility tests + +Verify native control semantics, focus behavior, `aria-pressed`, `aria-keyshortcuts`, keyboard parity, programmatic status data, SSR behavior, non-color status semantics, and host-facing lifecycle information. Automated checks supplement rather than replace manual assistive-technology and interaction review for a release candidate. + +## Coverage policy + +JavaScript/TypeScript owned production code is enforced at exact 100% statements, branches, functions, and lines by the repository Vitest/V8 coverage configuration. Coverage is a structural completeness gate, not proof of semantic correctness. Tests must continue to include realistic domain and adversarial behavior instead of creating vacuous branches merely to satisfy a percentage. + +Office Python uses a distinct language/tool contract. Across every advertised supported Python minor on protected CI, `coverage.py` runs with branch measurement enabled, `coverage report` is required to meet `fail_under = 100`, and `show_missing = true` exposes uncovered statement lines and missing branch destinations/start lines. The Office gate also requires 100% public-docstring completeness through `scripts/check_docstrings.py`. Python has no separate JavaScript-style function/line percentage counters in this repository contract; do not relabel the coverage.py report as four independent metrics. JavaScript/TypeScript public declarations and package boundaries additionally require strict packed-consumer compilation. + +## Security corpus + +At minimum, maintain regressions for: + +- duplicate JSON object names, negative zero, malformed JSON, malformed UTF-8, BOM, depth/value/string/byte limits, sparse/decorated/non-plain objects, symbols, accessors, proxies, reflection failures, and detached/cross-realm byte views; +- rich clipboard scripts, embeds, resources, forms, metadata, SVG/MathML, images, hidden subtrees, `dialog`, `details`, `popover`, Office `mso-hide`, CSS comments/escapes/case/whitespace, malformed fragments, tables/lists/formatting elements, unsafe links, and resource ceilings; +- SSR client-controlled form values, escaping, hydration continuity, reset behavior, and absence of server editor construction; +- autosave stale validators, conflict/failure recovery, ambiguous transport outcomes, duplicate/no-op lifecycle transitions, callback exceptions, queue bounds, flush/close behavior, and durable-validator coherence; +- selection/revision races and document movement during asynchronous hashing; +- Office formula prefixes, invalid XML characters, malicious strings, path/publication races, invalid worksheet names, invalid freeze panes, cyclic input, pathological nesting, excessive container size, and partial write failure; +- package/release stale draft assets, unexpected or non-regular local entries, exact three-file inventory violations, incomplete remote uploads, GitHub-vs-local digest mismatch, stale exact-head evidence, mutable provenance inputs, and isolated packed-consumer behavior. + +## Concurrency and failure testing + +Use deterministic barriers/fakes for local concurrency and real process/file boundaries where required. Prove that an observer exception cannot alter queue ordering; a stale digest cannot bind to a later editor state; an ambiguous durable save does not advance a validator; close/recovery does not leak waiters; and file publication either completes under the documented contract or fails without silently replacing unrelated content. + +Host persistence transactions, tenant isolation, distributed collaboration authorization, durable audit storage, and production network retry policy are host-owned and must be tested by the embedding product. Inkspan tests verify only the explicit adapter contract at those boundaries. + +## Release acceptance + +A release candidate requires the exact integrated protected head to satisfy applicable CI, security, JavaScript/TypeScript 100% statement/branch/function/line coverage, Office coverage.py 100% report plus public-docstring completeness, package-consumer, accessibility, browser differential, Office artifact, SBOM/provenance, reproducibility, unresolved-thread, actually required independent-review, and release-workflow gates. Queued, skipped-required, cancelled, absent, stale-head, predecessor-head, status-only, or synthetic-merge evidence is not accepted as success. + +The release workflow must also satisfy the normative `docs/CONTRACTS.md` draft inventory contract: exactly one npm tarball, exactly one Office wheel, and `SHA256SUMS`; no other top-level entry; remote uploaded asset names exactly equal local names; and every GitHub-reported `sha256:` digest equals the exact transferred local file digest. Missing, stale, unexpected, non-regular, incomplete, or digest-mismatched assets are failures, not cleanup opportunities. + +The 0.6.0 rich-clipboard release line specifically requires the Chromium, Firefox, and WebKit differential gate before publication. Deterministic jsdom coverage remains useful but is not a substitute for browser-engine acceptance. + +## Documentation verification + +Documentation tests must compare canonical PRD/TRD/Architecture/ADR/UML/data-model/security/operability claims against current public package names, state enums, runtime versions, integration boundaries, and release evidence. They should reject obsolete product/internal names and unsupported claims, not merely check that files exist. + +## Rollback of a test gate + +A gate may be changed only because its product contract changed or the gate itself is technically invalid. The replacement begins with a regression that demonstrates the mismatch. Do not disable, skip, broaden allowlists, or lower coverage/security thresholds merely to make a branch green. diff --git a/docs/THREAT_MODEL.md b/docs/THREAT_MODEL.md new file mode 100644 index 00000000..68dc9088 --- /dev/null +++ b/docs/THREAT_MODEL.md @@ -0,0 +1,87 @@ +# Inkspan Threat Model + +Status: Proposed canonical baseline + +## Scope and trust boundary + +Inkspan owns deterministic editor, conversion, canonical document-envelope, local revision/evidence, local autosave-ordering, accessibility metadata, and provider-neutral collaboration adapter behavior. A host owns network transport, authentication, authorization, tenant isolation, durable persistence, credentials, migrations, retention, deployment, audit storage, collaboration-provider lifecycle, and model-use policy. + +Protected `main` is the implementation authority. Open pull requests are evidence of proposed or active work only and must not be treated as shipped security controls until protected integration. + +## Protected assets + +- document bodies and versioned `document_envelope` values; +- tenant-confidential equality metadata such as local revisions and durable strong entity tags; +- deterministic conversion artifacts and release packages; +- local lifecycle, selection, transition, and collaboration metadata; +- host-supplied callbacks, provider bindings, and authorization context that Inkspan must not reinterpret; +- package/release provenance and exact-head verification evidence. + +Document bodies are content, not authorization. Revisions and strong validators are concurrency/equality evidence, not bearer credentials, signatures, tenant identifiers, or permission grants. + +## Threat actors and inputs + +Threats include malicious or malformed clipboard HTML, links, images, document envelopes, JSON/UTF-8 bytes, host callbacks, DOM capabilities, JavaScript proxies/accessors, form submissions, collaboration updates/awareness, Office-renderer input, package/release state, and compromised or misconfigured embedding hosts. Ordinary author mistakes and concurrent edits are treated as reliability threats when they can cause data loss or false durable-success claims. + +## Principal threat scenarios + +### Rich clipboard and active content + +Untrusted clipboard HTML can attempt script execution, external resource fetches, hidden-content smuggling, SVG/MathML or embedded active content, malformed tree reconstruction, unsafe links, resource exhaustion, or browser-parser differentials. The supported contract is fail-closed bounded semantic sanitization in the actual TipTap/ProseMirror paste pipeline. Cross-engine Chromium, Firefox, and WebKit differential evidence is a release-assurance requirement for the richer sanitizer line; deterministic jsdom evidence alone must not be represented as universal browser conformance. + +### Links, images, and external resources + +URLs or image-like content can exfiltrate document context, induce unexpected network access, or smuggle executable/active payloads. Inkspan validates only supported local semantics and does not grant network authority. Hosts remain responsible for downstream CSP, fetch policy, proxy/egress controls, content serving, and tenant authorization. + +### Spreadsheet formula injection + +XLSX cell values beginning with formula-significant prefixes can become executable spreadsheet formulas when opened by a user. Deterministic Office rendering must preserve the documented formula-injection neutralization boundary and never silently convert untrusted strings into formulas. No macro, network, or Desktop Office execution is part of the renderer contract. + +### Malformed Office structures and publication races + +DOCX/XLSX/PPTX generation must reject unsupported or malformed structures, invalid XML 1.0 content, excessive nesting/container growth, invalid worksheet names, invalid freeze panes, and package-bound violations. File publication must be race-safe, deterministic, and explicit about overwrite behavior. A successful local build is not durable release publication authority. + +### SSR and native forms + +A hidden/native form field contains client-controlled submission data. It is not authentication, authorization, CSRF protection, tenant identity, integrity proof, or durable-write evidence. Server rendering must not construct the browser editor. Hosts must validate and authorize submitted values independently. + +### Revision, selection, transition, and autosave evidence + +Local SHA-256 revisions identify deterministic content equality only. Selection coordinates are structural positions bound to one exact editor state and are not durable cross-revision anchors. Transition evidence does not prove actor, time, authorization, or durable persistence. Autosave observers must emit only document-free lifecycle metadata; observer failures cannot alter ordering or save outcomes. Durable compare-and-swap requires a host/server-selected strong validator under RFC 9110 semantics and must not be replaced with a local content digest. + +### Concurrency and stale state + +Concurrent editors, delayed digests, stale selections, ambiguous transport failure, or stale durable validators can cause lost updates or false success. Inkspan must bind asynchronous evidence to one immutable local state, keep autosave single-flight with bounded pending work, fail closed on ambiguous durable outcomes, and require explicit recovery from blocked conflict/failure states. Hosts own atomic persistence transactions and durable conflict resolution. + +### Collaboration and Yjs + +Inkspan may bind to Yjs-compatible document/awareness surfaces but does not own provider creation, room authorization, tenant identity, persistence, retention, or durable audit. Yjs updates and awareness metadata can contain sensitive tenant information. Host providers must authenticate rooms, authorize membership, bound awareness disclosure, and apply retention/encryption policy. Inkspan must not silently create a network provider or elevate an awareness update into authorization. + +### Model-assisted authoring + +Model output is untrusted proposed content. Deterministic editor/conversion validation remains authoritative. Inkspan must not own or expose model credentials, route models, authorize external data use, or treat model output as approved durable content. Hosts own model policy, redaction, routing, logging, retention, user approval, and tenant disclosure decisions. + +### Release and supply chain + +Stale draft assets, digest mismatch, mutable dependency/workflow references, absent SBOM/provenance, package drift, or stale exact-head checks can produce unverifiable releases. Publication must fail closed on ambiguous artifact inventory or digest mismatch. Exact-head CI/security/package/provenance evidence does not transfer after source movement. Automated comments/statuses are not formal approval. + +## Security invariants + +1. No Inkspan code path acquires host transport, tenant, credential, durable-storage, or model-routing authority implicitly. +2. Untrusted document/clipboard/form/Office input is validated before it reaches privileged or durable boundaries. +3. Public failures are bounded and redacted; document bodies, secrets, private exception causes, and tenant values are not reflected into generic diagnostics. +4. Resource ceilings apply before expensive traversal, parsing, hashing, rendering, or packaging where practical. +5. Provider-neutral collaboration cannot create or destroy the host provider. +6. Lifecycle and evidence metadata remains document-free unless an explicit versioned contract says otherwise. +7. Deterministic conversion is separate from model-assisted authoring. +8. Release authority requires exact integrated protected-head evidence and repository policy, not local or predecessor evidence. + +## Verification and residual risk + +Verification uses exact owned production coverage, hostile-input regression tests, packed ESM/CommonJS/strict-TypeScript consumers, SSR tests, deterministic Office renderer tests, security scanning, package inspection, documentation contracts, and release evidence. Real-browser parser differential testing is required where the browser itself materially participates in security semantics. + +Residual risk remains in embedding-host policy, browser engine behavior, downstream renderers/viewers, provider implementations, host persistence/authorization, model providers, and deployment configuration. Those risks must be addressed by host controls rather than by expanding Inkspan authority. + +## Rollback + +A security feature that proves unsafe is removed or disabled at the narrow Inkspan boundary while preserving canonical document readability and host-owned durable state. Rollback must not rewrite tenant persistence, weaken host authorization, or reinterpret release evidence. Material security-boundary changes require an ADR and fresh exact-head verification. diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md new file mode 100644 index 00000000..06b193b5 --- /dev/null +++ b/docs/TRACEABILITY.md @@ -0,0 +1,79 @@ +# Inkspan Standards and Evidence Traceability + +Status: Proposed canonical baseline + +## Purpose + +This record maps durable Inkspan product decisions to authoritative standards, primary technical documentation, repository evidence, and explicit claim limits. It is not a certification statement. Protected `main` remains the implementation authority; open pull requests are proposed or active evidence only. + +## Traceability matrix + +| Concern | Inkspan decision | Primary authority | Repository evidence | Claim limit | +|---|---|---|---|---| +| HTTP optimistic concurrency | Durable saves use a host/server-selected strong entity tag; local content digests do not substitute for durable `If-Match` authority | RFC 9110, HTTP Semantics | autosave session tests, `docs/document-autosave.md`, architecture concurrency sequence | Inkspan validates/coordinates local semantics; host owns atomic persistence and authorization | +| JSON envelope grammar | Versioned envelopes use strict JSON handling, duplicate-name defenses, bounded parsing, and explicit schema identity | RFC 8259; RFC 7493 where interoperable JSON constraints apply | envelope parser/resource-limit tests and package consumers | Current-schema parsing does not imply migration authority for unknown schemas | +| Envelope version routing | A bounded identity-only inspector may identify `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, Issue #74, envelope tests and planned framework-independent consumer contract | 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 | +| 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 | rich-clipboard unit/integration corpus and SafeClipboard ADR | jsdom-only success is not universal browser-engine conformance | +| Cross-engine release assurance | The same committed adversarial corpus must run under required Chromium, Firefox, and WebKit projects; only focused standards-grounded safe differences may be admitted | WHATWG HTML Living Standard; W3C Clipboard API and events; Playwright browser/project documentation | ADR 0016, Issue #66, TEST_STRATEGY, UML and planned exact-head browser evidence | The gate is planned behind SafeClipboard integration and does not claim byte-identical browser serialization or branded enterprise-policy coverage | +| Editor integration | Public behavior must exercise the actual TipTap/ProseMirror integration path, not an inert extension field or test-only hook | official TipTap and ProseMirror documentation for the locked dependency line | integration tests and package consumers | Inkspan does not claim compatibility with untested major-version integration semantics | +| Collaboration | Inkspan provides provider-neutral editor/Yjs bindings; host owns provider lifecycle, room authorization, awareness privacy, persistence and audit | official Yjs/provider documentation plus Inkspan public contract | collaboration tests and architecture ownership matrix | No network-provider or tenant-authorization authority is implied | +| Secure development | Security controls are developed test-first, with exact-head scanning/review/package evidence and root-cause regression | NIST SP 800-218 SSDF 1.1 | CI/security/SAST/package/provenance gates, doctoring and regression history | Repository evidence is not a claim of complete SSDF organizational conformance | +| Office rendering | JSON→DOCX/XLSX/PPTX is deterministic, bounded, network-free, macro-free, injection-aware and package-inspected | Office Open XML specifications and relevant Python package contracts | Office renderer tests, Python coverage/docstring/package gates | Format fidelity is limited to explicitly tested supported constructs | +| Release authority | Source movement invalidates exact-head evidence; stale assets/digest ambiguity fail closed; formal approval remains distinct from status/comments | GitHub protected-branch/review/release/attestation behavior and repository policy | release workflow tests, package checksums, SBOM/provenance, formal reviews | Local success or automated prose does not authorize protected merge/release | + +## Current primary references + +Bray, T. (Ed.). (2015). *The I-JSON Message Format* (RFC 7493). RFC Editor. https://doi.org/10.17487/RFC7493 + +Bray, T. (Ed.). (2017). *The JavaScript Object Notation (JSON) Data Interchange Format* (RFC 8259; STD 90). RFC Editor. https://doi.org/10.17487/RFC8259 + +Fielding, R., Nottingham, M., & Reschke, J. (Eds.). (2022). *HTTP Semantics* (RFC 9110; STD 97). RFC Editor. https://doi.org/10.17487/RFC9110 + +Rundgren, A., Jordan, B., & Erdtman, S. (2020). *JSON Canonicalization Scheme (JCS)* (RFC 8785). RFC Editor. https://doi.org/10.17487/RFC8785 + +Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software Development Framework (SSDF) Version 1.1: Recommendations for Mitigating the Risk of Software Vulnerabilities* (NIST SP 800-218). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-218 + +Web Hypertext Application Technology Working Group. (2026). *HTML Standard: Parsing HTML documents* (Living Standard). Retrieved August 9, 2026, from https://html.spec.whatwg.org/multipage/parsing.html + +World Wide Web Consortium. (2026, June 24). *Clipboard API and events* (W3C Working Draft). https://www.w3.org/TR/2026/WD-clipboard-apis-20260624/ + +World Wide Web Consortium. (2024, December 12). *Web Content Accessibility Guidelines (WCAG) 2.2*. https://www.w3.org/TR/WCAG22/ + +World Wide Web Consortium. (2013). *PROV-DM: The PROV Data Model*. https://www.w3.org/TR/prov-dm/ + +Microsoft. (n.d.). *Browsers*. Playwright documentation. Retrieved August 9, 2026, from https://playwright.dev/docs/browsers + +Microsoft. (n.d.). *Projects*. Playwright documentation. Retrieved August 9, 2026, from https://playwright.dev/docs/test-projects + +## Research-backed concurrency rationale + +The autosave design uses explicit optimistic-concurrency boundaries: Inkspan coordinates local single-flight work and immutable local evidence, while the host performs the authoritative atomic compare-and-swap. The classic optimistic-concurrency literature is rationale, not protocol authority: + +Kung, H. T., & Robinson, J. T. (1981). On optimistic methods for concurrency control. *ACM Transactions on Database Systems, 6*(2), 213–226. https://doi.org/10.1145/319566.319567 + +## Evidence hierarchy + +1. Protected `main` source and versioned public contracts. +2. Exact-current-head CI/security/package/provenance evidence for the source being considered. +3. Formal current-head review and repository protection state. +4. Canonical PRD/TRD/Architecture/ADRs/UML/data model/security/test/operability documents synchronized to that source. +5. Feature doctoring and implementation plans. +6. Historical PR bodies, predecessor-head results, conversations, comments, and local-only evidence. + +Lower levels may explain intent or history but cannot override a contradictory higher-level source. + +## Status discipline + +- **Implemented** means present in protected `main` and verified under its release/product contract. +- **Proposed** means documented or implemented on an unmerged branch and not yet protected-main authority. +- **Planned** means an accepted future direction without a protected implementation. +- **Superseded** means retained for history but replaced by a later explicit decision. + +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, 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. diff --git a/docs/TRD.md b/docs/TRD.md new file mode 100644 index 00000000..d3ab19bb --- /dev/null +++ b/docs/TRD.md @@ -0,0 +1,110 @@ +# Inkspan Technical Requirements + +Status: Proposed canonical baseline + +## Authority and runtime boundary + +Inkspan owns deterministic editor, conversion, document-envelope, local evidence, local autosave ordering, accessibility metadata, package, and provider-neutral adapter behavior. Hosts own network transport, authentication, authorization, tenant isolation, durable persistence, credentials, migrations, retention, durable audit storage, deployment, collaboration-provider lifecycle, and model-use policy. + +Protected `main` is the implementation authority. Open PRs may provide Proposed contracts or evidence but do not become shipped behavior until protected integration. + +## Package and module boundaries + +The root product may compose TipTap/ProseMirror, React-facing integration, deterministic conversion, revision evidence, autosave, collaboration adapters, email/base64 utilities, and Office rendering. Framework-independent subpaths must not accidentally require React, DOM globals, TipTap UI, ProseMirror view, Yjs, network, credentials, database clients, or model SDKs when their public contract excludes those dependencies. + +Standalone use must not require naruon or contextual-orchestrator. A CWL host can compose Inkspan additively through stable interfaces. + +## Document identity and schema boundary + +A versioned `document_envelope` is validated before canonicalization or hashing. Strict JSON text/byte handling rejects malformed input, duplicate object names, malformed UTF-8, unsupported schemas, hostile JavaScript object descriptors, and resource-limit violations according to the active contract. + +SHA-256 revision evidence identifies exact canonical content. Local content digests are equality evidence only and never replace server-selected RFC 9110 strong validators for durable compare-and-swap. Unknown schema migration remains host-owned; a future identity-inspection helper may expose routing metadata without becoming a permissive current-schema parser or migration executor. + +## Markdown/HTML editor boundary + +TipTap/ProseMirror is the deterministic editing authority for supported Markdown/HTML behavior. Import/export adapters must state supported and lossy constructs explicitly. Generated or rendered output does not silently replace canonical source state. Editor integrations validate extension/plugin configuration and public callback boundaries fail closed without executing accessors merely to inspect untrusted configuration. + +## Rich clipboard boundary + +Rich clipboard HTML is untrusted. Sanitization must be installed in the actual TipTap/ProseMirror paste pipeline, use bounded fail-closed validation, perform no external fetch or active execution, and preserve only supported semantic content. Pure sanitizer APIs and editor integration must remain behaviorally consistent. + +Where browser fragment parsing or serialization can change security semantics, a release must run the same adversarial corpus through dependency-locked Playwright Chromium, Firefox, and WebKit projects. Differences require an explicit standards/threat rationale rather than an unconditional parity normalization. + +## SSR and native forms + +SSR/native-form serialization is implemented on protected `main`. Server rendering must not instantiate an editor view. When a native field is explicitly enabled, SSR/hydration serializes the selected controlled Markdown/HTML value as escaped client-controlled input. Controlled `value` precedes `defaultValue` for that field. Before TipTap initializes, prop updates remain authoritative; once the editor is authoritative, document transactions synchronously mirror the configured serialization into the form field, and native reset processing restores the live serialized value when the host does not reset the editor. + +The hidden/native field is never authentication, authorization, CSRF protection, tenant identity, signature, integrity proof, or durable-write evidence. Hosts independently validate and authorize submissions. Collaborative Yjs content remains absent from the server shell until the host-owned client collaboration lifecycle is established. + +## Revision-scoped evidence + +Selection evidence captures structural coordinates and canonical document state from the same immutable editor snapshot before asynchronous hashing. Transition evidence validates previous and resulting envelopes before sequential revision derivation. Ordinary evidence contains revision/coordinate/change metadata rather than document bodies and does not synthesize actor, tenant, time, model identity, signature, authorization, transport result, or durable-persistence claims. + +Selection and transition evidence are implemented on protected `main`; their local equality/lineage claims remain intentionally narrower than host-owned authorization, occurrence provenance, durable audit, re-anchoring, and persistence authority. + +## Autosave state machine and durable concurrency + +States are `idle`, `saving`, `blocked`, `closing`, and `closed`, with explicit blocked reasons. The local queue remains single-flight and retains bounded active/pending work and bounded flush waiters. Evidence supplied to a callback is immutable and validated before scheduling. + +Durable sessions carry a host/server-selected strong HTTP entity tag. Successful host callbacks may advance the durable validator only from a validated replacement strong tag. Conflict, malformed result, callback failure, hostile reflection, promise-assimilation failure, timeout/network ambiguity, or explicit host failure never silently advances it. + +Autosave lifecycle observation is implemented on protected `main`. Observation is optional and bounded to one construction-time callback. Construction emits nothing. Only distinct externally visible document-free snapshots are emitted. A method call producing no visible lifecycle transition, including a no-op `resume()`, emits nothing. Observer exceptions are isolated from queue ordering, save outcomes, recovery, and durable-validator handoff. + +## Provider-neutral collaboration + +Inkspan may bind to host-supplied Yjs-compatible document/awareness surfaces. Inkspan does not create/destroy production network providers and does not own room identity, tenant admission, user identity, authorization, provider credentials, reconnect policy, awareness privacy, update persistence, retention, encryption, or durable audit. + +Provider updates and awareness state are untrusted tenant data, not authorization evidence. Provider outage/degraded mode is resolved by host policy; Inkspan must not invent durable collaboration success. + +## Deterministic conversion and Office renderer + +Deterministic conversion is an authority boundary separate from model-assisted authoring. Model output may become a proposed document change, but deterministic editor/conversion validation decides whether the resulting content is structurally acceptable. + +The Office JSON→DOCX/XLSX/PPTX renderer is **network-free**, **macro-free**, model-free, and Desktop-Office-free. It must: + +- validate the versioned input contract before publication; +- reject invalid XML 1.0 content and unsupported/non-JSON structures; +- bound bytes, strings, values, nesting, containers, and cyclic/aliased input according to the active renderer contract; +- enforce Excel worksheet-name, workbook, row/column, freeze-pane, and supported-cell limits; +- neutralize untrusted spreadsheet **formula**-significant strings under the supported value contract rather than silently creating formulas; +- preserve deterministic package metadata and supported document structure; +- publish through race-safe file operations with explicit overwrite semantics; and +- fail closed without reusing partial artifacts after validation or write failure. + +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. + +## Model-assisted authoring + +Inkspan does not own model credentials, provider routing, prompt retention, external-data-use approval, tenant disclosure policy, or model audit. A host may route assistance through contextual-orchestrator or another approved provider. Model output is untrusted proposed content and cannot bypass deterministic clipboard/document/conversion validation, user/host approval, or durable save authorization. + +## Naruon and CWL composition + +A naruon host may mount Inkspan through `compose` / `ui.panel` using a narrow client boundary. The host supplies serializable non-secret configuration, authenticated API calls, server-selected strong validators, accessible conflict/recovery UX, provider lifecycle, and optional model routing. Cross-document mounts use explicit host state/identity so editor/autosave state cannot bleed across documents. + +Central `.github` automation, contextual-orchestrator, and other CWL repositories are external bounded contexts. Inkspan does not copy a central defect locally merely to make a leaf gate green. + +## Failure and diagnostic semantics + +Public failures are bounded, deterministic where practical, and redacted. Document bodies, credentials, tenant identifiers, complete provider metadata, callback values, private exception causes, prompts/model output, and durable validators are not reflected into generic public diagnostics unless a versioned authorized contract explicitly requires them. + +Cancellation, retry/offline policy, network timeout budgets, durable reconciliation, and user-facing localized recovery remain host responsibilities when they involve host transport or persistence. + +## Accessibility and interaction semantics + +Toolbar shortcut metadata is implemented on protected `main`. Shipped keyboard behavior, focus behavior, native controls, `aria-pressed`, `aria-keyshortcuts`, programmatic save/conflict state, and visible shortcut documentation must agree. Repository-level keyboard behavior outranks extension-local defaults when determining metadata. Status must not depend on color alone. Inkspan exposes machine state sufficient for host WCAG-oriented messaging while leaving localization and application-specific live-region policy to the host. + +## Packaging, compatibility, and release evidence + +The root package and framework-independent subpaths are verified from packed artifacts under ESM, CommonJS, and strict TypeScript consumers. Office Python surfaces are verified under the documented supported Python matrix, exact production statement/branch/function/line coverage, complete public docstrings, built wheel/package inspection, and license/dependency consistency. + +Release publication verifies one exact integrated protected source head, expected artifact inventory and digests, package/runtime compatibility, security/coverage/accessibility/document-fidelity gates, SBOM/provenance/reproducibility where configured, zero valid unresolved findings, formal review/branch-protection requirements, and post-publication artifact smoke evidence. + +Queued, cancelled, skipped-required, absent, stale-head, predecessor-head, status-only, author-only, or synthetic-merge evidence is not success. A commit status, automated model verdict, comment, formal review, and merge authority are distinct evidence classes. + +## Security, privacy, and operability dependencies + +`SECURITY.md`, `docs/THREAT_MODEL.md`, `docs/TEST_STRATEGY.md`, `docs/OPERABILITY.md`, `docs/TRACEABILITY.md`, and the detailed ADR corpus are part of this technical contract. Root `SECURITY.md` is `implemented_on_protected_main` and is the normative private vulnerability-reporting/coordinated-disclosure policy. ADR 0017 records the durable decision, ownership boundary, claim limits, and recovery/supersession semantics without duplicating the policy text. + +## Implemented versus proposed + +Protected `main` is the sole implemented baseline. The security disclosure lifecycle, autosave lifecycle observation, toolbar shortcut accessibility metadata, SSR/native-form serialization, revision-scoped selection evidence, and document-transition evidence are implemented on protected `main`. SafeClipboard remains Proposed until merged; migration routing and cross-engine browser assurance remain planned according to their dependency order. Canonical docs distinguish target architecture from shipped behavior and must be updated when the protected implementation changes. diff --git a/docs/UML.md b/docs/UML.md new file mode 100644 index 00000000..fa6c3af5 --- /dev/null +++ b/docs/UML.md @@ -0,0 +1,382 @@ +# Inkspan Runtime Diagrams + +Status: Proposed canonical baseline + +These diagrams describe the accepted product boundary. Protected `main` remains implementation authority; elements owned by open PRs are Proposed until integrated. + +## Bounded-context and component topology + +```mermaid +flowchart LR + User[Author or reviewer] + HostApp[Standalone host or naruon compose / ui.panel] + EditorCore[Inkspan TipTap / ProseMirror editor] + ClipboardBoundary[Safe rich clipboard] + EvidenceCore[Envelope revision selection transition evidence] + AutosaveCore[Bounded local autosave] + ConversionCore[Deterministic Markdown / HTML conversion] + OfficeRenderer[Deterministic Office renderer] + CollaborationAdapter[Provider-neutral Yjs adapter] + HostProvider[Host-owned Yjs provider] + HostStore[Host persistence / audit] + ModelGateway[Host-approved contextual-orchestrator or model gateway] + ControlPlane[ContextualWisdomLab/.github control plane] + + User --> HostApp + HostApp --> EditorCore + EditorCore --> ClipboardBoundary + EditorCore --> EvidenceCore + EditorCore --> AutosaveCore + EditorCore --> ConversionCore + ConversionCore --> OfficeRenderer + EditorCore <--> CollaborationAdapter + CollaborationAdapter <--> HostProvider + AutosaveCore --> HostStore + HostApp --> ModelGateway + ControlPlane -. CI-only; not runtime: security review provenance release policy .-> EditorCore +``` + +The host owns transport, authentication, authorization, tenant isolation, persistence, credentials, provider lifecycle, retention, deployment, durable audit, and model-use policy. Inkspan owns deterministic local editor/conversion/evidence behavior only. + +## Rich paste sequence + +```mermaid +sequenceDiagram + participant UserClipboard + participant PastePipeline + participant SafeClipboard + participant EditorState + UserClipboard->>PastePipeline: rich HTML + PastePipeline->>SafeClipboard: transform pasted HTML + SafeClipboard->>SafeClipboard: bounded inert parse and semantic allowlist + alt accepted + SafeClipboard-->>PastePipeline: sanitized supported content + PastePipeline->>EditorState: parse and transact accepted content + else malformed, active, hidden, resource-bearing, or over limit + SafeClipboard-->>PastePipeline: bounded failure / safe omission + end +``` + +Security-relevant browser fragment semantics require the same hostile corpus under Chromium, Firefox, and WebKit before the relevant release line. + +## Cross-engine browser-semantic release assurance + +```mermaid +flowchart TB + Head[One exact protected-source candidate head] + Corpus[Committed synthetic adversarial clipboard corpus] + Lock[Immutable package lock and Playwright/browser revisions] + Chromium[Chromium project] + Firefox[Firefox project] + WebKit[WebKit project] + Semantic{Security-semantic result agrees?} + Difference{Serialization difference only?} + Basis[Focused standards basis + threat analysis + rollback note] + Repair[Repair sanitizer/integration test-first] + ReleaseGate[Rich-clipboard release assurance eligible] + Blocked[Release blocked; unrelated work may continue] + + Head --> Corpus + Head --> Lock + Corpus --> Chromium + Corpus --> Firefox + Corpus --> WebKit + Lock --> Chromium + Lock --> Firefox + Lock --> WebKit + Chromium --> Semantic + Firefox --> Semantic + WebKit --> Semantic + Semantic -->|yes| ReleaseGate + Semantic -->|no| Difference + Difference -->|proven safe standards-permitted difference| Basis --> ReleaseGate + Difference -->|unsafe, unexplained, missing browser, skipped or failed| Repair --> Blocked +``` + +A queued, pending, skipped, cancelled, absent or failed required browser is not passing evidence. Differences are never normalized merely to make engines agree; any admitted difference is a reviewed compatibility artifact. ADR 0016 and Issue #66 own this planned release-assurance decision behind the SafeClipboard integration dependency. + +## Author-to-model proposal sequence + +```mermaid +sequenceDiagram + participant Author + participant Host as Host / naruon + participant Gateway as Host-approved model gateway + participant Editor as Inkspan editor + participant Validator as Deterministic Inkspan validation + + Author->>Host: request assistance for explicit document scope + Host->>Host: authorize tenant and external model use + Host->>Gateway: send host-approved bounded context + Gateway-->>Host: untrusted proposed content + Host->>Editor: present proposal without durable apply + Editor->>Validator: parse / sanitize / convert / validate proposal + Validator-->>Author: diff and validation result + alt author / host approves + Author->>Editor: apply proposal to local document state + else rejected or invalid + Author-->>Editor: keep current document + end +``` + +Model output never bypasses deterministic validation, host authorization, user review, or durable save concurrency. + +## Import and export flow + +```mermaid +flowchart LR + Source[Supported Markdown / HTML or versioned envelope] + Validate[Strict bounded validation] + Editor[Inkspan editor state] + ExportChoice{Requested deterministic output} + Markdown[Markdown] + Html[HTML] + OfficeRequest[Versioned Office render request] + Office[DOCX / XLSX / PPTX artifact] + + Source --> Validate --> Editor --> ExportChoice + ExportChoice --> Markdown + ExportChoice --> Html + ExportChoice --> OfficeRequest --> Office +``` + +Unsupported or lossy constructs are surfaced by the relevant contract instead of being advertised as lossless round-trip fidelity. + +## Envelope identity and host-owned migration routing + +```mermaid +sequenceDiagram + participant Host + participant Inspector as Planned bounded identity inspector + participant Registry as Host migration registry + participant Migration as Host-owned version migration + participant StrictParser as Current-schema strict parser + participant Inkspan + + Host->>Inspector: complete untrusted envelope + Inspector->>Inspector: bounded JSON/UTF-8/duplicate-name/descriptor validation + alt identity invalid + Inspector-->>Host: stable redacted failure + else identity valid + Inspector-->>Host: frozen schemaId + schemaVersion only + Host->>Registry: select authorized migration route + alt current supported identity + Host->>StrictParser: validate current envelope + else registered legacy/future route + Registry->>Migration: run version-specific host migration + Migration-->>Host: candidate current-schema envelope + Host->>StrictParser: strict current-schema validation + else no route + Registry-->>Host: unsupported version; preserve original source + end + StrictParser-->>Inkspan: canonical document only after strict success + end +``` + +The identity result does not contain the document body and does not prove migration, authorization, persistence or durable success. ADR 0015 and Issue #74 define this planned routing aid; the host continues to own schema registry, migration execution, persistence, audit and rollback. + +## Office render and file publication sequence + +```mermaid +sequenceDiagram + participant Caller + participant Renderer as Deterministic Office renderer + participant Validator + participant Builder + participant Publisher as Race-safe file publication + participant Artifact + + Caller->>Renderer: versioned JSON render request + Renderer->>Validator: validate schema, XML, limits, names, freeze panes, values + Validator->>Validator: neutralize supported untrusted formula-significant strings + alt invalid or unsupported + Validator-->>Caller: bounded failure, no successful artifact + else valid + Validator->>Builder: validated inert model + Builder->>Builder: build network-free macro-free Office package + Builder->>Publisher: complete candidate artifact + alt publication succeeds under explicit overwrite policy + Publisher-->>Artifact: committed DOCX / XLSX / PPTX + Artifact-->>Caller: deterministic artifact result + else publication fails or conflicts + Publisher-->>Caller: bounded failure, partial output is not success + end + end +``` + +## File publication state machine + +```mermaid +stateDiagram-v2 + [*] --> validating + validating --> rejected: invalid / unsupported / over limit + validating --> building: validated request + building --> failed: build failure + building --> publishing: complete candidate package + publishing --> committed: publication succeeds + publishing --> conflicted: existing target without approved overwrite + publishing --> failed: write / race / validation failure + committed --> [*] + rejected --> [*] + conflicted --> [*] + failed --> [*] +``` + +No failed or partial file publication becomes a successful conversion artifact. + +## Autosave sequence and no-op observer rule + +```mermaid +sequenceDiagram + participant Host + participant Queue + participant DurableSave + Host->>Queue: enqueue immutable revision evidence + Queue-->>Host: saving snapshot when observer exists + Queue->>DurableSave: one active save with host strong validator + DurableSave-->>Queue: saved or conflict / ambiguous failure + alt saved + Queue-->>Host: next distinct lifecycle snapshot and coherent replacement validator + else blocked + Queue-->>Host: blocked snapshot + Host->>Queue: authenticated recovery / resume + alt resume changes state + Queue-->>Host: next distinct lifecycle snapshot + else resume is false / no-op + Queue-->>Host: no observer notification + end + end +``` + +## Autosave state machine + +```mermaid +stateDiagram-v2 + [*] --> idle + idle --> saving: enqueue + saving --> idle: saved without pending + saving --> saving: continue pending + saving --> blocked: conflict or failure + blocked --> saving: resume with pending + blocked --> idle: resume without pending + idle --> closed: close + blocked --> closed: close + saving --> closing: close during active save + closing --> closed: active save settles +``` + +## SSR and native form sequence + +```mermaid +sequenceDiagram + participant Server + participant Browser + participant Editor + participant NativeForm + participant Host + Server-->>Browser: escaped controlled native field value + Browser->>Editor: client hydration and editor creation + Editor->>NativeForm: mirror serialization on document transaction + NativeForm-->>Host: untrusted submitted value + Host->>Host: authenticate, authorize, validate, enforce tenant and concurrency policy +``` + +## Selection and revision capture + +```mermaid +sequenceDiagram + participant Caller + participant EditorState + participant RevisionDerivation + Caller->>EditorState: request selection revision evidence + EditorState->>EditorState: capture one immutable state and structural selection + EditorState->>RevisionDerivation: canonical envelope from same state + RevisionDerivation-->>Caller: frozen revision and structural coordinates +``` + +## Provider-neutral Yjs collaboration sequence + +```mermaid +sequenceDiagram + participant Host + participant Provider as Host-owned Yjs provider + participant YDoc as Host-supplied Yjs document + participant Inkspan + participant Awareness as Host-governed awareness + + Host->>Provider: authenticate / authorize room and create lifecycle + Provider<->>YDoc: synchronize host-owned updates + Host->>Inkspan: mount with supplied YDoc / awareness binding + Inkspan<->>YDoc: deterministic editor binding + Inkspan<->>Awareness: bounded awareness presentation + Note over Inkspan,Provider: Inkspan does not own credentials, room authorization, persistence, retention, or provider destruction + Host->>Inkspan: unmount panel / editor + Host->>Provider: retain or destroy provider according to host lifecycle +``` + +## naruon modular composition + +```mermaid +flowchart TB + Route[naruon route / product shell] + Panel[naruon compose / ui.panel client boundary] + Inkspan[Inkspan editor module] + HostApi[Authenticated host API] + Persistence[Host persistence and audit] + Yjs[Host-owned Yjs provider] + Orchestrator[Optional contextual-orchestrator] + + Route --> Panel --> Inkspan + Panel --> HostApi --> Persistence + Inkspan <--> Yjs + Panel --> Orchestrator + Orchestrator -. untrusted proposal .-> Panel +``` + +Inkspan remains importable without naruon or contextual-orchestrator. + +## Deployment topology + +```mermaid +flowchart LR + Browser[Browser / desktop webview] + InkspanJs[Inkspan JS/TS package] + OfficeProcess[Optional deterministic Office renderer process/library] + HostBackend[Host backend] + HostDb[Host durable store] + HostYjs[Host collaboration provider] + HostModel[Host model gateway] + + Browser --> InkspanJs + InkspanJs -->|host callback / application API| HostBackend + HostBackend --> HostDb + Browser <--> HostYjs + HostBackend --> HostModel + InkspanJs -->|explicit conversion request in supported deployment| OfficeProcess +``` + +The diagram does not imply a required network service owned by Inkspan. The Office renderer may be used as an independent library/tool under its package contract. + +## Failure and degraded modes + +```mermaid +flowchart TD + Event{Failure boundary} + Event -->|clipboard / import invalid| LocalReject[Reject safely; editor remains usable] + Event -->|durable save conflict / ambiguity| SaveBlocked[Blocked until explicit host recovery] + Event -->|Yjs provider unavailable| CollabDegraded[Host chooses local edit / read-only / reconnect / block] + Event -->|model gateway unavailable| ManualMode[Deterministic manual editing continues] + Event -->|Office render invalid / publication fails| RenderFailed[No successful artifact; source document preserved] + Event -->|release evidence stale / incomplete| ReleaseBlocked[No publication] +``` + +## Authority and release evidence flow + +```mermaid +flowchart TB + InkspanAuthority[Inkspan deterministic authority] --> EditorSemantics[Editor / conversion / revision semantics] + HostAuthority[Host authority] --> HostServices[Transport / identity / tenancy / persistence / audit / model policy] + MachineEvidence[Exact-head CI / security / package / browser / Office / provenance] --> ProtectedMerge[Protected merge] + IndependentReview[Independent formal review where required] --> ProtectedMerge + ProtectedMerge --> ReleaseAuthority[Exact protected release authority] +``` diff --git a/docs/adr/0001-product-host-authority.md b/docs/adr/0001-product-host-authority.md new file mode 100644 index 00000000..975f4c58 --- /dev/null +++ b/docs/adr/0001-product-host-authority.md @@ -0,0 +1,41 @@ +# ADR 0001: Product and host authority boundary + +Status: Proposed + +## Context + +Inkspan must work as a standalone authoring/conversion product and as a module inside naruon or another CWL host. Mixing host transport, tenant, identity, persistence, credential, deployment, retention, audit, or model-routing authority into the editor package would make standalone use harder and create hidden security coupling. + +## Alternatives considered + +- Let Inkspan own transport, persistence, tenancy, credentials, and collaboration-provider lifecycle. Rejected because it creates hidden privilege and makes independent package use depend on one deployment architecture. +- Provide separate standalone and CWL-host product forks. Rejected because duplicated implementations would drift on document, conversion, and security semantics. +- Keep Inkspan deterministic and capability-bounded while hosts inject explicitly owned services. Selected because it preserves standalone reuse and makes authority reviewable at integration boundaries. + +## Decision + +Inkspan owns deterministic editor, conversion, canonical envelope, local evidence, local autosave ordering, accessibility metadata, package, and provider-neutral adapter behavior. Hosts own transport, authentication, authorization, tenant isolation, durable persistence, credentials, migrations, retention, deployment, durable audit storage, collaboration-provider lifecycle, and model-use policy. + +## Consequences + +The same package can be embedded or used independently. Host failures remain explicit adapter failures rather than becoming implicit editor state. Some end-to-end properties, such as tenant authorization and durable atomic writes, cannot be proven by Inkspan alone and require host verification. + +## Failure and recovery + +If an Inkspan change begins to create network providers, choose tenants, store secrets, authorize model calls, or claim durable persistence success, fail the architecture contract and revert the authority expansion. Host integration defects are repaired at the host boundary rather than by granting Inkspan broader privilege. + +## Security and privacy impact + +Least privilege is explicit: Inkspan receives only capabilities necessary for deterministic local behavior and does not acquire tenant credentials or durable authorization authority. Document bodies and host-confidential metadata remain subject to host purpose, access, encryption, logging, retention, and deletion policy rather than being copied into a new Inkspan control plane. + +## Compatibility and migration + +Existing standalone package consumers require no host service. Host integrations evolve additively through explicit capabilities and versioned public contracts. Any future transfer of persistence, tenant, credential, provider, or deployment ownership into Inkspan is a breaking architecture change requiring an accepted ADR, migration and rollback plan, revised threat model, and compatibility evidence for standalone consumers. + +## Verification + +Canonical Architecture/PRD/TRD ownership language, integration tests, package-consumer isolation, provider-neutral collaboration tests, and documentation contracts must agree. Protected `main` remains the implementation authority. + +## Rollback or supersession + +Rollback removes the authority-expanding path while preserving deterministic document compatibility. Supersession requires a material architecture proposal with threat model, migration/rollback, deployment, and tenant-ownership evidence. diff --git a/docs/adr/0002-document-revision-authority.md b/docs/adr/0002-document-revision-authority.md new file mode 100644 index 00000000..15a68373 --- /dev/null +++ b/docs/adr/0002-document-revision-authority.md @@ -0,0 +1,41 @@ +# ADR 0002: Canonical document and revision authority + +Status: Proposed + +## Context + +Inkspan needs deterministic document persistence/export and local equality evidence without turning a content digest into authorization, identity, or durable concurrency authority. + +## Alternatives considered + +- Use editor-local JSON or rendered HTML directly as durable identity. Rejected because semantically equivalent values can serialize differently and because rendered output is not the canonical source authority. +- Treat a SHA-256 content digest as the durable concurrency token. Rejected because a client-derived digest does not prove the server's current durable representation or authorization state. +- Use a versioned canonical envelope for local equality and a host/server-selected validator for durable concurrency. Selected because it separates deterministic content identity from durable authority. + +## Decision + +A versioned validated `document_envelope` is the canonical document value. Strict JSON/UTF-8 handling and bounded validation precede canonicalization. SHA-256 revision evidence is derived from the exact canonical envelope and is equality evidence only. Durable compare-and-swap uses a host/server-selected strong validator under RFC 9110 semantics. + +## Consequences + +Local restore, transition, and selection evidence can bind to exact content without copying document bodies. Hosts must maintain their own durable version, actor, authorization, audit, migration, and retention semantics. Unsupported envelope versions remain explicit migration routes rather than permissive parsing. + +## Failure and recovery + +Malformed, ambiguous, over-limit, unsupported, or hostile envelope input fails closed with bounded diagnostics. If local state moves during asynchronous evidence capture, the operation returns no stale claim. Durable ambiguity is reconciled by the host; a local digest never advances durable state. + +## Security and privacy impact + +Canonicalization and revision derivation operate without granting network, tenant, credential, or persistence authority. Revision values can still be tenant-confidential correlation metadata, so they must not be exposed as public high-cardinality telemetry or treated as bearer capabilities. Ordinary evidence avoids duplicating complete document bodies. + +## Compatibility and migration + +Envelope schema identifiers and versions are explicit compatibility boundaries. Existing revision values must remain stable for the same accepted canonical contract. Unsupported legacy/future versions route through host-owned migration rather than being interpreted by the current parser. Any canonicalization or digest change requires a new versioned contract, compatibility fixtures, migration guidance, and rollback evidence. + +## Verification + +Envelope parsing/canonicalization/UTF-8/resource-limit regressions, revision/transition/selection tests, packed consumer tests, and RFC 9110/JCS documentation must remain coherent. + +## Rollback or supersession + +A future algorithm/schema change adds a versioned contract and migration path. It must not silently redefine existing revision values. Supersession requires compatibility tests, rollback, and updated traceability. diff --git a/docs/adr/0003-safe-rich-clipboard.md b/docs/adr/0003-safe-rich-clipboard.md new file mode 100644 index 00000000..2b235f53 --- /dev/null +++ b/docs/adr/0003-safe-rich-clipboard.md @@ -0,0 +1,41 @@ +# ADR 0003: Safe rich clipboard boundary + +Status: Proposed + +## Context + +Rich clipboard HTML is attacker-controlled browser input. A sanitizer that is correct only as a pure helper but is not installed in the real TipTap/ProseMirror paste pipeline does not protect the product. Browser fragment parsing can also differ across Chromium, Firefox, and WebKit. + +## Alternatives considered + +- Accept browser/TipTap parsing with only downstream rendering controls. Rejected because active, hidden, resource-bearing, or malformed content can enter editor state before later controls run. +- Sanitize only through a standalone helper that hosts may call. Rejected because omission or wrong pipeline ordering would leave the default editor path unprotected. +- Install one bounded semantic sanitizer in the real paste pipeline while retaining the pure API and cross-engine verification. Selected because it binds the trust boundary to actual editor behavior and preserves deterministic direct use. + +## Decision + +Sanitize untrusted rich HTML through a bounded, fail-closed semantic allowlist installed in the actual TipTap/ProseMirror paste pipeline. The sanitizer performs no external fetch or active execution, rejects unsupported active/resource-bearing structures, applies resource ceilings, and keeps its pure API behavior aligned with editor integration. Browser-engine differential evidence is required before the rich-clipboard release line is published. + +## Consequences + +Supported semantic formatting can survive paste while active or hidden content is removed or rejected. A downstream host still owns CSP, network egress, rendering policy, tenant authorization, and any later transforms. Cross-engine tests increase release cost but reduce parser-specific acquisition risk. + +## Failure and recovery + +Malformed input, hostile DOM capabilities, reflection failures, over-limit input, or unsafe structures fail closed with bounded diagnostics. If a security-relevant engine difference is found, add it to the differential corpus and repair the sanitizer or explicitly document a standards-grounded safe difference. Do not broaden the allowlist merely to recover parity. + +## Security and privacy impact + +The boundary reduces active-content, external-resource, hidden-content, parser-confusion, and denial-of-service risk before clipboard content becomes editor state. It performs no network request, executes no embedded script, exposes no credential, and must not copy rejected source payloads or private exception causes into public diagnostics. Host CSP, tenant authorization, downstream rendering, and external egress policy remain independent controls. + +## Compatibility and migration + +This boundary may intentionally remove HTML constructs that were previously accepted by permissive paste behavior, so the default rich-paste change is a release-significant compatibility decision. Supported semantic constructs and any engine-specific safe differences are versioned through tests/documentation. Rollout requires browser-differential evidence; rollback may disable the rich transform or return to a narrower plain/supported-content path without accepting arbitrary HTML. + +## Verification + +Use sanitizer unit tests, actual editor paste-pipeline integration tests, hostile DOM/reflection cases, package consumers, security scans, and the dependency-locked Chromium/Firefox/WebKit differential release gate tracked by the rich-clipboard assurance issue. + +## Rollback or supersession + +Rollback disables the rich transform or reverts to a narrower safe input path without granting arbitrary HTML authority. Supersession requires an equivalent or stronger fail-closed browser-integrated boundary, threat analysis, and fresh cross-engine evidence. diff --git a/docs/adr/0004-durable-validator-autosave.md b/docs/adr/0004-durable-validator-autosave.md new file mode 100644 index 00000000..6e1b67d2 --- /dev/null +++ b/docs/adr/0004-durable-validator-autosave.md @@ -0,0 +1,44 @@ +# ADR 0004: Strong durable validator and bounded autosave + +Status: Proposed + +## Context + +Local editor revisions are useful equality evidence but cannot prove a durable server write. Autosave also must avoid concurrent duplicate writes, unbounded queued work, silent conflict recovery, and telemetry callbacks that alter persistence behavior. + +## Alternatives considered + +- Overlapping saves for every local edit: rejected because ordering and retained work become ambiguous. +- A client content digest as the durable validator: rejected because local equality evidence is not durable server authority. +- An unbounded FIFO of every intermediate revision: rejected because memory and write volume grow without a fixed ceiling. +- One active save plus one replaceable pending revision, with explicit blocked recovery and a host/server-selected strong validator: selected. + +## Decision + +Use a single-flight autosave queue with bounded active/pending work and explicit `idle`, `saving`, `blocked`, `closing`, and `closed` lifecycle state. A durable session accepts and advances only host/server-selected strong HTTP entity tags for compare-and-swap. Conflict or ambiguous save failure blocks progression until explicit recovery. Optional lifecycle observation emits only distinct document-free transitions; construction and no-op operations emit nothing, and observer exceptions are isolated. + +`getSnapshot()` is an explicit local coordination API rather than generic diagnostics. Its bounded document-free contract may expose `activeStrongEntityTag`, `pendingStrongEntityTag`, and `lastSavedStrongEntityTag` when those values exist so the host can reason about exact local queue/durable-validator state. These fields remain tenant-confidential equality/concurrency metadata: their presence in an authenticated in-process snapshot does not authorize disclosure, persistence, metrics, logging, or reuse as a public identifier. + +## Consequences + +Inkspan can coordinate deterministic local ordering while the host remains the only durable authority. Hosts must implement atomic `If-Match` semantics, authorization, retry policy, and durable reconciliation. UI can observe bounded machine state without polling or receiving document bodies. Consumers of `getSnapshot()` must treat validator fields as confidential local state and must not promote them to public telemetry dimensions. + +## Failure and recovery + +Malformed or weak validators fail closed. Conflict preserves durable uncertainty and requires authenticated host recovery. Ambiguous transport failure never advances the validator. Recovery with no pending work may return to idle; unsuccessful/no-op recovery does not synthesize an observer event. Closing waits only for bounded retained work. + +## Security and privacy impact + +Autosave coordination does not grant authorization or tenant authority. Strong validators and local revisions can be tenant-confidential equality metadata. The explicit in-process `getSnapshot()` contract may expose only the bounded active/pending/last-saved validator fields required for local coordination, while generic diagnostics, public metrics, unauthenticated logs, lifecycle notifications, and ordinary telemetry remain document-free and must never expose validators, credentials, complete document bodies, or private callback failures. Any durable/shared handling is host-authorized, purpose-bound, and minimum-disclosure. + +## Compatibility and migration + +The public queue/session states, the three optional validator snapshot fields, validator grammar, and result contracts are compatibility surfaces. Hosts adopting the durable session supply a valid server-selected strong validator, keep durable retry/conflict policy, and classify snapshot validator values as confidential local metadata. Any future lifecycle-state, snapshot-field, or retention change requires compatibility tests, migration guidance, and a rollback that preserves host durable state. + +## Verification + +Queue/session state-machine tests, active/pending/last-saved snapshot ordering tests, no-op observation regressions, callback-failure isolation, validator grammar tests, concurrency/flush/close regressions, packed ESM/CommonJS/strict-TypeScript consumers, and exact-head coverage/security gates verify the contract. Documentation tests must preserve the distinction between the explicit `getSnapshot()` coordination surface and generic diagnostics/telemetry. + +## Rollback or supersession + +Rollback removes optional lifecycle observation or durable-session convenience while preserving explicit `getSnapshot()`, local queue ordering, and host-owned durable writes. Removing or renaming validator snapshot fields requires a documented compatibility migration rather than silently changing the privacy contract. Supersession requires an equally bounded concurrency model, exact validator semantics, migration/rollback, and fresh package-consumer evidence. diff --git a/docs/adr/0005-revision-scoped-review-evidence.md b/docs/adr/0005-revision-scoped-review-evidence.md new file mode 100644 index 00000000..8dfa121d --- /dev/null +++ b/docs/adr/0005-revision-scoped-review-evidence.md @@ -0,0 +1,41 @@ +# ADR 0005: Revision-scoped review evidence + +Status: Proposed + +## Context + +Delayed review, annotation, AI assistance, and audit-like workflows need to refer to an exact editor state. Copying selected text or entire document bodies into ordinary metadata increases privacy exposure, while asynchronous hashing can accidentally bind coordinates to a later document if state is reread. + +## Alternatives considered + +- Store selected text or full documents in ordinary review metadata. Rejected because it duplicates sensitive content and still does not guarantee stable anchoring after edits. +- Capture coordinates first and reread the document later for hashing. Rejected because asynchronous work can bind coordinates and revision to different editor states. +- Capture structural coordinates and canonical content from one immutable editor snapshot, then derive minimum revision-scoped evidence. Selected because it preserves temporal consistency while minimizing disclosure. + +## Decision + +Capture selection coordinates and the canonical document envelope from the same immutable editor state before asynchronous revision derivation. Transition evidence validates previous and resulting envelopes before deriving both revisions. Evidence contains only the minimum revision/coordinate/change metadata required by the versioned contract and excludes document bodies, actor, tenant, time, authorization, model identity, transport result, signature, and durable-write claims. + +## Consequences + +Hosts can detect stale review coordinates and content transitions without duplicating full document content in routine evidence. Hosts remain responsible for durable annotation IDs, cross-revision re-anchoring, actor/time attribution, authorization, audit storage, and model-use policy. + +## Failure and recovery + +If editor state changes during a capture boundary, the API must not emit a stale mixed-state claim. Coordinates are valid only with the matching revision. A changed document requires an explicit host re-anchor, compare, merge, fork, or collaborative anchoring policy. + +## Security and privacy impact + +Ordinary evidence is deliberately content-minimized and does not synthesize actor, tenant, model, authorization, signature, or durable-save claims. Revision identifiers and structural coordinates can still correlate tenant activity, so hosts must treat them as purpose-bound metadata and avoid public high-cardinality telemetry or unauthenticated disclosure. + +## Compatibility and migration + +Evidence shape and coordinate semantics are versioned contracts. Existing evidence remains meaningful only against its exact revision. A future durable/collaborative anchor format requires an explicit new version or migration rule; old coordinates must not be silently reinterpreted against a new document generation. + +## Verification + +Use concurrency tests, range/caret cases, transition ordering tests, recursive document-content absence checks, frozen-output checks, packed ESM/CommonJS/strict-TypeScript consumers, and exact-head coverage/security gates. + +## Rollback or supersession + +Rollback removes the convenience evidence surface while preserving canonical envelope/revision primitives. Supersession requires a versioned anchor/evidence contract that retains minimum disclosure and explicitly defines compatibility/migration semantics. diff --git a/docs/adr/0006-ssr-native-form-boundary.md b/docs/adr/0006-ssr-native-form-boundary.md new file mode 100644 index 00000000..14573a8a --- /dev/null +++ b/docs/adr/0006-ssr-native-form-boundary.md @@ -0,0 +1,42 @@ +# ADR 0006: SSR and native form client-controlled boundary + +Status: Proposed + +## Context + +Inkspan must support server-rendered shells and optional native form integration without instantiating a browser editor on the server or treating a hidden field as trusted server state. Hydration must not lose the chosen initial value, and form serialization must stay synchronized once the editor becomes authoritative. + +## Alternatives considered + +- Construct TipTap/ProseMirror during server rendering. Rejected because editor-view behavior depends on browser/DOM capabilities and risks hydration divergence. +- Treat the native form field as a durable/authenticated document channel. Rejected because browser-controlled fields are untrusted request input. +- Omit all native-form support. Rejected because standards-based host forms benefit from an optional synchronized submission surface. +- Emit an escaped client-controlled field only when explicitly enabled, then mirror editor transactions after hydration. Selected. + +## Decision + +Server rendering never constructs a TipTap/ProseMirror editor view. When `formFieldName` is explicitly enabled, Inkspan emits the selected controlled Markdown/HTML serialization as an escaped read-only native input value through SSR and matching hydration. Once the editor is authoritative, document transactions synchronously mirror the current serialization into the native field. The submitted value is client-controlled input only. + +## Consequences + +SSR hosts can participate in native form flows while preserving hydration continuity. Hosts remain responsible for authentication, authorization, CSRF defenses, tenant isolation, request validation/limits, durable concurrency, persistence, and acceptance. Hosts that cannot place a document body in server HTML must not enable this field. + +## Failure and recovery + +Invalid or stale client values never become authorization or durable evidence. Native resets cannot silently desynchronize a configured field from an editor the host intentionally retains. If SSR/form integration proves unsafe for a host, omit the field and use an explicit host transport after independent server validation. + +## Security and privacy impact + +The native field is intentionally treated as attacker-controlled request data. Inkspan escapes serialized content and does not assign authentication, CSRF, tenant, signature, integrity, or durable-write authority to it. Because enabling the field can place the document body in HTML/form submission, hosts with stricter disclosure or retention requirements must opt out and use their own authorized transport. + +## Compatibility and migration + +`formFieldName` remains opt-in, so existing consumers keep their current transport behavior. Hosts adopting it must validate and authorize the submitted value exactly as any other client input. Future changes to field naming, serialization mode, hydration, or reset semantics require SSR/hydration compatibility tests and migration guidance; rollback is to remove the optional field without changing canonical document semantics. + +## Verification + +Use `renderToString`/SSR tests, controlled-over-default precedence, escaping cases, hydration continuity, synchronous transaction-to-`FormData` tests, reset behavior, opt-out non-disclosure, no server editor construction, and documentation contracts. + +## Rollback or supersession + +Rollback disables `formFieldName` or returns the host to explicit client transport without changing canonical document semantics. Supersession requires equivalent SSR safety, client-controlled-input treatment, compatibility tests, and a migration/rollback note. diff --git a/docs/adr/0007-provider-neutral-collaboration.md b/docs/adr/0007-provider-neutral-collaboration.md new file mode 100644 index 00000000..1bbda8be --- /dev/null +++ b/docs/adr/0007-provider-neutral-collaboration.md @@ -0,0 +1,41 @@ +# ADR 0007: Provider-neutral collaboration and host ownership + +Status: Proposed + +## Context + +Inkspan needs collaborative editing compatibility without choosing a network provider, room identity, tenant authority, credential, persistence store, retention policy, or audit system. Coupling those responsibilities to the editor would make standalone operation weaker and create hidden privilege. + +## Alternatives considered + +- Bundle a specific collaboration service/provider into Inkspan. Rejected because transport credentials, tenant admission, retention, reconnect policy, and deployment would become hidden editor responsibilities. +- Let Inkspan create and destroy host providers automatically. Rejected because a provider may be shared across product surfaces and its lifecycle belongs to the host. +- Accept host-supplied Yjs-compatible document/awareness bindings and keep provider lifecycle external. Selected because it preserves provider neutrality and standalone operation. + +## Decision + +Inkspan may bind editor state to host-supplied Yjs-compatible document and awareness surfaces. The host creates, authenticates, authorizes, monitors, reconnects, persists, expires, and destroys the collaboration provider. Inkspan does not create or destroy that provider and does not interpret awareness or update receipt as authorization or durable persistence evidence. + +## Consequences + +Standalone and enterprise hosts can select their own collaboration transport and deployment model. Provider lifecycle and tenant security remain testable in the host rather than buried in the editor. Inkspan must keep its collaboration adapter narrow and must tolerate provider-independent mounting/unmounting. + +## Failure and recovery + +Provider outage, authorization failure, room loss, or awareness-policy failure is surfaced to the host. The host decides whether the editor remains locally writable, becomes read-only, reconnects, forks, or blocks. Detaching Inkspan must not destroy a provider shared with other product surfaces or erase the host-owned Yjs document. + +## Security and privacy impact + +Yjs updates and awareness metadata are untrusted tenant data and can contain sensitive content or presence information. They are never treated as authorization, tenant admission, or durable-write proof. Provider credentials, room keys, retention, encryption, logging, and awareness-disclosure policy remain host-owned and must not be embedded in document or editor configuration intended for generic diagnostics. + +## Compatibility and migration + +The collaboration entrypoint stays optional and must not make Yjs/provider dependencies mandatory for standalone non-collaborative consumers. Hosts may migrate providers without changing Inkspan document semantics when they preserve the supplied Yjs-compatible contract. Any future Inkspan-owned transport/provider requires a breaking versioned contract, threat review, migration/rollback plan, and preserved standalone mode. + +## Verification + +Use provider-neutral integration tests with supplied Yjs state, lifecycle/remount tests, awareness non-authority/privacy contracts, package-dependency checks, and architecture documentation tests. No test should require a production provider credential or network connection to prove the adapter contract. + +## Rollback or supersession + +Rollback detaches the collaboration adapter while preserving local document operation and the host-owned Yjs state/provider. Supersession requires an explicit versioned collaboration contract with equivalent host authority separation, migration/rollback, privacy, and operability evidence. diff --git a/docs/adr/0008-deterministic-office-rendering.md b/docs/adr/0008-deterministic-office-rendering.md new file mode 100644 index 00000000..2ecfc99d --- /dev/null +++ b/docs/adr/0008-deterministic-office-rendering.md @@ -0,0 +1,41 @@ +# ADR 0008: Deterministic Office rendering boundary + +Status: Proposed + +## Context + +Enterprise buyers need DOCX, XLSX, and PPTX output that is reproducible and testable without model inference, Desktop Office automation, macros, or hidden network access. Office formats also introduce formula injection, XML/package validity, resource exhaustion, and file-publication hazards. + +## Alternatives considered + +- Automate Desktop Office/LibreOffice during production rendering. Rejected because it adds desktop-runtime, macro, platform, and hidden-network authority and weakens deterministic packaging. +- Let an LLM emit Office XML/packages directly. Rejected because output structure and safety would become model-dependent and difficult to bound or reproduce. +- Render a strict versioned JSON-compatible request through bounded local libraries and explicit publication semantics. Selected because it keeps supported fidelity testable and deterministic. + +## Decision + +The Office renderer consumes a strict versioned JSON-compatible contract and produces deterministic DOCX/XLSX/PPTX artifacts locally. It is network-free, macro-free, model-free, and Desktop-Office-free. Validate XML 1.0 content, resource/container/depth limits, worksheet names, freeze panes, supported structures, and spreadsheet formula-injection boundaries before successful publication. Output publication is race-safe with explicit overwrite semantics. + +## Consequences + +Rendering remains independently deployable and auditable. Fidelity claims are limited to tested supported constructs rather than broad Office compatibility claims. Hosts retain storage, authorization, content classification, retention, and downstream viewer policy. + +## Failure and recovery + +Invalid or unsupported input fails closed with bounded diagnostics. Partial or failed output is not successful evidence and must not replace unrelated files. A caller requesting overwrite does so explicitly. Recovery rebuilds from the exact validated source rather than editing a partially generated package. + +## Security and privacy impact + +The renderer does not fetch external resources, execute macros, invoke a model, or require Desktop Office. Spreadsheet strings are neutralized according to the supported literal-text contract so untrusted content cannot silently become a formula. Bounded validation reduces XML/package and resource-exhaustion risk. Callers still own authorization, storage location, document classification, retention, distribution, and downstream-viewer policy. + +## Compatibility and migration + +Supported DOCX/XLSX/PPTX constructs and the JSON request schema are explicit compatibility surfaces. New structures require schema/version updates and real document-fidelity fixtures; unsupported constructs must not be silently advertised as lossless. Python runtime/package support must remain aligned across metadata, CI, README, wheel evidence, and release guidance. Rollback restores the last verified renderer contract and rebuilds artifacts from source. + +## Verification + +Run supported Python-version tests, exact statement/branch coverage, complete public docstrings, realistic DOCX/XLSX/PPTX package inspection, formula-prefix regressions, XML-invalid character cases, cyclic/depth/container limits, worksheet/freeze-pane cases, publication-race tests, wheel/package inspection, license checks, and deterministic digest/evidence checks where applicable. + +## Rollback or supersession + +Rollback restores the last verified renderer contract and rebuilds artifacts without mutating host files outside the explicit target. Supersession requires a versioned input/output contract, migration/fidelity analysis, equivalent no-network/no-macro guarantees, and fresh package/release evidence. diff --git a/docs/adr/0009-naruon-modular-composition.md b/docs/adr/0009-naruon-modular-composition.md new file mode 100644 index 00000000..8ccb80f5 --- /dev/null +++ b/docs/adr/0009-naruon-modular-composition.md @@ -0,0 +1,41 @@ +# ADR 0009: Naruon modular composition without product coupling + +Status: Proposed + +## Context + +CWL hosts need to compose Inkspan through naruon `compose` / `ui.panel` surfaces while standalone adopters must not acquire a naruon or contextual-orchestrator dependency. SSR, autosave, collaboration, model use, and durable storage also have distinct authority boundaries that must not collapse inside a panel component. + +## Alternatives considered + +- Make naruon a required Inkspan runtime dependency. Rejected because standalone consumers would inherit host-specific transport, tenancy, and release coupling. +- Let Inkspan call contextual-orchestrator, host APIs, persistence, or provider services directly. Rejected because host credentials and authorization boundaries would move into the editor package. +- Keep integration additive through a narrow host-owned client/panel boundary with serializable non-secret configuration. Selected because Inkspan remains independently usable while CWL hosts share composition conventions. + +## Decision + +Naruon integration is additive and host-owned. A naruon host mounts Inkspan through a narrow client boundary, passes only serializable non-secret configuration, owns authenticated API calls and strong durable validators, owns Yjs provider lifecycle, and optionally routes model assistance through contextual-orchestrator under host policy. Inkspan retains deterministic editor/conversion/evidence authority and does not import host control-plane authority. + +## Consequences + +Inkspan remains independently usable and testable, while CWL products can share composition conventions. Cross-document remount identity, accessible conflict/recovery UX, and server-selected validators are explicit host responsibilities. Shared platform defects are fixed in their owning repository rather than duplicated in Inkspan. + +## Failure and recovery + +A host must issue a fresh editing-context identity when switching authorized documents so local queue/editor state cannot bleed across documents. Provider or model outages are host-degraded modes. A central `.github`, naruon, or contextual-orchestrator defect is treated as a read-only dependency; Inkspan continues independent work rather than weakening local safety gates. + +## Security and privacy impact + +Only non-secret serializable configuration crosses the generic composition boundary. Authentication tokens, tenant authority, durable validators, provider credentials, model credentials, retention policy, and durable audit remain host-owned. Cross-document remount identity prevents local editor/autosave state from being reused as implicit authorization for another host document. + +## Compatibility and migration + +Standalone exports remain usable without naruon or contextual-orchestrator. Host-specific composition evolves through versioned/narrow adapters rather than changing Inkspan's canonical document semantics. A host migration may replace API, provider, or model infrastructure while preserving the documented capability contract; rollback detaches the host adapter without rewriting Inkspan documents. + +## Verification + +Use `ARCHITECTURE.md`, `docs/naruon-compose-ui-panel.md`, architecture documentation tests, SSR/client-boundary tests, autosave validator/recovery tests, host-provider lifecycle tests, and packed standalone consumers proving no required naruon/contextual-orchestrator dependency. + +## Rollback or supersession + +Rollback detaches the naruon adapter/guide while preserving standalone Inkspan APIs and host data. Supersession requires an explicit versioned host composition contract, compatibility/migration evidence, and preserved standalone operation. diff --git a/docs/adr/0010-release-evidence-authority.md b/docs/adr/0010-release-evidence-authority.md new file mode 100644 index 00000000..96469aaa --- /dev/null +++ b/docs/adr/0010-release-evidence-authority.md @@ -0,0 +1,41 @@ +# ADR 0010: Release evidence authority + +Status: Proposed + +## Context + +Inkspan releases combine JavaScript/TypeScript packages, Office Python artifacts, checksums, SBOM/provenance, exact-head CI/security evidence, and repository review/protection state. Stale draft assets or predecessor-head checks can look healthy while referring to different source. + +## Alternatives considered + +- Publish best-effort artifacts whenever local builds appear green. Rejected because local or predecessor evidence can refer to a different source generation and does not prove remote artifact integrity or repository-policy acceptance. +- Reuse or repair mutable draft-release assets opportunistically. Rejected because stale/unexpected assets and digest drift can be mistaken for the intended exact-source release. +- Bind publication to one exact integrated protected head and fail closed on evidence or inventory ambiguity. Selected because source, review, provenance, and artifact authority remain reconstructable for operators and buyers. + +## Decision + +Release authority exists only for one exact integrated protected head. The release process verifies the expected local and remote artifact inventory and digests, package-consumer evidence, supported runtime matrix, applicable security/coverage/accessibility gates, SBOM/provenance, unresolved review findings, formal review requirements, and repository protection. Stale, unexpected, incomplete, or digest-mismatched draft assets fail closed and are not silently deleted or reused. Comments, model prose, status-only signals, predecessor evidence, or author assertions are not substitutes for formal approval where policy requires it. + +## Consequences + +Publication is slower than a best-effort upload but produces acquisition-reviewable evidence tied to exact source. Re-running after a head change is intentional. Historical releases and failed drafts remain auditable instead of being rewritten to produce a cleaner story. + +## Failure and recovery + +If artifact inventory, digest, provenance, review, or exact-head evidence is ambiguous, stop publication. Repair the source or release workflow, regenerate evidence from the exact head, and resume only through a contract that proves the existing remote draft is exactly expected. A bad published release is corrected through a new verified release or supported withdrawal/yank process. + +## Security and privacy impact + +Release credentials remain least-privilege workflow authority and are not product runtime credentials. Artifact integrity, provenance, and exact-source binding reduce supply-chain substitution risk. Release logs, SBOM/provenance records, and packaged artifacts must not accidentally include tenant documents, prompts/model outputs, host credentials, private validators, or unrelated secrets. Review or approval authority is never synthesized from a bot comment, author identity, or status-only signal. + +## Compatibility and migration + +A release contract binds one package/version and artifact inventory to one exact protected source generation. Evidence from an older head, merge result, or draft cannot transfer after source or expected assets change. A future release service or artifact layout requires explicit compatibility and migration rules for package consumers, provenance, draft-state recovery, and rollback while preserving historical immutable evidence rather than rewriting it. + +## Verification + +Use release-workflow regressions, expected-asset/digest tests, package and wheel consumers, reproducibility checks, security/coverage gates, SBOM/provenance/attestation checks where configured, formal review inspection, and post-publication artifact/checksum smoke verification. + +## Rollback or supersession + +Rollback never falsifies or deletes historical evidence to imply the failed publication did not occur. Supersession requires a versioned release contract with at least equivalent exact-source binding, artifact integrity, provenance, review, and recovery guarantees. diff --git a/docs/adr/0011-deterministic-vs-model-assisted-authoring.md b/docs/adr/0011-deterministic-vs-model-assisted-authoring.md new file mode 100644 index 00000000..34cfb1b4 --- /dev/null +++ b/docs/adr/0011-deterministic-vs-model-assisted-authoring.md @@ -0,0 +1,43 @@ +# ADR 0011: Deterministic conversion versus model-assisted authoring + +Status: Proposed + +## Context + +Inkspan supports deterministic editing/conversion surfaces and may be embedded in products that offer model-assisted writing. A generated proposal is probabilistic, provider-dependent content; deterministic document validation, conversion, revision, and persistence evidence must not inherit model authority or availability. + +## Alternatives considered + +- Let model output directly mutate or persist canonical documents. Rejected because provider behavior would become document authority and could bypass deterministic validation, host authorization, and user intent. +- Put model invocation inside deterministic conversion APIs. Rejected because conversion would gain network, credential, availability, privacy, and reproducibility dependencies. +- Keep model assistance as a host-authorized proposal path that rejoins Inkspan only through ordinary deterministic validation and explicit acceptance. Selected because it preserves offline-capable core behavior and a clear trust boundary. + +## Decision + +Deterministic editing, import/export, canonical envelope handling, revision evidence, autosave coordination, and Office rendering remain authoritative without an LLM or network dependency. Model-backed authoring is an optional host-owned capability. Model output is untrusted proposed content until an authorized host/user accepts it and the resulting content passes the same deterministic Inkspan parsing, sanitization, schema, conversion, and persistence boundaries as manually authored content. + +No model result authorizes a save, selects a tenant, advances a durable validator, bypasses clipboard or Office safety, changes a release gate, or becomes formal review evidence merely because the provider returned successfully. + +## Consequences + +Inkspan remains usable when model infrastructure is absent or degraded. Hosts can select providers and product UX independently. Model-assisted workflows incur an explicit proposal/diff/acceptance step and must maintain their own prompt/data governance and audit evidence. + +## Failure and recovery + +Provider timeout, quota exhaustion, malformed output, policy rejection, or unavailable credentials disables only the optional proposal path. Existing deterministic authoring and conversion remain available according to host policy. Invalid proposed content is rejected or sanitized through the normal deterministic boundary; it is never promoted to success by a fallback that skips validation. + +## Security and privacy impact + +The host owns model credentials, provider selection, external-data-use approval, prompt construction, redaction/minimization, retention, tenant authorization, and human approval. Inkspan must not rematerialize raw provider secrets after model execution or place prompts, model outputs, tenant data, or credentials into generic diagnostics. Model output is treated as attacker-controlled/untrusted content at every deterministic ingress. + +## Compatibility and migration + +Deterministic package APIs cannot acquire a required provider/network dependency in a compatible minor change. A future model-assistance SDK must remain optional and versioned separately from deterministic document/conversion contracts. Hosts may migrate model providers without changing canonical Inkspan document semantics. Rollback disables model assistance without document migration. + +## Verification + +Package-consumer tests prove deterministic subpaths work without model SDKs or credentials. Editor/conversion security tests exercise model-proposed content through the same sanitization/schema boundaries as other untrusted input. Host integration evidence must prove authorization and explicit acceptance before durable persistence. Outage tests verify deterministic authoring remains functional when the model path is unavailable. + +## Rollback or supersession + +Rollback removes or disables the optional proposal integration while preserving canonical documents and deterministic APIs. Supersession requires an explicit versioned trust-boundary ADR showing why any model-derived authority is necessary, with privacy, security, offline/degraded-mode, compatibility, migration, and rollback evidence. diff --git a/docs/adr/0012-spreadsheet-formula-injection.md b/docs/adr/0012-spreadsheet-formula-injection.md new file mode 100644 index 00000000..5f6769c5 --- /dev/null +++ b/docs/adr/0012-spreadsheet-formula-injection.md @@ -0,0 +1,43 @@ +# ADR 0012: Spreadsheet formula-injection handling + +Status: Proposed + +## Context + +Inkspan Office accepts AI-authored or otherwise untrusted JSON-like spreadsheet values and produces XLSX files. Spreadsheet applications can interpret text beginning with formula-significant prefixes such as `=`, `+`, `-`, or `@` as executable formulas or formula-like expressions if a renderer writes them with formula semantics. The 0.1 renderer contract does not include formula generation. + +## Alternatives considered + +- Preserve formula-looking strings as formulas. Rejected because untrusted text could gain executable spreadsheet semantics and formula generation is outside the current contract. +- Prefix every suspicious string with a visible apostrophe. Rejected as the primary contract because it mutates the user's textual value and can create avoidable fidelity differences. +- Write supported string values as literal text cells and verify that formula-significant prefixes remain data, while rejecting values that cannot be represented safely. Selected because it preserves user text without granting formula authority. + +## Decision + +String inputs are rendered as literal text by default, including values whose first non-whitespace character is `=`, `+`, `-`, or `@`. Inkspan Office does not infer or generate spreadsheet formulas in the current contract. Numeric and textual limits are validated before publication, and callers must supply identifiers or high-precision digit sequences as strings when exact textual representation matters. + +A future formula feature must use a distinct explicit schema field or versioned contract; it may not reinterpret existing string cells as formulas. + +## Consequences + +AI-authored and pasted content cannot silently become a formula merely because its text begins with a formula-significant prefix. Consumers that intentionally need formulas must wait for or adopt a future explicit formula contract rather than relying on implicit spreadsheet-library behavior. + +## Failure and recovery + +If a value cannot be represented under the safe literal/numeric contract, validation fails before successful artifact publication. The renderer must not fall back from rejected text to formula semantics. Recovery is to provide a supported literal representation or adopt a future explicit formula schema after its security review. + +## Security and privacy impact + +This boundary removes a common path from untrusted document content to spreadsheet formula execution, external-link behavior, or application-specific formula side effects. It does not make downstream spreadsheet viewers universally safe; hosts remain responsible for distribution policy, viewer hardening, authorization, and document classification. Formula-significant source strings are content and should not be copied into generic security logs solely because they were neutralized. + +## Compatibility and migration + +Existing string cells retain literal-text semantics across compatible releases. Introducing intentional formulas is a breaking semantic expansion unless represented by a new explicit versioned shape that coexists without changing old strings. Migration must distinguish literal historical values from intentional formulas and provide rollback to the literal-only renderer. + +## Verification + +Maintain regression fixtures for leading `=`, `+`, `-`, and `@`, including whitespace-prefixed variants and ordinary literal strings. Inspect generated XLSX cell types/values with the supported library and realistic package round-trips. Keep numeric-precision, string-length, worksheet-limit, package, coverage, and deterministic-render tests green on every supported Python runtime. + +## Rollback or supersession + +Rollback restores the last verified literal-only spreadsheet contract and rebuilds artifacts from exact source. Supersession requires an explicit formula schema, threat analysis, injection regressions, compatibility/migration rules, downstream-viewer considerations, and a safe rollback that never reinterprets historical literal strings. diff --git a/docs/adr/0013-atomic-file-publication.md b/docs/adr/0013-atomic-file-publication.md new file mode 100644 index 00000000..11e58141 --- /dev/null +++ b/docs/adr/0013-atomic-file-publication.md @@ -0,0 +1,43 @@ +# ADR 0013: Atomic file publication and explicit overwrite semantics + +Status: Proposed + +## Context + +Deterministic Office rendering produces binary artifacts that may be published to caller-selected filesystem paths. A naïve check-then-write sequence can race another writer, expose partial files, or unexpectedly replace an existing artifact. Publication semantics therefore belong to the renderer's correctness and security contract, not only to operational convenience. + +## Alternatives considered + +- Write directly to the destination path. Rejected because consumers can observe partially written artifacts and a failed render can corrupt the target. +- Check whether the destination exists and then rename a temporary file into place. Rejected for non-overwrite mode because the check and publication are separate operations and can race another writer. +- Publish a complete same-directory temporary artifact through race-safe non-overwrite semantics, with a separate explicit overwrite mode using atomic replacement. Selected because partial output never becomes success and overwrite intent is unambiguous. + +## Decision + +Inkspan Office builds and validates the complete artifact before publication. Publication uses a securely created same-directory temporary file. The default non-overwrite path must atomically fail if the target already exists rather than replacing it after a check-then-act race. Replacement of an existing target is permitted only when the caller explicitly selects overwrite behavior, and that replacement must be atomic for the supported filesystem contract. + +Temporary/partial output is never returned as a successful `conversion_artifact`. Cleanup is bounded to the renderer's own temporary artifact and must not delete or rewrite unrelated caller files. + +## Consequences + +Callers get deterministic conflict behavior and never need to infer whether an existing artifact was replaced. Same-directory temporary publication has filesystem constraints and may cost an additional write/rename step, but it makes the success boundary reviewable and protects consumers from partial output. + +## Failure and recovery + +Validation, build, temporary-write, publication, destination-conflict, or replacement failure returns a bounded failure and leaves no successful-artifact claim. In default mode an existing destination is preserved. Recovery is to choose a new output path, remove/rename the target under caller authority, or explicitly request overwrite. Retrying must rebuild or reuse only a fully validated candidate under the documented contract. + +## Security and privacy impact + +Race-safe non-overwrite behavior reduces unintended clobbering and symlink/check-then-act style publication hazards within the supported path contract. Secure temporary files reduce exposure of predictable intermediate names. Inkspan does not broaden filesystem authority, traverse unrelated storage for cleanup, or infer that overwrite is safe from content identity. Hosts remain responsible for directory permissions, tenant path isolation, quotas, retention, encryption at rest, and downstream distribution. + +## Compatibility and migration + +Default non-overwrite behavior is a stable safety contract. Changing default replacement semantics would be breaking. Existing callers that intentionally replace files must continue to opt in explicitly. Any future object-store or remote-publication adapter requires a separate versioned compare/create/replace contract with equivalent conflict, partial-success, and rollback semantics rather than pretending filesystem atomicity applies remotely. + +## Verification + +Maintain tests for existing-target conflicts, explicit overwrite, simultaneous publication attempts, temporary-write/build failure, cleanup of only owned temporary artifacts, extension mismatch, and successful round-trip package integrity. Use concurrency regressions that prove the default path has no check-then-replace window and that a consumer never observes a partially published success artifact. + +## Rollback or supersession + +Rollback restores the last verified publication implementation without weakening default non-overwrite behavior. Supersession requires an explicit storage-specific atomicity model, conflict semantics, security review, concurrency tests, migration guidance, and a rollback that preserves already published caller artifacts. diff --git a/docs/adr/0014-local-assets-font-licensing.md b/docs/adr/0014-local-assets-font-licensing.md new file mode 100644 index 00000000..a08c6047 --- /dev/null +++ b/docs/adr/0014-local-assets-font-licensing.md @@ -0,0 +1,45 @@ +# ADR 0014: Local assets and font-licensing boundary + +Status: Proposed + +## Context + +Inkspan is intended to remain usable in offline and air-gapped environments while rendering Korean, English, Japanese, Simplified Chinese, Traditional Chinese, Vietnamese, and other supported text predictably. Fetching web fonts at runtime would introduce availability, privacy, CSP/egress, and supply-chain dependencies. Bundling fonts also creates a redistribution/licensing obligation that must remain visible to package consumers and acquisition reviewers. + +The current package bundles Noto Sans-family WOFF2 subsets generated from the Google Fonts distribution. `src/fonts/OFL.txt` carries the SIL Open Font License 1.1 text and `src/fonts/NOTICE` records the bundled families, attribution, source, subset process, and license boundary. + +## Alternatives considered + +- Fetch fonts from Google Fonts or another CDN at runtime. Rejected because offline/air-gapped operation would fail and document rendering would gain external network, privacy, CSP, and provider-availability dependencies. +- Depend only on host/system fonts. Rejected as the default product contract because cross-platform glyph coverage, CJK/Vietnamese rendering, layout evidence, and acquisition reproducibility become host-specific. +- Bundle permissively redistributable local subsets with complete license/notice material and explicit package exports. Selected because rendering remains offline-capable while third-party asset obligations are reviewable. + +## Decision + +Inkspan bundles the supported Noto Sans-family font subsets as local package assets and exposes local CSS/font package paths. Runtime authoring/rendering must not require a remote font fetch. Bundled font software remains under SIL OFL-1.1; Inkspan application source remains under its own repository license. The OFL license text and attribution/NOTICE must ship with the redistributed font assets. + +The product does not rename, imply ownership of, or silently relicense the Noto font software. Any future modified font must respect Reserved Font Name and other OFL conditions before distribution. + +## Consequences + +Offline and closed-network consumers get stable multilingual font availability and do not leak document access through third-party font requests. Package size is larger, and font inventory/licensing becomes part of release verification. Host applications may still choose their own typography, but the documented Inkspan offline asset path remains available without network authority. + +## Failure and recovery + +If a required bundled asset, license text, attribution record, CSS mapping, or package export is missing or inconsistent, package/release verification fails rather than silently falling back to a remote URL. If a font license or provenance cannot be established, remove the affected asset from the release until a reviewed replacement or corrected provenance record exists. Runtime must not auto-download a substitute. + +## Security and privacy impact + +Local fonts eliminate runtime font-CDN requests that could disclose client IP, application access timing, origin/referrer information, or violate CSP/egress policy. Shipping only reviewed assets reduces remote supply-chain substitution risk. Font files remain untrusted binary assets for downstream browser/font parsers, so package provenance and bounded asset inventory matter even though Inkspan does not execute them as code. + +## Compatibility and migration + +Package paths such as the local font CSS/assets are consumer-facing compatibility surfaces. Removing a bundled family, changing unicode-range coverage, renaming a family, changing weights, or replacing licenses requires release notes and rendering/packaging compatibility evidence. Hosts can migrate to their own fonts without changing canonical document semantics because font choice is presentation, not document authority. Rollback restores the last verified local asset inventory and its matching license/NOTICE files. + +## Verification + +Package verification must confirm the expected font files, CSS exports, `src/fonts/OFL.txt`, and `src/fonts/NOTICE` are included as intended. Tests or release evidence should verify no canonical Inkspan stylesheet requires a remote font URL, multilingual fixtures render with the declared local families/weights, and asset/license inventory matches package metadata and documentation. Supply-chain review checks the recorded upstream provenance before changing bundled font bytes. + +## Rollback or supersession + +Rollback restores the previous verified font/CSS inventory together with its exact license and NOTICE material. Supersession requires an explicit asset/provenance/license decision with offline/degraded-mode behavior, package-size and rendering compatibility analysis, supply-chain review, migration guidance, and a no-network rollback path. diff --git a/docs/adr/0015-envelope-schema-migration-routing.md b/docs/adr/0015-envelope-schema-migration-routing.md new file mode 100644 index 00000000..5995c1e4 --- /dev/null +++ b/docs/adr/0015-envelope-schema-migration-routing.md @@ -0,0 +1,68 @@ +# ADR 0015: Envelope schema identity and host-owned migration routing + +Status: Proposed + +## Context + +Inkspan's protected implementation uses a strict versioned document envelope so canonical document bytes, revisions, restore behavior, and conversion contracts are not silently reinterpreted. The current parser is intentionally current-schema-only. That protects document semantics, but it also means a host that receives a structurally valid legacy or future envelope cannot use the strict parser merely to learn which migration route should handle it. + +Issue #74 tracks a bounded identity-only inspection surface for that gap. The architectural question is whether Inkspan should become a migration engine, relax the current parser, require every host to reimplement hostile-input parsing, or expose only enough validated schema identity to let the host select its own migration. + +## Alternatives considered + +- Make the current envelope parser permissive for unknown versions. Rejected because identifying a version is not the same as validating that version's document semantics; permissive parsing would weaken the source/revision authority in ADR 0002. +- Require each host to parse untrusted envelope JSON independently before calling Inkspan. Rejected because duplicate-name, strict UTF-8, resource-limit, accessor, descriptor, and redacted-error boundaries would drift across hosts exactly where version routing must be trustworthy. +- Move schema registry, migration execution, persistence transactions, rollback, and audit into Inkspan. Rejected because durable migration is host-owned under ADR 0001 and would couple the standalone editor to application persistence and tenancy. +- Provide a bounded identity-only inspection API while retaining strict current-schema parsing. Selected because it reuses Inkspan's deterministic hostile-input boundary without granting migration or persistence authority. + +## Decision + +Inkspan may expose a framework-independent identity inspector that returns only a frozen `schemaId` and positive safe-integer `schemaVersion` after bounded structural validation. The inspector is a dispatcher aid, not a document parser or migration engine. + +The identity path must share the envelope's existing byte, string, depth, value-count, JSON, duplicate-name, strict UTF-8, BOM, plain-object, descriptor, accessor, proxy/reflection, and redacted-error boundaries where applicable. It may require the complete envelope and the presence of `documentJson` while deliberately avoiding current TipTap/ProseMirror semantic validation of that member before returning routing metadata. It may ignore future fields only when doing so requires no accessor execution and does not weaken complete-input validation. + +`parseDocumentEnvelope()` remains strict and current-schema-only. After identity inspection, the host selects and executes any migration, authorizes the operation, chooses schema-registry policy, performs durable persistence, records audit evidence, applies tenant isolation and retention, and decides rollback. A migrated result must re-enter the ordinary strict current-schema validation path before it can become canonical Inkspan document state. + +## Consequences + +A host can route old or future envelopes without duplicating a second untrusted JSON parser, while Inkspan avoids accepting unknown document semantics. The public API gains one additional versioned evidence/value surface, so package consumers and acquisition reviewers can distinguish schema identification from migration success. + +The identity result is intentionally too small to support editing, authorization, durable persistence, or provenance claims. A host that needs richer legacy metadata must obtain it from the version-specific migration component rather than widening this generic inspector. + +## Failure and recovery + +Malformed JSON or UTF-8, duplicate names, missing identity/body members, invalid scalar types, unsafe version numbers, accessors, hostile proxies, decorated roots, resource-limit violations, or reflection failures fail closed with stable redacted errors. No partial identity is returned. + +An unsupported identity is not itself an error if the bounded identity contract is valid; it is a routing result. If no host migration is registered, the host keeps the original source unchanged and reports an unsupported-version outcome. Migration failure does not alter the original envelope and does not permit the current parser to accept the unsupported representation. + +## Security and privacy impact + +The inspector performs no network, file-system, environment, credential, database, scheduler, model, or collaboration-provider operation. It does not return `documentJson`, document text, links, inline image data, source-controlled property names, tenant identifiers, actor data, credentials, transport state, signatures, or durable-write claims. + +Schema identity can still be application metadata, so hosts may classify it as tenant-confidential. Public errors and generic telemetry must not echo arbitrary schema strings or private exception causes. The inspector does not authorize migration and does not prove that a supplied envelope came from a trusted actor. + +## Compatibility and migration + +Existing current-schema create/parse/encode/revision/restore behavior remains unchanged. The identity inspector is additive and must be available from a framework-independent package surface without pulling in React, TipTap UI, ProseMirror view, Yjs, DOM globals, network clients, or model SDKs. + +Any future change to envelope identity fields, canonicalization, or digest meaning requires an explicit new versioned contract and corresponding ADR update or supersession. Migration implementations remain host-owned and version-specific; this ADR does not define a universal transformation between arbitrary schema generations. + +## Verification + +Issue #74 defines the test-first implementation acceptance. Verification must include legacy/current/future identity, JSON text and strict UTF-8 bytes, duplicate names, malformed input, hostile JavaScript descriptors/proxies, resource limits, recursive absence of document-bearing output, deterministic repeated inspection, packed ESM/CommonJS/strict-TypeScript consumers, and proof that the strict current parser still rejects unsupported schemas. + +Repository-wide exact owned production statement/branch/function/line coverage and public docstring gates remain unchanged. Exact-head CI, security, package, provenance, review, and branch-protection evidence are required before the implementation can become protected-main authority. + +## Rollback or supersession + +Before protected integration, rollback removes the proposed inspector and leaves the strict parser plus host-owned migration guidance unchanged. After integration, removal or semantic broadening is a compatibility change requiring package-version review and migration guidance. + +Supersession is acceptable only if a later design preserves or strengthens the separation between bounded schema identification, strict current-schema validation, and host-owned migration/persistence authority. + +## References + +Bray, T. (Ed.). (2015). *The I-JSON Message Format* (RFC 7493). RFC Editor. https://doi.org/10.17487/RFC7493 + +Bray, T. (Ed.). (2017). *The JavaScript Object Notation (JSON) Data Interchange Format* (RFC 8259; STD 90). RFC Editor. https://doi.org/10.17487/RFC8259 + +Rundgren, A., Jordan, B., & Erdtman, S. (2020). *JSON Canonicalization Scheme (JCS)* (RFC 8785). RFC Editor. https://doi.org/10.17487/RFC8785 diff --git a/docs/adr/0016-cross-engine-browser-assurance.md b/docs/adr/0016-cross-engine-browser-assurance.md new file mode 100644 index 00000000..aaac3cfa --- /dev/null +++ b/docs/adr/0016-cross-engine-browser-assurance.md @@ -0,0 +1,72 @@ +# ADR 0016: Cross-engine browser-semantic release assurance + +Status: Proposed + +## Context + +Inkspan's safe rich-clipboard boundary depends on browser HTML fragment parsing, DOM construction, serialization, hidden-content interpretation, and TipTap/ProseMirror integration. Unit and jsdom evidence is valuable but cannot prove that Chromium, Firefox, and WebKit expose identical security-relevant behavior. Earlier development already found a CSSOM mismatch around proprietary Office style handling, demonstrating that one simulated DOM is not a sufficient publication boundary. + +PR #65 owns the sanitizer implementation. Issue #66 separately owns the browser-realistic differential release gate after PR #65 reaches protected `main`. The architectural decision is how browser-semantic differences are admitted into a release without turning engine disagreement into either an unbounded compatibility promise or an excuse to normalize away a security defect. + +## Alternatives considered + +- Treat jsdom/unit success as browser conformance. Rejected because simulated DOM behavior is not authoritative for real engine fragment parsing or serialization. +- Test only Chromium. Rejected because Inkspan is a browser-facing package and security semantics can diverge in Firefox or WebKit even when Chromium is green. +- Require byte-identical output from all engines without exception. Rejected because standards-permitted serialization differences can exist and unconditional normalization can hide a real semantic discrepancy. +- Run one dependency-locked adversarial corpus across Chromium, Firefox, and WebKit, require semantic parity for security decisions, and admit only narrowly reviewed standards-grounded differences. Selected because it provides buyer-reviewable evidence without claiming universal engine identity. + +## Decision + +Before a release line can claim the rich-clipboard boundary as supported, the same committed adversarial corpus must execute through Inkspan's public sanitizer and supported paste integration in named Playwright `chromium`, `firefox`, and `webkit` projects on one exact source head. + +The suite is hermetic and network-free, depends on immutable package/workflow pins, and does not require operating-system clipboard permissions. It covers strict element/attribute allowlisting; unsafe-link handling; scripts, resources, embeds, forms, metadata, media, SVG/MathML and images; interactive/obsolete fallback structures; hidden and Office-specific content; malformed fragment/table/list/formatting/namespace cases; hostile DOM capabilities; bounded bytes/nodes/depth; stable redacted failures; and representative performance alarms. + +Security-relevant semantic decisions must agree across required engines. A serialization difference may be allowlisted only when a focused fixture, authoritative standards basis, threat analysis, exact affected engine/version evidence, expected canonical interpretation, and rollback note show that the difference does not weaken the trust boundary. An allowlist is code-reviewed evidence, not a generic normalizer. + +The gate fails closed when a required browser is absent, skipped, cancelled, unsuccessful, or cannot produce the required evidence. Queued or pending execution is not success. Exact-head evidence records the package lock, Playwright version, browser revisions, operating system, corpus version, and source SHA without exposing private clipboard data or local paths. + +## Consequences + +Rich clipboard publication costs more CI time and browser artifacts, but acquisition reviewers receive direct evidence for the parser/serializer boundary most likely to vary by browser. Engine upgrades become observable compatibility events rather than silent semantic changes. + +The gate does not make Inkspan responsible for branded browser enterprise policy, extensions, host CSP, downstream rendering, transport, authorization, tenancy, persistence, credentials, retention, migration, or model policy. Those remain separate host/runtime controls. + +## Failure and recovery + +A newly divergent fixture blocks the release lane. The first response is to classify whether the divergence changes the security/semantic result, is a standards-permitted serialization-only difference, or exposes a test/environment defect. Repair the sanitizer or integration test-first when the boundary is unsafe. Add a focused reviewed allowance only for a proven safe difference. + +Do not delete a failing engine, weaken the corpus, convert a required project to optional, hide the failure behind retries, or reuse predecessor-head browser evidence. If browser provisioning itself is unavailable, the release remains blocked while unrelated Inkspan work continues. + +## Security and privacy impact + +The differential corpus contains committed synthetic fixtures only. Browser jobs receive no tenant document, production clipboard payload, provider credential, model credential, or persistence secret. Network access is disabled for the test scenario where practical, and logs/artifacts must not emit raw hostile input outside the public fixture set. + +The design reduces parser-confusion, hidden-content, active-resource, serialization, and regression risk. It does not replace host CSP, egress policy, authorization, tenant isolation, or application security testing. + +## Compatibility and migration + +Each accepted browser/Playwright revision is part of release evidence rather than a forever-supported browser guarantee. Upgrading Playwright or its browser revisions requires the complete corpus to rerun before the new evidence becomes authoritative. A future browser difference that is safe only behind a narrower supported construct must be reflected in the public compatibility contract rather than silently normalized. + +The gate is ordered behind the safe-clipboard implementation because there is no useful release assurance for a trust boundary that is not yet integrated. It does not change existing document-envelope or persistence migration semantics. + +## Verification + +Issue #66 defines the test-first implementation acceptance. Required proof includes RED evidence that the differential harness detects an intentionally introduced divergence or unsafe reconstruction, GREEN evidence after removing the fault, all three named browser projects, deterministic corpus/allowlist/evidence-generation tests, representative performance bounds, exact-head CI/security/package evidence, and a qualifying independent review before the release gate is accepted. + +The canonical documentation and test strategy must continue to state that jsdom-only evidence is not real-engine conformance and that differences are never normalized merely to make engines agree. + +## Rollback or supersession + +Before protected integration, rollback removes the proposed browser gate while leaving the rich-clipboard release line explicitly unaccepted for publication. After integration, disabling a required engine or loosening difference admission is a security/release-policy change requiring a superseding ADR and new threat analysis. + +Supersession is acceptable only if a later harness provides equivalent or stronger real-engine coverage and preserves exact-head, fail-closed, synthetic-fixture, and reviewed-difference boundaries. + +## References + +Microsoft. (n.d.-a). *Browsers*. Playwright documentation. Retrieved August 9, 2026, from https://playwright.dev/docs/browsers + +Microsoft. (n.d.-b). *Projects*. Playwright documentation. Retrieved August 9, 2026, from https://playwright.dev/docs/test-projects + +Web Hypertext Application Technology Working Group. (2026). *HTML Standard: Parsing HTML documents* (Living Standard). Retrieved August 9, 2026, from https://html.spec.whatwg.org/multipage/parsing.html + +World Wide Web Consortium. (2026, June 24). *Clipboard API and events* (W3C Working Draft). https://www.w3.org/TR/2026/WD-clipboard-apis-20260624/ diff --git a/docs/adr/0017-security-disclosure-lifecycle.md b/docs/adr/0017-security-disclosure-lifecycle.md new file mode 100644 index 00000000..385f20d8 --- /dev/null +++ b/docs/adr/0017-security-disclosure-lifecycle.md @@ -0,0 +1,80 @@ +# ADR 0017: Security disclosure lifecycle and coordinated vulnerability handling + +Status: Proposed + +## Context + +Protected `main` now contains root `SECURITY.md` and its deterministic documentation contract from the independently reviewed security-disclosure line. Inkspan therefore has a shipped repository-native intake and coordinated-disclosure policy rather than an architecture target owned by a separate active PR. The canonical documentation graph must capture the durable decision without rewriting the security policy itself or confusing a protected-main process contract with certification, a bounty, a legal safe-harbor promise, or an operational response-time SLA. + +The policy must remain consistent with Inkspan's product boundary: Inkspan owns its editor, deterministic conversion, package, and release surfaces, while embedding hosts retain their own transport, authentication, authorization, tenancy, durable persistence, credentials, deployment, retention, collaboration-provider authority, and model-use policy. + +## Alternatives considered + +### Keep disclosure guidance only in feature doctoring or PR history + +Rejected. Security researchers and acquisition reviewers need a stable repository-root discovery path; PR bodies and conversation history are not durable product authority. + +### Duplicate the full `SECURITY.md` policy into architecture documents + +Rejected. Duplicated policy prose creates two security authorities that can drift. The root policy remains normative for reporting and supported release lines; this ADR records why that lifecycle and ownership boundary exist. + +### Promise fixed response/remediation SLAs or a bounty as part of the baseline + +Rejected. The repository does not have evidence or organizational authority to guarantee those commitments. Such promises require separately reviewed operational/legal authority and measurable support capacity. + +### Public issue intake for vulnerability details + +Rejected. Public issue content is unsuitable for undisclosed vulnerability details, proof-of-concept payloads, secrets, or customer data. Private GitHub Security Advisory reporting is preferred when available; the bounded public fallback may only request a private contact without vulnerability details. + +## Decision + +1. Root `SECURITY.md` is the canonical vulnerability-reporting and coordinated-disclosure policy for Inkspan. +2. Supported pre-1.0 release lines are derived from actual package manifests and kept under deterministic documentation tests rather than copied as unbound prose. +3. Reporters are directed to GitHub's private vulnerability-reporting / Security Advisory path when available. A public issue may only request a private reporting contact and must not contain vulnerability details, proof-of-concept payloads, secrets, or customer data. +4. Intake requests exact affected version, minimal synthetic reproduction, exploit prerequisites, and minimized evidence sufficient for reproduction. +5. Maintainer handling follows private intake -> exact-version reproduction -> root-cause regression -> bounded fix -> exact-head CI/security/coverage/package/provenance/review evidence -> advisory/CVE coordination when appropriate -> coordinated disclosure. +6. The policy does not claim a response-time SLA, bounty, legal safe harbor, certification, or complete framework conformance. +7. Inkspan's security policy covers Inkspan-owned product/package surfaces. Host-owned identity, transport, tenancy, persistence, deployment, retention, collaboration-provider, and model-policy incidents remain the embedding host's responsibility unless evidence shows an Inkspan defect contributes to the issue. +8. Final published standards remain normative where a newer revision is only draft; draft standards may be tracked as evidence inputs but do not silently replace the final baseline. + +## Consequences + +Security researchers have a stable private intake path and explicit evidence-minimization guidance. Acquisition reviewers can distinguish a documented disclosure process from unsupported certification or SLA claims. The canonical architecture remains single-source: `SECURITY.md` defines the operational reporting policy, this ADR defines the durable decision and boundaries, and doctoring records source/version evidence. + +The repository assumes ongoing maintenance responsibility for keeping supported release rows, GitHub reporting mechanics, standards references, and release evidence synchronized with actual package and repository behavior. + +## Failure and recovery + +If private vulnerability reporting becomes unavailable, the public fallback remains limited to requesting a private contact; sensitive details are not moved into a public issue as a workaround. If package support lines change, deterministic tests must fail until `SECURITY.md` is updated to match the manifests. If GitHub changes the private-reporting workflow, the policy must fail safe toward private contact rather than inventing a nonexistent control. + +If a security-policy change overstates organizational capability, certification, legal protection, or response guarantees, revert the unsupported claim and restore the last evidence-backed policy while a separately authorized decision is reviewed. Active vulnerability handling should not be blocked by documentation repair when a safe private channel exists. + +## Security and privacy impact + +The decision reduces accidental disclosure by preferring private intake and minimizing submitted evidence. Public issue fallback explicitly excludes vulnerability details, secrets, customer content, and proof-of-concept payloads. Reproduction should use synthetic/minimized data wherever possible, with exact affected versions and exploit prerequisites recorded separately from unnecessary tenant content. + +The policy itself does not grant authorization to test third-party or customer systems and does not expand maintainer access to host-owned data or credentials. + +## Compatibility and migration + +This is a repository-process contract and does not change runtime APIs, stored document schemas, package formats, database objects, host integrations, or deployment topology. Existing users continue to consume the same product packages. Future security-policy changes remain backward-compatible when possible; any new support commitment or reporting mechanism requires synchronized documentation tests and, when material, a superseding ADR. + +## Verification + +Protected `main` contains root `SECURITY.md`, deterministic tests binding its supported release rows to package manifests, and doctoring that records ISO/IEC 29147:2018, ISO/IEC 30111:2019, NIST SP 800-218 SSDF Version 1.1, the draft-status boundary for the later SSDF revision, and GitHub private-reporting/coordinated-disclosure guidance. The protected merge of the security-disclosure line is implementation evidence; this documentation branch must not downgrade it to an active-PR claim. + +Canonical documentation tests require this ADR, its index entry, and `docs/DOCUMENTATION_FITNESS.md` to classify the root security disclosure policy as `present_current` with `implemented_on_protected_main` maturity. + +## Rollback or supersession + +Rollback of this ADR does not remove protected `SECURITY.md`; the root policy remains authoritative until separately changed through normal protected review. A future ADR may supersede this decision if Inkspan adopts a materially different reporting platform, formal support SLA, bounty program, legal safe-harbor policy, or organization-wide security-response authority. Supersession must preserve a discoverable private vulnerability-reporting path during migration. + +## References + +International Organization for Standardization. (2018). *Information technology — Security techniques — Vulnerability disclosure* (ISO/IEC 29147:2018). + +International Organization for Standardization. (2019). *Information technology — Security techniques — Vulnerability handling processes* (ISO/IEC 30111:2019). + +Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software Development Framework (SSDF) Version 1.1: Recommendations for Mitigating the Risk of Software Vulnerabilities* (NIST SP 800-218). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-218 + +GitHub. (n.d.). *About repository security advisories*. GitHub Docs. diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 00000000..2f984d91 --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,49 @@ +# Inkspan Architecture Decision Records + +Status values: Proposed, Accepted, Superseded. + +This index records durable architectural decisions. Protected-main implementation is required before a feature-specific decision may be treated as shipped behavior. Detailed ADR files are authoritative for their decision; this index is authoritative for discovery and status. + +| ADR | Status | Decision | +|---|---|---| +| [0001](0001-product-host-authority.md) | Proposed | Product and host authority boundary | +| [0002](0002-document-revision-authority.md) | Proposed | Canonical document and revision authority | +| [0003](0003-safe-rich-clipboard.md) | Proposed | Safe rich clipboard boundary | +| [0004](0004-durable-validator-autosave.md) | Proposed | Strong durable validator and bounded autosave | +| [0005](0005-revision-scoped-review-evidence.md) | Proposed | Revision-scoped review evidence | +| [0006](0006-ssr-native-form-boundary.md) | Proposed | SSR/native form client-controlled boundary | +| [0007](0007-provider-neutral-collaboration.md) | Proposed | Provider-neutral collaboration and host ownership | +| [0008](0008-deterministic-office-rendering.md) | Proposed | Deterministic Office rendering boundary | +| [0009](0009-naruon-modular-composition.md) | Proposed | Naruon modular composition without product coupling | +| [0010](0010-release-evidence-authority.md) | Proposed | Release evidence authority | +| [0011](0011-deterministic-vs-model-assisted-authoring.md) | Proposed | Deterministic conversion versus model-assisted authoring | +| [0012](0012-spreadsheet-formula-injection.md) | Proposed | Spreadsheet formula-injection handling | +| [0013](0013-atomic-file-publication.md) | Proposed | Atomic file publication and explicit overwrite semantics | +| [0014](0014-local-assets-font-licensing.md) | Proposed | Local assets and font-licensing boundary | +| [0015](0015-envelope-schema-migration-routing.md) | Proposed | Envelope schema identity and host-owned migration routing | +| [0016](0016-cross-engine-browser-assurance.md) | Proposed | Cross-engine browser-semantic release assurance | +| [0017](0017-security-disclosure-lifecycle.md) | Proposed | Security disclosure lifecycle and coordinated vulnerability handling | + +## Decision discipline + +- **Proposed**: documented or implemented on an unmerged branch; not protected-main authority. +- **Accepted**: integrated into protected `main` with the applicable verification evidence. +- **Superseded**: retained for historical traceability but explicitly replaced by a later ADR. + +An ADR cannot promote an unmerged feature to shipped behavior. When a decision changes materially, add or supersede an ADR rather than silently rewriting history. A Proposed ADR may document a decision whose underlying implementation already exists on protected `main`; its own status remains Proposed until this ADR file is integrated, while implementation maturity is recorded separately in the canonical fitness/traceability records. + +## ADR quality requirements + +Every material ADR records explicit sections for: + +1. context and the problem boundary; +2. materially distinct alternatives considered; +3. the selected decision; +4. consequences and ownership trade-offs; +5. failure and recovery semantics; +6. security and privacy impact; +7. compatibility and migration behavior; +8. verification/acceptance evidence; and +9. rollback or explicit supersession conditions. + +Accessibility, operability, research/standards traceability, and release implications are included wherever the decision affects them. Canonical PRD, TRD, Architecture, contracts, UML, data/evidence model, threat model, test strategy, operability, and traceability documents must remain synchronized with Accepted decisions. diff --git a/src/canonicalDocumentationDecisionCoverage.test.ts b/src/canonicalDocumentationDecisionCoverage.test.ts new file mode 100644 index 00000000..27baa3c6 --- /dev/null +++ b/src/canonicalDocumentationDecisionCoverage.test.ts @@ -0,0 +1,108 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { describe, expect, it } from 'vitest'; + +/** Read one authoritative repository document as UTF-8 text. */ +function repositoryFile(path: string): string { + return readFileSync(resolve(process.cwd(), path), 'utf8'); +} + +const migrationAdr = + 'docs/adr/0015-envelope-schema-migration-routing.md'; +const browserAssuranceAdr = + 'docs/adr/0016-cross-engine-browser-assurance.md'; +const securityDisclosureAdr = + 'docs/adr/0017-security-disclosure-lifecycle.md'; + +describe('canonical architecture decision coverage', () => { + it('requires durable ADRs for migration routing, browser assurance, and security disclosure', () => { + for (const path of [migrationAdr, browserAssuranceAdr, securityDisclosureAdr]) { + expect(existsSync(resolve(process.cwd(), path)), path).toBe(true); + } + + const index = repositoryFile('docs/adr/README.md'); + expect(index).toContain('0015-envelope-schema-migration-routing.md'); + expect(index).toContain('0016-cross-engine-browser-assurance.md'); + expect(index).toContain('0017-security-disclosure-lifecycle.md'); + expect(index).toContain('Envelope schema identity and host-owned migration routing'); + expect(index).toContain('Cross-engine browser-semantic release assurance'); + expect(index).toContain('Security disclosure lifecycle and coordinated vulnerability handling'); + }); + + it('keeps the new decisions complete enough for acquisition review', () => { + for (const path of [migrationAdr, browserAssuranceAdr, securityDisclosureAdr]) { + const adr = repositoryFile(path); + expect(adr).toContain('Status: Proposed'); + for (const heading of [ + '## Context', + '## Alternatives considered', + '## Decision', + '## Consequences', + '## Failure and recovery', + '## Security and privacy impact', + '## Compatibility and migration', + '## Verification', + '## Rollback or supersession', + '## References', + ]) { + expect(adr).toContain(heading); + } + } + }); + + it('distinguishes documentation completeness from implementation maturity', () => { + const fitness = repositoryFile('docs/DOCUMENTATION_FITNESS.md'); + expect(fitness).toContain('Envelope schema identity / migration routing'); + expect(fitness).toContain('Cross-engine browser-semantic release assurance'); + expect(fitness).toContain('SECURITY disclosure policy'); + expect(fitness).toContain('present_current'); + expect(fitness).toContain('implemented_on_protected_main'); + expect(fitness).toContain('planned'); + expect(fitness).toContain('Issue #74'); + expect(fitness).toContain('Issue #66'); + }); + + it('makes both planned decision paths reviewable as diagrams and standards traceability', () => { + const uml = repositoryFile('docs/UML.md'); + const traceability = repositoryFile('docs/TRACEABILITY.md'); + + expect(uml).toContain('## Envelope identity and host-owned migration routing'); + expect(uml).toContain('## Cross-engine browser-semantic release assurance'); + expect(traceability).toContain('Envelope version routing'); + expect(traceability).toContain('Cross-engine release assurance'); + expect(traceability).toContain('RFC 7493'); + }); + + it('keeps the conceptual ERD explicit without inventing Inkspan persistence', () => { + const dataModel = repositoryFile('docs/DATA_MODEL.md'); + + for (const marker of [ + 'document_schema_identity', + 'browser_assurance_evidence', + 'browser_difference_allowance', + ]) { + expect(dataModel).toContain(marker); + } + expect(dataModel).toContain('planned value and evidence objects'); + expect(dataModel).toContain('does **not** own an application database'); + expect(dataModel).toContain('physical database ERD'); + }); + + it('keeps autonomous maintenance work-conserving without making it product runtime', () => { + const agents = repositoryFile('AGENTS.md'); + const claude = repositoryFile('CLAUDE.md'); + const fitness = repositoryFile('docs/DOCUMENTATION_FITNESS.md'); + + for (const guidance of [agents, claude]) { + expect(guidance).toContain('work-conserving'); + expect(guidance).toContain('blocked PR blocks only that lane'); + expect(guidance).toContain('status report'); + expect(guidance).toContain('external scheduler'); + } + + expect(fitness).toContain('Autonomous maintenance governance'); + expect(fitness).toContain('out_of_scope'); + expect(fitness).toContain('external scheduler'); + }); +}); diff --git a/src/canonicalProductDocumentation.test.ts b/src/canonicalProductDocumentation.test.ts new file mode 100644 index 00000000..c15e2ef9 --- /dev/null +++ b/src/canonicalProductDocumentation.test.ts @@ -0,0 +1,290 @@ +import { existsSync, 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'); + +const requiredFiles = [ + 'AGENTS.md', + 'CLAUDE.md', + 'ARCHITECTURE.md', + 'CHANGELOG.md', + 'SECURITY.md', + 'docs/README.md', + 'docs/DOCUMENTATION_FITNESS.md', + 'docs/PRD.md', + 'docs/TRD.md', + 'docs/CONTRACTS.md', + 'docs/UML.md', + 'docs/DATA_MODEL.md', + 'docs/THREAT_MODEL.md', + 'docs/TEST_STRATEGY.md', + 'docs/OPERABILITY.md', + 'docs/TRACEABILITY.md', + 'docs/adr/README.md', + 'docs/adr/0001-product-host-authority.md', + 'docs/adr/0002-document-revision-authority.md', + 'docs/adr/0003-safe-rich-clipboard.md', + 'docs/adr/0004-durable-validator-autosave.md', + 'docs/adr/0005-revision-scoped-review-evidence.md', + 'docs/adr/0006-ssr-native-form-boundary.md', + 'docs/adr/0007-provider-neutral-collaboration.md', + 'docs/adr/0008-deterministic-office-rendering.md', + 'docs/adr/0009-naruon-modular-composition.md', + 'docs/adr/0010-release-evidence-authority.md', + 'docs/adr/0011-deterministic-vs-model-assisted-authoring.md', + 'docs/adr/0012-spreadsheet-formula-injection.md', + 'docs/adr/0013-atomic-file-publication.md', + 'docs/adr/0014-local-assets-font-licensing.md', + 'docs/adr/0015-envelope-schema-migration-routing.md', + 'docs/adr/0016-cross-engine-browser-assurance.md', + 'docs/adr/0017-security-disclosure-lifecycle.md', + 'src/fonts/OFL.txt', + 'src/fonts/NOTICE', + 'src/fonts/fonts.css', + 'src/fonts/fonts-latin.css', +] as const; + +describe('canonical product documentation graph', () => { + it('keeps the acquisition-critical document graph discoverable', () => { + for (const path of requiredFiles) { + expect(existsSync(resolve(process.cwd(), path)), path).toBe(true); + } + }); + + it('keeps contributor guidance aligned with the canonical documentation spine', () => { + const agents = repositoryFile('AGENTS.md'); + const claude = repositoryFile('CLAUDE.md'); + const index = repositoryFile('docs/README.md'); + + for (const document of [agents, claude]) { + expect(document).toContain('docs/README.md'); + expect(document).toContain('Protected `main`'); + expect(document).toContain('docs/PRD.md'); + expect(document).toContain('docs/TRD.md'); + expect(document).toContain('docs/CONTRACTS.md'); + } + for (const marker of [ + '../ARCHITECTURE.md', + '../SECURITY.md', + 'DOCUMENTATION_FITNESS.md', + 'PRD.md', + 'TRD.md', + 'CONTRACTS.md', + 'UML.md', + 'DATA_MODEL.md', + 'THREAT_MODEL.md', + 'TEST_STRATEGY.md', + 'OPERABILITY.md', + 'TRACEABILITY.md', + 'adr/README.md', + ]) { + expect(index).toContain(marker); + } + }); + + it('does not promote active-PR product requirements to protected-main implementation', () => { + const prd = repositoryFile('docs/PRD.md'); + const productDefinition = prd.slice( + prd.indexOf('## Product definition'), + prd.indexOf('## Users and buyers'), + ); + + expect(productDefinition).toContain('Protected `main`'); + expect(productDefinition).toContain('Active PR / Proposed'); + expect(productDefinition).toContain('not shipped claims'); + }); + + it('does not leave merged autosave lifecycle observation classified as active-PR work', () => { + const prd = repositoryFile('docs/PRD.md'); + const trd = repositoryFile('docs/TRD.md'); + const fitness = repositoryFile('docs/DOCUMENTATION_FITNESS.md'); + const currentScope = prd.slice(prd.indexOf('## Current, proposed, and planned scope')); + + expect(currentScope).toContain('Lifecycle observation is implemented on protected `main`'); + expect(currentScope).not.toMatch(/open development lines include[^.]*lifecycle observation/u); + expect(trd).toContain('Autosave lifecycle observation is implemented on protected `main`'); + expect(trd).not.toContain('open clipboard, autosave observation'); + expect(fitness).toContain('Autosave lifecycle observation'); + expect(fitness).toMatch(/Autosave lifecycle observation[^\n]*implemented_on_protected_main/u); + }); + + it('records host ownership and deterministic Inkspan authority consistently', () => { + const prd = repositoryFile('docs/PRD.md'); + const trd = repositoryFile('docs/TRD.md'); + const contracts = repositoryFile('docs/CONTRACTS.md'); + const threatModel = repositoryFile('docs/THREAT_MODEL.md'); + const operability = repositoryFile('docs/OPERABILITY.md'); + + for (const document of [prd, trd, contracts, threatModel, operability]) { + expect(document).toMatch(/host[^.]*transport/i); + expect(document).toMatch(/host[^.]*authentication/i); + expect(document).toMatch(/host[^.]*authorization/i); + expect(document).toMatch(/host[^.]*tenant(?: isolation|cy)/i); + expect(document).toMatch(/host[^.]*durable persistence|host[^.]*persistence/i); + } + expect(prd).toContain('deterministic'); + expect(trd).toContain('Protected `main`'); + expect(threatModel).toContain('document bodies'); + expect(operability).toContain('rollback'); + }); + + it('preserves durable product decisions from the canonical documentation graph', () => { + const prd = repositoryFile('docs/PRD.md'); + const trd = repositoryFile('docs/TRD.md'); + const contracts = repositoryFile('docs/CONTRACTS.md'); + const uml = repositoryFile('docs/UML.md'); + const dataModel = repositoryFile('docs/DATA_MODEL.md'); + + for (const marker of ['Markdown', 'HTML', 'Office', 'naruon', 'provider-neutral']) { + expect(prd).toContain(marker); + } + for (const marker of ['network-free', 'macro-free', 'formula', 'Office']) { + expect(trd).toContain(marker); + } + for (const marker of [ + 'document envelope', + 'autosave', + 'collaboration', + 'naruon', + 'host-owned', + 'versioned', + ]) { + expect(contracts).toContain(marker); + } + for (const marker of ['Office', 'naruon', 'Yjs', 'file publication']) { + expect(uml).toContain(marker); + } + expect(uml).toContain('CI-only; not runtime'); + for (const marker of [ + 'conversion_request', + 'conversion_artifact', + 'render_warning', + 'host_capability', + 'audit_event', + ]) { + expect(dataModel).toContain(marker); + } + }); + + it('documents realistic security, test, and release evidence boundaries', () => { + const security = repositoryFile('SECURITY.md'); + const threatModel = repositoryFile('docs/THREAT_MODEL.md'); + const testStrategy = repositoryFile('docs/TEST_STRATEGY.md'); + const traceability = repositoryFile('docs/TRACEABILITY.md'); + const contracts = repositoryFile('docs/CONTRACTS.md'); + + expect(security).toContain('Report a vulnerability'); + expect(security).toContain('private GitHub Security Advisory'); + expect(security).toContain('does not promise a response-time SLA'); + expect(threatModel).toContain('clipboard'); + expect(threatModel).toContain('formula'); + expect(threatModel).toContain('Yjs'); + expect(testStrategy).toContain('Chromium'); + expect(testStrategy).toContain('Firefox'); + expect(testStrategy).toContain('WebKit'); + expect(testStrategy).toContain('100%'); + expect(traceability).toContain('RFC 9110'); + expect(traceability).toContain('WCAG 2.2'); + expect(traceability).toContain('Protected `main`'); + expect(contracts).toContain('No secret'); + expect(contracts).toContain('No database'); + expect(contracts).toContain('degraded'); + }); + + it('keeps documentation completeness and implementation maturity explicit', () => { + const fitness = repositoryFile('docs/DOCUMENTATION_FITNESS.md'); + + for (const documentFamily of [ + 'PRD', + 'TRD', + 'ARCHITECTURE', + 'ADR', + 'UML', + 'DATA_MODEL / ERD', + 'SECURITY', + 'THREAT_MODEL', + 'TEST_STRATEGY', + 'OPERABILITY', + 'TRACEABILITY', + ]) { + expect(fitness).toContain(documentFamily); + } + + for (const status of [ + 'present_current', + 'implemented_on_protected_main', + 'implemented_on_active_pr', + 'planned', + 'not_applicable', + ]) { + expect(fitness).toContain(status); + } + + expect(fitness).toContain('SECURITY disclosure policy'); + expect(fitness).toContain('physical relational ERD'); + expect(fitness).toContain('not_applicable'); + expect(fitness).toContain('Protected `main`'); + expect(fitness).toContain('not yet a protected-main canonical baseline'); + }); + + it('keeps offline font provenance and network-free asset contracts explicit', () => { + const license = repositoryFile('src/fonts/OFL.txt'); + const notice = repositoryFile('src/fonts/NOTICE'); + const fullFonts = repositoryFile('src/fonts/fonts.css'); + const latinFonts = repositoryFile('src/fonts/fonts-latin.css'); + + expect(license).toContain('SIL Open Font License, Version 1.1'); + expect(license).toContain('Reserved Font Name "Noto"'); + for (const marker of [ + 'Noto Sans', + 'Noto Sans KR', + 'Noto Sans JP', + 'Noto Sans SC', + 'Noto Sans TC', + 'air-gapped', + 'OFL-1.1', + ]) { + expect(notice).toContain(marker); + } + for (const stylesheet of [fullFonts, latinFonts]) { + const fontUrls = [ + ...stylesheet.matchAll( + /url\(\s*(?:'([^']*)'|"([^"]*)"|([^'")\s]+))\s*\)/gu, + ), + ].map(([, singleQuoted, doubleQuoted, unquoted]) => + singleQuoted ?? doubleQuoted ?? unquoted, + ); + expect(fontUrls.length).toBeGreaterThan(0); + for (const url of fontUrls) { + expect(url).toMatch(/^\.\/files\//u); + } + } + }); + + it('keeps detailed ADRs indexed and complete enough for acquisition review', () => { + const index = repositoryFile('docs/adr/README.md'); + + for (const path of requiredFiles.filter((path) => /docs\/adr\/\d{4}-/.test(path))) { + const filename = path.split('/').at(-1); + expect(filename).toBeDefined(); + expect(index).toContain(filename!); + const adr = repositoryFile(path); + for (const heading of [ + '## Context', + '## Alternatives considered', + '## Decision', + '## Consequences', + '## Failure and recovery', + '## Security and privacy impact', + '## Compatibility and migration', + '## Verification', + '## Rollback or supersession', + ]) { + expect(adr).toContain(heading); + } + } + }); +}); From 32310a50b20f2f0a9814d4ae000057ca9b0492db Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 03:12:06 +0900 Subject: [PATCH 2/2] docs: make protected lifecycle status explicit --- docs/PRD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PRD.md b/docs/PRD.md index 1f538cf2..daca27f3 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -129,4 +129,4 @@ Shareable acquisition evidence excludes production tenant content and credential Protected `main` is the sole implemented baseline. Open PRs may describe Proposed or Active work but are not shipped contracts until protected integration. Canonical documentation must state when a requirement is target architecture rather than current implementation. -The root security disclosure lifecycle, lifecycle observation, toolbar shortcut accessibility metadata, SSR/native-form serialization, revision-scoped selection evidence, and document-transition evidence are implemented on protected `main`. Current open development lines include SafeClipboard and canonical documentation reconciliation. Cross-engine browser assurance remains planned behind SafeClipboard, and a future envelope-identity migration-routing API is tracked separately and must preserve host migration ownership. +Lifecycle observation is implemented on protected `main`. The root security disclosure lifecycle, toolbar shortcut accessibility metadata, SSR/native-form serialization, revision-scoped selection evidence, and document-transition evidence are also implemented on protected `main`. Current open development lines include SafeClipboard and canonical documentation reconciliation. Cross-engine browser assurance remains planned behind SafeClipboard, and a future envelope-identity migration-routing API is tracked separately and must preserve host migration ownership.