docs: record rules conformance, import contract, and round-trip law

Add docs/rules-conformance.md documenting how the repo conforms to the
vendored canon, the documentation.md vs dependency-free conflict and its
scoped resolution, the domain-mismatched rules that do not apply, and the
conscious deviations. Add docs/package-import-contract-status.md. State
the round-trip law (Lambda1/Lambda2) in DESIGN.md.
This commit is contained in:
Mihaela M 2026-06-26 06:14:24 +02:00
parent 7270359d14
commit 44abc12e80
3 changed files with 103 additions and 0 deletions

View File

@ -240,3 +240,19 @@ source.
Yams and its bundled `CYaml` / libyaml-derived C parser are compatibility
references for PureYAML. They should guide test cases and behavior questions.
They are not source material to copy into this package.
## Round-trip law
PureYAML keeps the parser (`PureYAML.parse`) and the emitter (`PureYAML.dump`)
as separate hand-written implementations, not one invertible grammar. The
guarantee they jointly hold is a fixed point over the emitter's normal form, not
a bijection over arbitrary author formatting (the surface-to-model relation is
many-to-one: spacing, flow vs block, and comments do not survive into the model):
- Lambda1 (model fidelity): for a `Model.Value` `x`, `parse(dump(x)) == x`.
- Lambda2 (idempotence): `parse(dump(parse(s))) == parse(s)` for any input `s`.
These are enforced as gates, not hoped for: corpus and generated-document
round-trip tests assert reparsed equality, and the empty-collection degenerate
values are pinned as passing regressions. `dump` is total: no force-unwraps, and
every empty collection renders to `[]` / `{}` at every position.

View File

@ -0,0 +1,13 @@
# Package import contract status
One row per target. The contract: the library target imports nothing beyond the
Swift standard library; the test target imports only `PureYAML`, `Testing`, and
`Foundation`.
| Target | Allowed imports | Actual | State |
|---|---|---|---|
| `PureYAML` (library, `Sources/`) | Swift standard library only (no `Foundation`, no external packages) | zero `import` statements | matches |
| `PureYAMLTests` (`Tests/`) | `PureYAML`, `Testing`, `Foundation` | `@testable import PureYAML`, `import Testing`, `import Foundation` | matches |
Enforced mechanically by `scripts/check-forbidden-patterns.sh` (no `import` in
`Sources/`) and `Package.swift` (`dependencies: []`).

74
docs/rules-conformance.md Normal file
View File

@ -0,0 +1,74 @@
# Rules conformance
This records how PureYAML conforms to the vendored Swift rules canon
(`docs/rules-swift/`, mirror of `rules-swift`), including the few places where a
canon rule does not apply to a dependency-free YAML library or conflicts with a
project non-negotiable. Per `docs/rules-swift/core/rules.md`, conflicts are
resolved by scope: the condition under which each rule applies is named, so
exactly one governs any case.
## Conformant (applicable rules)
Every applicable rule passes its acceptance check. Mechanically verified:
`check-style`, `check-namespacing` (now namespace-aware, catches one-type-per-file
inside `extension` blocks), `check-forbidden-patterns`, `check-changelog-touched`,
`check-roadmap`, `swiftformat --lint`, `swiftlint --strict`, `swift build`,
`swift test` (277 tests). Concurrency, dependency-injection, cross-platform,
framework-policy, namespacing (anchor choice), folder-grouping, domain-first, and
the no-shortcuts mechanical tells are all clean.
## Conflict: documentation.md Rule 1 vs the dependency-free non-negotiable
`documentation.md` Rule 1 mandates a DocC catalog plus `swift-docc-plugin` as a
**package dependency**. PureYAML's binding non-negotiable (`CLAUDE.md`,
`AGENTS.md`) is **no external SwiftPM dependencies** (`dependencies: []`). Both
cannot hold. Resolution by scope: the dependency-free invariant is the
higher-stability project contract and wins. PureYAML is therefore exempt from
`documentation.md` Rule 1's plugin requirement. API documentation lives in
source `///` comments and `docs/`. If a maintainer later accepts a build-time
plugin dependency, this exemption is revisited.
## Not applicable: domain-mismatched canon files
Several canon files describe other domains (OpenAPIKit data parsing, an
After-Effects-style tool, app startup, UI). Their domain-specific clauses do not
bind a dependency-free YAML library; the transferable clauses are satisfied.
- `parsing-rules.md`: the CSV/XML/Excel/`Codable`-record and multi-encoding
`Data` clauses are N/A; the applicable parse-then-validate separation and
enum-with-associated-values clauses pass.
- `validation-rules.md`: the OpenAPI component-store / `$ref` / type-erasure and
"validate-or-exclude every public type" coverage-gate clauses are N/A (the
input model is the 5-case `Model.Value` enum, handled exhaustively, so no input
is silently dropped). The applicable rule/validator/issue-path clauses pass.
- `business-rules-and-constraints.md`: entirely N/A (no external services, auth,
PII, or compliance surface in an offline library).
- `ui/*`, `openapi-generated.md`, `exp/app-target.md`: N/A (no UI, not generated,
no app target).
## Conscious deviations (intent met another way)
- `core/round-trip-transformation.md` "by construction" (one `ParserPrinter`):
PureYAML keeps a hand-written parser and emitter rather than a single
invertible algebra, because a bijective ParserPrinter is not a faithful model
of full YAML (block/flow, anchors, tags, chomping, comments; surface-to-model
is many-to-one). The law it protects is enforced by a fixed-point gate:
`parse(dump(parse(x))) == parse(x)` over the corpus and generated documents,
plus the empty-collection adversarial regression. The round-trip law is stated
in `docs/DESIGN.md`.
- `code-style.md` file-naming: the repo uses folder-based bare names in some
folders (`Parsing/`) and dotted `<Namespace>.<Type>.swift` in others
(`Tagged/`, `Validation/`). Both are blessed patterns; the standing convention
is dotted for new namespaced files. A full rename to one pattern is deferred as
disproportionate churn against the highest-stability rename surface.
- `documentation.md` Rule 14 (`///` on every public decl): the non-trivial public
entry points are documented; the remaining gap is the MAY-skip categories
(`Codable` conformance members, `CustomStringConvertible.description`,
self-describing data fields). Tracked, not blanket-filled.
## Historical commits
A few commits that predate the commit-message hook have non-Conventional or
over-72-character subjects. They are already on `origin/main`; rewriting pushed
history is out of scope. The `commit-msg` hook now enforces Conventional Commits
and the 72-character subject limit going forward.