diff --git a/AGENTS.md b/AGENTS.md
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,14 +1,36 @@
-# AGENTS.md
-
-Shared Rust libraries for the Craft Apps. The shared standards live in
-[craftrules](https://github.com/storytold/craftrules) (`../../craftrules` on a dev machine): read its
-`AGENTS.md`, then `standards/never-crash.md`, `standards/engineering.md` and `standards/licensing.md`.
-
-- **Never crash.** Every crate's `lib.rs` starts with
-  `#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]`.
-  No `unsafe` (the workspace forbids it).
-- **New crates** go in `crates/<name>`, named `craft-<name>`, inherit the workspace package fields
-  and lints, and are listed in `[workspace.dependencies]`.
-- **No app code.** A crate here must be useful to more than one app and must not depend on any app.
-- **Before merging:** `cargo fmt --check`, `cargo clippy --workspace --all-targets -- -D warnings`,
-  `cargo test --workspace`.
+# Agent instructions
+
+Shared Rust libraries for the Craft Apps. Read [CONTRIBUTING.md](CONTRIBUTING.md) and the
+affected crate's README/API before changing it. The shared standards are in
+[craftrules](https://github.com/storytold/craftrules): `AGENTS.md`, `standards/never-crash.md`,
+`standards/engineering.md` and `standards/licensing.md`. If unavailable, report that limitation;
+these public instructions and the checked-out source remain usable.
+
+- Keep the never-crash Clippy deny header in each library root and inherit workspace lints,
+  including `unsafe_code = "forbid"`. Handle invalid input and errors explicitly.
+- New crates go in `crates/<name>`, are named `craft-<name>`, inherit workspace package metadata
+  and lints, and are registered in workspace dependencies. Do not depend on app crates.
+- Identify two actual consumers before extracting a capability. Compare behavior, preserve
+  intentional app differences, and document errors, state ownership and platform boundaries.
+- Preserve unrelated changes and consumer pins. Use isolated worktrees for compatibility
+  comparisons and record exact source identities. Follow host build/storage rules.
+- Use the existing commands in [CI and verification](docs/ci-and-verification.md). The merge
+  gates are formatting, workspace Clippy and tests; feature/platform checks follow the changed
+  surface. For docs-only edits, check links, commands and source claims and report that scope.
+- Distinguish proposals, implementation, executed tests and native observations. Never turn
+  an unverified consumer inventory or requested hardware profile into passing evidence.
+- Use the contributor's own authorized Git identity. Never configure another person's name or
+  email as a repository-wide author. Honor username-only credit preferences and preserve
+  existing contributors' credits. Do not infer a public display name or change it.
+- Keep commits and PRs focused, with accurate validation and limitations. Follow the selected
+  repository merge method; these instructions do not mandate squashing or rewriting history.
+
+Service boundaries are in [shared services](docs/shared-services-architecture.md); API design
+and style guidance are in [type contracts](docs/type-system-contracts.md) and
+[code style](docs/code-style-and-lints.md). Proposed crates and tooling are not prerequisites.
+
+Read the [shared contracts](docs/shared-crate-contract.md) and
+[extraction RFC](docs/rfc-shared-capabilities-architecture.md). The
+[consumer inventory](docs/consumer-dependency-map.md) and
+[platform plan](docs/platform-test-inventory.md) require evidence before claiming adoption
+or completed verification.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,84 @@
+# Contributing to craft-libs
+
+Start with the [library catalog](README.md#libraries) and the affected crate's README and API.
+Shared crates provide mechanisms useful to multiple apps; document state, commands, undo,
+localization and product policy stay with the application.
+
+## Propose and implement a focused change
+
+Shared UI or functionality required across multiple Craft applications must land in craft-libs first before being adopted downstream. Downstream consumer pull requests should remain draft pull requests pinned to branch heads until the shared library PR merges into main.
+
+### Upstream-First UI Pre-Check
+
+Before authoring new UI widgets, shell chrome, persistence, or platform integration in an application:
+1. Check `storytold/craft-libs`:
+   - Shell widgets & chrome: `craft-ui` (`ZoomBar`, `FontBox`, `RibbonGroup`, `DockLayout`, `StatusBar`)
+   - Platform menus & lifecycle: `craft-menu`, `craft-quit`
+   - Persistence & preferences: `craft-atomic`, `craft-settings`
+   - Printing: `craft-print`
+2. If the component exists upstream, consume it via `Cargo.toml`. Do not implement local variants.
+3. If the component does not exist in `craft-libs` but applies to two or more Craft applications:
+   - Propose the component in `storytold/craft-libs/issues` before building in-tree app code.
+   - Implement it in `craft-libs` first under `#![forbid(unsafe_code)]` and zero-panic invariants.
+   - Adopt it downstream in a subsequent pin update.
+
+For a new extraction, inspect two actual consumers at known revisions. Compare their callers,
+behavior and workloads. Distinguish defects from intentional app differences and retain
+unresolved policy in the app. Describe shared errors, post-failure state and platform limits
+before choosing the API. Existing public contracts must remain compatible within their release
+line; do not redesign them only to make every crate look alike.
+
+Code uses Rust 2024, resolver 3 and a minimum compiler of Rust 1.95. Workspace lints forbid
+unsafe code; library roots deny Clippy's unwrap, expect, panic, unimplemented, todo and
+unreachable lints. Check input-derived indices, arithmetic, allocations and recursion bounds.
+Return useful errors and preserve state on rejection. See [type contracts](docs/type-system-contracts.md)
+and [code style](docs/code-style-and-lints.md).
+
+Code is MIT OR Apache-2.0. Do not copy proprietary implementation or incompatible licensed
+code. Record bundled asset sources, authors and licenses in [ATTRIBUTION.md](ATTRIBUTION.md)
+and preserve required notices. Keep large corpora outside the repository; prefer small
+synthetic regression cases. Check the actual crate's asset/build setup instead of assuming
+an external corpus or font-fetching tool exists.
+
+## Add a crate
+
+Create `crates/<name>` with package name `craft-<name>`. Inherit `version`, `edition`,
+`license`, `repository`, `rust-version` and workspace lints in its manifest. The workspace's
+`crates/*` member glob includes it; add its path to `[workspace.dependencies]`. Add a README
+and a catalog entry explaining scope, features, platforms and usage. A crate must not depend
+on any application. The catalog at the selected revision defines what is available; an open
+PR for a media-time crate or compatibility runner is not a shipped dependency.
+
+## Validate and submit
+
+Add regression tests that fail for the defect or independently check the new behavior.
+Exercise meaningful boundaries and app-specific differences. Follow
+[CI and verification](docs/ci-and-verification.md) for available workspace and targeted WASM
+commands. For an extraction or behavior change, compare affected consumers in isolated
+worktrees at exact baseline and candidate revisions. Check package names before choosing
+commands, record nonzero test counts, and separate baseline failures from new regressions.
+
+Native service changes also need suitable OS/workflow evidence. A unit test or cross-compile
+cannot establish interactive save/close behavior, physical printing or power-loss durability.
+Report unavailable checks explicitly. For documentation-only changes, validate local links,
+command availability and claims against source; do not present an older build as new evidence.
+Required merge/release checks still apply to the final combined source.
+
+Use your own authorized commit identity and preferred contributor credit. Never copy another
+contributor's name/email into Git configuration. Give the PR a concise problem/change summary,
+verification at the reviewed revision and remaining limitations. Link relevant issues without
+claiming they are fixed until their acceptance criteria are met. Keep commits reviewable;
+maintainers choose the merge method. No repository-wide squash requirement is established here.
+
+## Releases and consumer upgrades
+
+The [initial release policy](https://github.com/storytold/craft-libs/pull/41) defines
+coordinated, immutable workspace tags and independent consumer upgrades. Use the versioning
+document included in the release you adopt. Its 0.1.x
+line permits compatible fixes and additions; changes requiring consumer action belong in
+0.2.0. A release does not require every application to update its pin.
+
+Keep every craft-libs dependency in a consumer graph on one revision. Test an upgrade in that
+application before changing its shipped pin. Proposed future APIs and support promises do not
+apply retroactively to existing pins. [Shared services](docs/shared-services-architecture.md)
+describes the boundaries to check when adopting persistence, shutdown, printing and UI helpers.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 Craft App Libraries
-===================
+============
 
 This repo contains libraries used by the various Craft Apps. It's a Rust monorepo workspace, and the libraries can be included by any Craft Apps that need them.
 
@@ -40,6 +40,17 @@ craft-<name> = { git = "https://github.com/storytold/craft-libs", rev = "<commit
 - [Panel docking](docs/ui-docking.md): validated panel moves, floating groups, workspace adapters
   and an optional egui docking surface.
 
+## Contributing
+
+Guidelines for proposing shared capabilities, crate layout conventions, the "Never Crash" standard, and cross-application verification are documented in [CONTRIBUTING.md](CONTRIBUTING.md). Operational agent instructions live in [AGENTS.md](AGENTS.md).
+
+## Architecture & Standards
+
+- [Shared capability extraction RFC](docs/rfc-shared-capabilities-architecture.md): The intended role of `craft-libs`, capability acceptance protocol, differential testing, and evidence standards.
+- [Shared crate contracts](docs/shared-crate-contract.md): Mandatory invariants, failure semantics, post-failure state guarantees, and cross-platform behavior specifications.
+- [Consumer Dependency Map](docs/consumer-dependency-map.md) ([JSON](docs/consumer-dependency-map.json)): Unverified consumer inventory and instructions for recording actual pins and comparisons.
+- [Platform Test Inventory](docs/platform-test-inventory.md) ([JSON](docs/platform-test-inventory.json)): Planned verification scopes and evidence requirements; no completed platform qualifications.
+
 ## License and credits
 
 craft-libs is dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option.
diff --git a/docs/ci-and-verification.md b/docs/ci-and-verification.md
new file mode 100644
--- /dev/null
+++ b/docs/ci-and-verification.md
@@ -0,0 +1,52 @@
+# CI and verification
+
+Run these workspace gates for code changes and before merging the combined source:
+
+```sh
+cargo fmt --check
+cargo clippy --workspace --all-targets --locked -- -D warnings
+cargo test --workspace --locked
+cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
+cargo test --workspace --all-features --locked
+```
+
+The workspace uses Rust 1.95 or newer. Follow host-specific allocation/build instructions
+when running these commands. For docs-only edits, validate links, commands and source claims;
+state that scope rather than quoting old test counts as validation of the new revision.
+
+## Targeted platform checks
+
+With the WASM target installed, the supported check surface is:
+
+```sh
+cargo check -p craft-ui --all-features --locked --target wasm32-unknown-unknown
+cargo check -p craft-settings --locked --target wasm32-unknown-unknown
+```
+
+The settings check covers its trait, document helpers and errors, not persistent storage.
+Native printing and shutdown have separate platform contracts. Do not advertise all workspace
+crates or every architecture as browser-compatible from these commands.
+
+Inspect [Shared UI](../.github/workflows/ui.yml) and
+[Print spooler](../.github/workflows/print.yml) for current job matrices and path filters.
+They cover selected native Linux/macOS/Windows jobs and UI WASM; a docs-only PR may not trigger
+them. Workspace release validation is introduced by the
+[release policy change](https://github.com/storytold/craft-libs/pull/41); inspect the workflows
+present at the selected revision. Report actual candidate check results before release,
+including all required final-source jobs.
+
+## Consumer comparisons
+
+No `cargo xtask ci`, `cargo xtask compat` or `compat-check` command is required or available
+in this revision. Inspect the affected consumer's manifest and instructions and run its real
+commands in isolated worktrees. Record its SHA, baseline resolved craft-libs pin, candidate
+SHA/patch, features, commands, executed test counts, result and environment. Preserve the
+consumer's normal checkout and real user data. Zero tests or zero comparisons is unverified.
+Baseline failures must be distinguished from candidate regressions; an unresolved baseline
+failure is not evidence that the candidate works.
+
+For changes in native behavior, exercise the app workflow on the relevant OS/device. Mocked
+filesystem failures test error handling; compilation checks an API surface; interactive save,
+cancel, close and reopen checks app behavior. Physical Touch Bar or printer claims need the
+corresponding hardware evidence. None of these alone establishes physical power-loss survival.
+Keep limitations and evidence links with the PR so the result can be reviewed at its exact SHA.
diff --git a/docs/code-style-and-lints.md b/docs/code-style-and-lints.md
new file mode 100644
--- /dev/null
+++ b/docs/code-style-and-lints.md
@@ -0,0 +1,24 @@
+# Code style and lints
+
+Use the checked-in [rustfmt configuration](../rustfmt.toml) (`max_width = 150`,
+`use_small_heuristics = "Max"`) and nearby code conventions. Inherit package metadata and
+lints from [Cargo.toml](../Cargo.toml); the workspace forbids unsafe code. Keep each library's
+Clippy deny header for unwrap, expect, panic, unimplemented, todo and unreachable.
+The [Clippy configuration](../clippy.toml) allows unwrap, expect, panic and indexing in tests;
+production code still needs explicit handling of invalid input and errors.
+
+Use meaningful regression assertions and independently derived expectations. When handling
+poisoned mutexes, decide whether invariants can be restored or an error is needed; blindly
+taking the inner value is not always safe. Treat thread joins as fallible. Avoid blocking or
+allocation where a caller's measured latency contract prohibits them.
+
+Explain non-obvious invariants and compatibility constraints near the code. Keep API docs
+accurate about errors, mutation and platform limits. Follow [type contracts](type-system-contracts.md)
+and [verification guidance](ci-and-verification.md).
+
+Use original or compatibly licensed code and preserve attribution and notices. See
+[CONTRIBUTING.md](../CONTRIBUTING.md) for asset and crate requirements. Do not copy another
+person's commit identity; use your own authorized identity and chosen credit. Write concise
+commit subjects and explain important tradeoffs in the body. PRs should state the problem,
+resulting behavior and actual verification. Keep changes focused; the repository's selected
+merge method determines integration, with no blanket squash or history-rewrite requirement.
diff --git a/docs/consumer-dependency-map.json b/docs/consumer-dependency-map.json
new file mode 100644
--- /dev/null
+++ b/docs/consumer-dependency-map.json
@@ -0,0 +1,87 @@
+{
+  "format_version": 1,
+  "status": "unverified_inventory",
+  "shared_library_repository": "storytold/craft-libs",
+  "applications": [
+    {
+      "repository": "storytold/photocraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/vectorcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/designcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/pdfcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/lightcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/filmcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/effectcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/cadcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/soundcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    },
+    {
+      "repository": "storytold/wordcraft",
+      "status": "unverified",
+      "consumer_revision": null,
+      "dependencies": [],
+      "verified_commands": [],
+      "evidence": []
+    }
+  ]
+}
diff --git a/docs/consumer-dependency-map.md b/docs/consumer-dependency-map.md
new file mode 100644
--- /dev/null
+++ b/docs/consumer-dependency-map.md
@@ -0,0 +1,23 @@
+# Consumer dependency evidence
+
+The [JSON inventory](consumer-dependency-map.json) lists applications to inspect. All entries
+start **unverified**: this PR has not audited their current manifests, lockfiles or test
+commands. It deliberately supplies no suite-wide pin, adopted crates or guessed package names.
+Do not use this file as a release gate or claim all listed apps consume craft-libs.
+
+When auditing a consumer, record its exact commit, manifest paths and dependency declarations,
+the resolved craft-libs revision from its lockfile, used crates/features and source links.
+Record a validated command only after checking that package and test target exist. Keep
+baseline/candidate comparison results, nonzero test counts, environment and durable evidence
+links with the audit. A candidate integration and a merged dependency pin are separate states.
+
+Update only entries for which there is evidence. `null` and empty lists mean unknown, not
+absent or passing. The file is ordinary inventory data, not a JSON Schema or runner config.
+No compatibility runner consumes it in this revision.
+
+Application upgrades are independent. A new library release does not update their manifests.
+Validate affected internal dependents and actual consumers for a change, then propose
+consumer pin changes separately when needed. Keep all craft-libs dependencies within one
+consumer graph on the same revision. See the
+[initial release policy](https://github.com/storytold/craft-libs/pull/41); use the versioning
+document included in the selected release.
diff --git a/docs/platform-test-inventory.json b/docs/platform-test-inventory.json
new file mode 100644
--- /dev/null
+++ b/docs/platform-test-inventory.json
@@ -0,0 +1,51 @@
+{
+  "format_version": 1,
+  "status": "verification_plan",
+  "environments": [
+    {
+      "id": "macos-touchbar",
+      "status": "unverified",
+      "scope": "Native close/menu, save cancellation and Touch Bar teardown on relevant hardware",
+      "os_version": null,
+      "hardware": null,
+      "candidate_revision": null,
+      "evidence": []
+    },
+    {
+      "id": "macos-native",
+      "status": "unverified",
+      "scope": "Native quit, printing and filesystem behavior on a recorded macOS environment",
+      "os_version": null,
+      "hardware": null,
+      "candidate_revision": null,
+      "evidence": []
+    },
+    {
+      "id": "linux-native",
+      "status": "unverified",
+      "scope": "Filesystem errors, UI tests and CUPS integration where available",
+      "os_version": null,
+      "hardware": null,
+      "candidate_revision": null,
+      "evidence": []
+    },
+    {
+      "id": "windows-native",
+      "status": "unverified",
+      "scope": "Filesystem sharing violations and Windows spooler integration",
+      "os_version": null,
+      "hardware": null,
+      "candidate_revision": null,
+      "evidence": []
+    },
+    {
+      "id": "wasm32",
+      "status": "unverified",
+      "scope": "craft-ui compilation and craft-settings trait/document helpers only",
+      "os_version": null,
+      "hardware": null,
+      "candidate_revision": null,
+      "evidence": []
+    }
+  ]
+}
diff --git a/docs/platform-test-inventory.md b/docs/platform-test-inventory.md
new file mode 100644
--- /dev/null
+++ b/docs/platform-test-inventory.md
@@ -0,0 +1,21 @@
+# Platform verification plan
+
+The [JSON inventory](platform-test-inventory.json) is a list of verification needs, not a
+record of available hardware or completed tests. Every environment starts **unverified**.
+There are no inferred tester names, OS versions or successful runs.
+
+Select an environment that can exercise the changed behavior. In particular, a generic Mac
+runner does not establish Touch Bar behavior. The motivation for that check is recorded in
+[the quit implementation](../crates/quit/src/lib.rs) and
+[issue #3](https://github.com/storytold/craft-libs/issues/3); this inventory does not independently
+confirm a reproduction or fix on any model.
+
+For each run, record the candidate SHA/patch, crate/features, exact command or GUI steps,
+OS/architecture, relevant hardware/filesystem, observed result and evidence link. Distinguish
+mock tests, compilation, native API tests and interactive hardware observations. An unavailable
+environment remains unverified; successful compilation cannot fill that gap.
+
+Support is crate-specific. UI WASM checks and settings document-helper checks do not imply
+browser filesystem persistence or native printing. A VM print queue test does not establish
+physical printer output. File-sync ordering does not establish power-loss survival. Read the
+workflows at the candidate revision for automated coverage and the release notes for its scope.
diff --git a/docs/rfc-shared-capabilities-architecture.md b/docs/rfc-shared-capabilities-architecture.md
new file mode 100644
--- /dev/null
+++ b/docs/rfc-shared-capabilities-architecture.md
@@ -0,0 +1,52 @@
+# RFC: extracting shared capabilities
+
+Status: proposed contributor workflow. This document does not announce a new crate,
+compatibility runner, consumer adoption or platform qualification.
+
+`craft-libs` holds reusable mechanisms. Applications keep document state, commands, undo,
+product policy, localization and their own UI choices. Existing crates are listed in the
+[README catalog](../README.md#libraries); open PRs are not available dependencies.
+
+## Extraction protocol
+
+1. **Find two actual consumers.** Inspect their implementations and callers at recorded
+   revisions. Identify the shared behavior, state owners, workloads and platform assumptions.
+   A list of possible apps is not adoption evidence.
+2. **Compare behavior.** Use representative fixtures and boundary cases from both consumers.
+   Classify differences as defects, intentional app policy or unresolved assumptions. Keep
+   intentional differences explicit in adapters or APIs; retain unresolved behavior in the app.
+3. **Write the contract.** Define errors, post-failure state, resource limits, performance
+   expectations and platform boundaries. Follow the [shared contracts](shared-crate-contract.md).
+   Do not hide unsupported operations behind successful empty results.
+4. **Verify the change.** Run the repository's relevant checks and meaningful comparisons in
+   isolated consumer worktrees. Record the baseline pin, candidate commit/patch, consumer SHA,
+   command, executed test count and result. Keep working trees and real user data untouched.
+   Separate pre-existing failures from candidate regressions; zero comparisons is unverified.
+   Exercise affected native workflows on a suitable OS/device and retain evidence.
+
+There is no required `cargo xtask compat`, `compat-check` or `ci` command in this revision.
+Use commands present in the checked-out repositories. A future runner may automate this
+protocol, but its absence does not prevent a focused, manually recorded comparison.
+
+## Evidence and release decisions
+
+Label claims as proposed/documented, implemented, tested or observed on a named environment.
+A passing mock test does not qualify native behavior, and a GUI smoke test does not prove
+power-loss durability. Include source revision, actual environment, test scope, results and
+limitations. Keep useful failed comparisons and rejected assumptions with the change.
+
+The [consumer map](consumer-dependency-map.md) and
+[platform plan](platform-test-inventory.md) are starting points for collecting evidence;
+they do not certify adoption, hardware access or passing tests. Scope compatibility checks
+to affected consumers and workflows rather than requiring a suite-wide upgrade.
+
+The [initial release policy](https://github.com/storytold/craft-libs/pull/41) defines
+coordinated, immutable workspace tags and independent consumer upgrades. Use the versioning
+document included in the release you adopt. Its 0.1.x
+line permits compatible fixes and additions; changes requiring consumer action belong in
+0.2.0. A release does not require every application to update its pin.
+
+Measure hot-path changes using representative workloads and input sizes. Shared extraction
+should preserve responsiveness and memory behavior as well as results. Use fuzz/property
+coverage where it meaningfully exercises parsers or invariants; do not require an unrelated
+tool or ceremony for every small change.
diff --git a/docs/shared-crate-contract.md b/docs/shared-crate-contract.md
new file mode 100644
--- /dev/null
+++ b/docs/shared-crate-contract.md
@@ -0,0 +1,106 @@
+# Shared crate contracts
+
+This guide sets expectations for new shared APIs and records the boundaries of existing
+services. Read the API and README at the exact dependency revision. Proposals, implementation,
+tests and native observations are different kinds of evidence.
+
+## Workspace rules
+
+Crates must serve more than one app and must not depend on application crates. Inherit
+workspace metadata and lints, including the unsafe-code prohibition. Retain each library's
+never-crash Clippy deny header. The workspace uses Rust 2024, resolver 3 and Rust 1.95 as its
+minimum compiler. Code is dual-licensed MIT OR Apache-2.0; retain third-party notices.
+
+## Errors and state
+
+Define invalid input, supported operations, errors and post-failure state alongside each
+public API. Use `Result` or `Option` when callers need to react. Use typed errors where callers
+need to distinguish cases; do not change an existing error type merely to standardize style.
+A fallback or saturating operation needs documented semantics and boundary tests. A lint
+header alone cannot prove freedom from runtime panics or resource exhaustion.
+
+State exactly which mutations commit before an error and which cleanup is best effort.
+Do not promise rollback across unrelated files, concurrent writers or external side effects.
+Bound input-derived allocation and iteration, and test the relevant malformed inputs.
+
+## Atomic persistence
+
+`atomic_write*` stages beside the resolved destination as `.{stem}.{pid}-{counter}.craft_tmp`,
+using exclusive creation with collision retries. The suffix is not cryptographically random.
+It follows destination symlink chains with a hop limit; it does not perform canonical mount
+verification. The parent directory must already exist.
+
+The replacement path writes and syncs the stage, then renames it over the destination.
+Staging or rename failure leaves the prior destination bytes unchanged by this operation;
+cleanup is attempted, but removal failures and abrupt process termination can leave stages.
+There is no copy fallback for a failed replacement rename, including `EXDEV`. `write_new*`
+is a separate exclusive-creation API: it tries a hard link, then exclusive copy when linking
+is unavailable. The copy fallback can expose a partially written new file; it does not
+provide replacement-style atomic visibility.
+
+File sync uses `sync_all`; Apple platforms fall back to plain `fsync` for the specific
+unsupported-full-sync errors. Parent-directory sync after publication is best effort on
+Unix and a no-op elsewhere. A directory-sync failure does not undo publication or turn it
+into a failed save. This ordering is not proof of physical power-loss survival. Windows
+rename retry handles transient locks with bounded backoff.
+
+Existing Unix destination mode bits are restored after the writer callback. Replacement
+creates a new inode; ownership, extended attributes, ACLs and hard-link identity are not
+preserved. New-file permissions are version-specific: consult the selected revision's
+[atomic API](../crates/atomic/src/lib.rs). The
+[permission policy change](https://github.com/storytold/craft-libs/pull/43) defines normal
+creation permissions for documents/exports (`0666` subject to umask/default ACL), explicit
+private creation for settings (`0600`), and private replacement staging. Check that the
+selected API includes this policy; do not use a generic default as a privacy guarantee.
+
+## Settings
+
+The [settings API and README](../crates/settings/README.md) describe section defaults,
+comment/unknown-key preservation, schema migration, backup and read-only recovery. A newer
+schema or failed migration keeps the store read-only. Invalid TOML is moved aside before
+using defaults; if preservation fails, the store remains read-only. Loading does not
+automatically restore `.bak`.
+
+Before saving, the store rechecks disk contents. Blank or corrupt input must not replace a
+known-good backup. Known values from the store overwrite known keys; comments and unknown
+keys are retained where documented. Saves are best effort, last writer wins, with no
+multi-process locking. Apps own their schema, scheduling, error reporting and final flush;
+a successful atomic write is not an application-level transaction across several files.
+Persistent storage is native-only; WASM exposes the trait, document helpers and error types.
+
+## Application shutdown
+
+The [quit API](../crates/quit/src/lib.rs) bridges egui close requests and the optional
+macOS app-menu Quit action to deferred `-[NSApplication terminate:]`. Apps run their
+unsaved-change decision in `eframe::App::logic` before `on_close_request`, using `CancelClose`
+to keep the app open. They request Close only after a save succeeds or discard is confirmed;
+asynchronous saves must finish first. Keep prompts accessible when the window is hidden.
+
+The crate does not install `applicationShouldTerminate:`, intercept Dock Quit or POSIX
+signals, implement Windows `WM_CLOSE`, or drain arbitrary application buffers. On platforms
+without its native quit path, `quit()` returns false and normal window closing continues.
+The optional menu replaces the macOS application menu; apps with their own menu route the
+Quit item through `on_menu_event`. Menu installation failure leaves the default menu, whose
+Quit can bypass the app's prompt. Apps must handle that failure.
+
+The documented eframe/winit integration runs `on_exit` through `applicationWillTerminate:`;
+code after `run_native` is not a substitute. Test the actual app's save/close behavior and
+relevant hardware separately; this adapter alone does not establish crash-free termination.
+
+## Compatibility and adoption
+
+The [initial release policy](https://github.com/storytold/craft-libs/pull/41) defines
+coordinated, immutable workspace tags and independent consumer upgrades. Use the versioning
+document included in the release you adopt. Its 0.1.x
+line permits compatible fixes and additions; changes requiring consumer action belong in
+0.2.0. A release does not require every application to update its pin.
+
+Validate direct internal dependents, then affected application workflows using exact baseline
+and candidate revisions. Document API removals and migration paths in the breaking release;
+deprecate first when useful. Consumers on immutable older pins keep their old API. Do not make
+API removal conditional on upgrading every application. Use the same craft-libs revision for
+all its crates within one consumer dependency graph to avoid duplicate type identities.
+
+Support is crate- and feature-specific. Browser UI compilation does not establish native
+settings persistence, printing or shutdown support in a browser. See the
+[platform verification plan](platform-test-inventory.md) for evidence requirements.
diff --git a/docs/shared-services-architecture.md b/docs/shared-services-architecture.md
new file mode 100644
--- /dev/null
+++ b/docs/shared-services-architecture.md
@@ -0,0 +1,79 @@
+# Shared service boundaries
+
+These summaries describe the checked-out services. Read the linked API at the exact revision
+you use; pending proposals are identified separately.
+
+## Atomic persistence
+
+`atomic_write*` stages beside the resolved destination as `.{stem}.{pid}-{counter}.craft_tmp`,
+using exclusive creation with collision retries. The suffix is not cryptographically random.
+It follows destination symlink chains with a hop limit; it does not perform canonical mount
+verification. The parent directory must already exist.
+
+The replacement path writes and syncs the stage, then renames it over the destination.
+Staging or rename failure leaves the prior destination bytes unchanged by this operation;
+cleanup is attempted, but removal failures and abrupt process termination can leave stages.
+There is no copy fallback for a failed replacement rename, including `EXDEV`. `write_new*`
+is a separate exclusive-creation API: it tries a hard link, then exclusive copy when linking
+is unavailable. The copy fallback can expose a partially written new file; it does not
+provide replacement-style atomic visibility.
+
+File sync uses `sync_all`; Apple platforms fall back to plain `fsync` for the specific
+unsupported-full-sync errors. Parent-directory sync after publication is best effort on
+Unix and a no-op elsewhere. A directory-sync failure does not undo publication or turn it
+into a failed save. This ordering is not proof of physical power-loss survival. Windows
+rename retry handles transient locks with bounded backoff.
+
+Existing Unix destination mode bits are restored after the writer callback. Replacement
+creates a new inode; ownership, extended attributes, ACLs and hard-link identity are not
+preserved. New-file permissions are version-specific: consult the selected revision's
+[atomic API](../crates/atomic/src/lib.rs). The
+[permission policy change](https://github.com/storytold/craft-libs/pull/43) defines normal
+creation permissions for documents/exports (`0666` subject to umask/default ACL), explicit
+private creation for settings (`0600`), and private replacement staging. Check that the
+selected API includes this policy; do not use a generic default as a privacy guarantee.
+
+## Settings
+
+The [settings API and README](../crates/settings/README.md) describe section defaults,
+comment/unknown-key preservation, schema migration, backup and read-only recovery. A newer
+schema or failed migration keeps the store read-only. Invalid TOML is moved aside before
+using defaults; if preservation fails, the store remains read-only. Loading does not
+automatically restore `.bak`.
+
+Before saving, the store rechecks disk contents. Blank or corrupt input must not replace a
+known-good backup. Known values from the store overwrite known keys; comments and unknown
+keys are retained where documented. Saves are best effort, last writer wins, with no
+multi-process locking. Apps own their schema, scheduling, error reporting and final flush;
+a successful atomic write is not an application-level transaction across several files.
+Persistent storage is native-only; WASM exposes the trait, document helpers and error types.
+
+## Shutdown
+
+The [quit API](../crates/quit/src/lib.rs) bridges egui close requests and the optional
+macOS app-menu Quit action to deferred `-[NSApplication terminate:]`. Apps run their
+unsaved-change decision in `eframe::App::logic` before `on_close_request`, using `CancelClose`
+to keep the app open. They request Close only after a save succeeds or discard is confirmed;
+asynchronous saves must finish first. Keep prompts accessible when the window is hidden.
+
+The crate does not install `applicationShouldTerminate:`, intercept Dock Quit or POSIX
+signals, implement Windows `WM_CLOSE`, or drain arbitrary application buffers. On platforms
+without its native quit path, `quit()` returns false and normal window closing continues.
+The optional menu replaces the macOS application menu; apps with their own menu route the
+Quit item through `on_menu_event`. Menu installation failure leaves the default menu, whose
+Quit can bypass the app's prompt. Apps must handle that failure.
+
+The documented eframe/winit integration runs `on_exit` through `applicationWillTerminate:`;
+code after `run_native` is not a substitute. Test the actual app's save/close behavior and
+relevant hardware separately; this adapter alone does not establish crash-free termination.
+
+## Printing and UI
+
+[craft-print](../crates/print/README.md) owns native discovery and spooler submission; apps
+own PDF generation and document policy. Platform capabilities and errors differ; test the
+backend used by the app. Native spooler support does not imply browser printing support.
+
+[craft-ui](../crates/ui/README.md) supplies egui widgets, theme helpers, icons, docking and
+print-settings UI. App assets, localization, commands and document bindings remain app-owned.
+Test input, focus, accessibility and persistence behavior as well as appearance when changing
+an interaction. Headless tests do not qualify native windows or device accessibility.
diff --git a/docs/type-system-contracts.md b/docs/type-system-contracts.md
new file mode 100644
--- /dev/null
+++ b/docs/type-system-contracts.md
@@ -0,0 +1,26 @@
+# Types, errors and state
+
+Use explicit `Result` or `Option` for failures callers need to handle. Prefer typed errors
+when callers must distinguish causes; `thiserror` is an available design choice, not a
+workspace mandate. Existing APIs include `io::Result`, boxed errors and
+[`Result<T, String>` printing helpers](../crates/ui/src/printing.rs). Changing a public error
+type is an API compatibility decision, not a lint cleanup.
+
+Document invalid input, unsupported operations, mutation ordering and post-failure state.
+An empty success value must not conceal an error. Fallbacks and saturation need explicit
+semantics; they are not substitutes for checked arithmetic when losing information is unsafe.
+Do not add `todo!`, `unimplemented!`, unwrap or panic paths to shipped code.
+
+Validate input-derived indices and UTF-8 boundaries; use `.get()` for fallible access.
+Check lengths, offsets, division and numeric conversions. Bound allocations and recursion
+from untrusted data. Reject non-finite values when the operation requires finite inputs.
+Choose limits and tests based on actual callers and workloads.
+
+Keep logical identities stable across label changes, localization and reordering. Preserve
+app-owned state and intentional product differences when extracting a shared mechanism.
+For future domain types, establish the contract with real consumers first. This revision
+contains no `craft-time` crate and imposes no speculative `Tick` or SMPTE API on contributors.
+
+Use [shared services](shared-services-architecture.md) for persistence and quit boundaries,
+and the [initial release policy](https://github.com/storytold/craft-libs/pull/41) for
+compatibility rules; use the versioning document included in the selected release.
