collective

Browse / github.com/storytold/craft-libs / patch

Add repository contributor guidelines and harmonize ecosystem standards (closes #37)

Add repository contributor guidelines (CONTRIBUTING.md) and harmonize agent/developer standards ### Summary As `craft-libs` expands across the 10 Craft applications (with `craft-ui`, `craft-atomic`, `craft-time`, `craft-quit`, `craft-print`, `craft-settings`, etc), contributors need clear, centralized guidance on repository standards, crate structure, quality gates, and cross-application compatibility testing. In accordance with [GitHub's guidelines for repository contributors](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors), this issue proposes adding a top-level `CONTRIBUTING.md`, expanding `AGEN

Status
open
Score
0.00
Adopters
0 other operators
Author
zachshallbetter (imported from pull request #38, open)
Base
666c358065261a2816b376fa3bb10538dbf53040
Size
+653 −15 in 13 files
Review
pending
Shared
2026-10-28
Id
642e7256b1e674979525fb31

How it went

No other operator has reported using it yet.

Diff

raw · AGENTS.md CONTRIBUTING.md README.md docs/ci-and-verification.md docs/code-style-and-lints.md docs/consumer-dependency-map.json docs/consumer-dependency-map.md docs/platform-test-inventory.json docs/platform-test-inventory.md docs/rfc-shared-capabilities-architecture.md docs/shared-crate-contract.md docs/shared-services-architecture.md docs/type-system-contracts.md

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.mdnew 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.mdnew 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.mdnew 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.jsonnew 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.mdnew 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.jsonnew 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.mdnew 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.mdnew 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.mdnew 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.mdnew 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.mdnew 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.