📖 [Docs]: YAML commands grouped by task with in-depth guides for each area - #53
Merged
Marius Storhaug (MariusStorhaug) merged 5 commits intoAug 2, 2026
Merged
Conversation
…ains Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…Engine group Helpers reachable from only one public domain move into that domain. Helpers reachable from more than one move into Engine, the shared YAML processing engine. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The pages carry the long-form usage guidance and a verified reference for the YAML to PowerShell object projection. Packaging tests now assert the domain layout and the presence of each group landing page. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…art page Long-form usage depth moved into the published group pages. The README keeps the overview, requirements, installation, the command table, a taster for each group, and the data model, conformance and compatibility sections. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Grouping the public commands moves their generated pages under the group folder, so every .LINK and every cross-group link now carries the group segment. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Member
Author
|
Checks note for reviewers: this pull request targets Auto-merge is deliberately not enabled. This branch is stacked on #47; if #47 squash-merges into |
Marius Storhaug (MariusStorhaug)
merged commit Aug 2, 2026
6ec4321
into
release/v1.0.0
53 checks passed
This was referenced Aug 2, 2026
Open
Marius Storhaug (MariusStorhaug)
added a commit
that referenced
this pull request
Aug 2, 2026
…and suite (#47) `Yaml` v1.0.0 is the first functional release of the `Yaml` PowerShell module on the PowerShell Gallery, delivering a complete YAML 1.2.2 command suite for parsing, validating, formatting, merging, reading, and writing. ## New: Complete YAML 1.2.2 command suite The module provides end-to-end YAML handling, grouped by the task you are doing. | Group | Command | User outcome | | --- | --- | --- | | Conversion | `ConvertFrom-Yaml` | Parse YAML text into PowerShell objects | | Conversion | `ConvertTo-Yaml` | Serialize PowerShell objects into YAML text | | Files | `Import-Yaml` | Read YAML files directly into PowerShell objects | | Files | `Export-Yaml` | Write PowerShell objects directly to YAML files | | Streams | `Test-Yaml` | Validate YAML syntax and structure without object construction | | Streams | `Format-Yaml` | Normalize YAML text with representation-preserving, idempotent output | | Streams | `Merge-Yaml` | Deep-merge two or more YAML streams with configurable sequence, conflict, and null policies | ## New: Task-based documentation with a full object-model reference Every command group ships an in-depth guide that is published as its own documentation section: Conversion, Files, and Streams. The Conversion guide includes a complete YAML-to-PowerShell object model reference, so you can predict the exact shape a document produces before running anything — scalar tag resolution to `String`, `Boolean`, `Int32`/`Int64`/`BigInteger`, `Decimal`/`Double`, `DateTime`/`DateTimeOffset` and `Byte[]`; `PSCustomObject` note-properties versus `-AsHashtable` ordered dictionaries; `-NoEnumerate`; multi-document streams; anchor and alias identity; the `!!set`, `!!omap`, `!!pairs` and `!!binary` collection tags; and the cases that deliberately fail rather than silently lose data. ## New: Native YAML 1.2.2 engine with no third-party dependencies Parsing and serialization are implemented entirely in PowerShell — no YamlDotNet or other runtime dependency. The engine enforces bounded parse, clone, merge, and emit work budgets, avoids arbitrary .NET type activation, and uses iterative graph traversal to stay stable on large or adversarial inputs. Duplicate YAML mapping keys are rejected by design to prevent ambiguous data projection. ## New: YAML 1.2.2 conformance across the pipeline The scanner, parser, constructor, serializer, and emitter are validated against the YAML 1.2.2 specification, including edge cases around BOM handling, NBSP behavior, flow scalars, and tag decoding. --- <details> <summary>Technical details</summary> - Consolidates the full YAML draft-stack delivery into `release/v1.0.0`, including prior preparatory PR slices (#37, #39, #40, #41, #42, #45) and the documentation/layout slice #53. The entry-removal slice (#46) was reverted out of this branch before release, so `Remove-YamlEntry` is not part of the shipped surface. - Implements a fully owned PowerShell YAML 1.2.2 pipeline (scanner -> parser/composer -> constructor -> projector -> serializer -> emitter) with no YamlDotNet dependency. - Conformance baseline uses the 402-input `yaml-test-suite` corpus plus chapter 2 fixtures under `tests/fixtures/yaml-spec-1.2.2/chapter-02/`. - `Major` is correct on the merits and not only as release framing: the diff deletes `src/functions/public/Get-PSModuleTest.ps1`, and removing a previously exported command is a breaking change under [PSModule Versioning](https://psmodule.io/Modules/Versioning/). The `Major` label also cuts `v1.0.0` for the module go-live. - Two scalar defects found during the final review pass were corrected before release: folded block scalars with keep chomping (`>+`) dropped the `b-chomped-last` feed and could not round-trip trailing empty lines, and double-quoted scalars folded the `l-empty` lines after an escaped break into a space instead of emitting line feeds. Both have regression coverage in `tests/ConvertFrom-Yaml.Tests.ps1`. - `src/manifest.psd1` is removed. Build-PSModule derives `PowerShellVersion` and `CompatiblePSEditions` from the `#Requires` statements it finds in the built root module and overwrites whatever a source manifest declared, and it builds an empty manifest when none exists. The two keys were dead weight duplicating `src/header.ps1`. The built artifact is unchanged, and `tests/Packaging.Tests.ps1` still asserts `7.6`/`Core` directly on the generated manifest. - Source layout follows PSModule domain grouping: public commands live under `src/functions/public/{Conversion,Files,Streams}/`, private helpers mirror those domains with a shared `Engine` group for helpers reachable from more than one public domain, and each public group carries a `<Group>.md` overview that Document-PSModule publishes as that section's landing page. `Streams` is a behavioural domain rather than a catch-all: `Test-Yaml`, `Format-Yaml` and `Merge-Yaml` all operate on YAML text at the representation level and never project to PowerShell objects. Grouping moves the generated command pages under their group, so all 98 `.LINK` values were repointed to the canonical grouped URLs. ### Validation - The full `Process-PSModule` v6.1.15 pipeline is green on `6ec4321` ([run 30757319554](https://github.com/PSModule/Yaml/actions/runs/30757319554)): plan, build module, build docs, build site, repository lint, source-code lint and tests on Linux/Windows/macOS, module lint and test on Linux/Windows/macOS, test results, and code coverage. 66 checks, 0 failures. - Per-command Pester suites run on Linux, Windows, and macOS: `ConvertFrom-Yaml`, `ConvertTo-Yaml`, `Test-Yaml`, `Import-Yaml`, `Export-Yaml`, `Format-Yaml`, and `Merge-Yaml`, plus `Specification`, `Conformance`, and `Packaging`. - `tests/Conformance.Tests.ps1` runs the pinned, hash-verified `yaml-test-suite` release archive: 402 inputs, 400 syntax passes, 0 failures, and exactly 2 documented policy differences (`2JQS`, `X38W`) that come from rejecting duplicate mapping keys — the stricter, safer behavior this release ships. Both cases are genuine key collisions: `2JQS` repeats the empty key, and `X38W` uses an alias as a second key that resolves to the same node as the first. YAML 1.2.2 §3.2.1.1 requires mapping keys to be unique and §3.3.2 lists duplicate keys as a legitimate failure point, so rejecting them is intentional and is not tracked as a defect. - `tests/Specification.Tests.ps1` covers YAML 1.2.2 chapter-2 examples 2.01 through 2.28. - `tests/Packaging.Tests.ps1` asserts the shipped package carries no `YamlDotNet` or third-party license artifacts, that the generated manifest declares `PowerShellVersion = '7.6'` and `CompatiblePSEditions = @('Core')`, that the workflow stays pinned to Process-PSModule v6.1.15, and that every function file sits under exactly one domain folder with a `<Group>.md` present for each public group. - The object-model reference in the Conversion guide was verified by executing every documented mapping against the built command surface rather than asserted from the source, including the widening boundaries between `Int32`, `Int64` and `BigInteger`, `Decimal` versus `Double` selection, the YAML 1.1-only forms that intentionally stay strings, timestamp `Kind`/offset behavior, and the `YamlInvalidTaggedScalar` failure for a standard tag that does not match its text. - Automated review note: GitHub Copilot declined to review this pull request three times because the diff exceeds its 20,000-line limit. A compensating automated code review was run over the shipped surface (`src/**`, `.github/workflows/**`, `.github/PSModule.yml`) with a differential harness against a reference parser. It found the two scalar defects listed above, which are fixed here. A third reported divergence was investigated, prototyped, and disproven — the reference parser disagrees with the official corpus there, and the module matches the corpus (see #52). Resource guards, tag handling, atomic file writes, BOM sniffing, and emitter idempotency were all clean. ### Standards and framework alignment pass (Pass A) | Changed surface | Standards checked | Framework docs checked | Result | | --- | --- | --- | --- | | `src/functions/public/{Conversion,Files,Streams}/**`, `src/functions/private/**` | MSX Coding Standards: PowerShell/Functions, Naming | PSModule Standards (group by domain, mirror public and private domains, group documentation pages with source) | Fixed in this PR via #53 | | `src/header.ps1` (source of the runtime constraint) | MSX Coding Standards: PowerShell/Version-Constraints | PSModule Standards, Process-PSModule module anatomy | Aligned — `#Requires -Version 7.6`, `#Requires -PSEdition Core`; redundant `src/manifest.psd1` removed | | `tests/**` | MSX Coding Standards: PowerShell/Testing | PSModule Standards, PSModule Test-Specification, Process-PSModule build-test-pack-publish | Fixed in this PR | | `.github/workflows/**`, `.github/PSModule.yml`, `.github/linters/**` | MSX Coding Standards: GitHub Actions | Process-PSModule repository structure and build-test-pack-publish | Aligned | | `README.md`, `src/functions/public/*/*.md`, `examples/**` | MSX Coding Standards: Documentation | PSModule Repository-Defaults (relocate depth only to a published home; do not duplicate generated command documentation) | Fixed in this PR via #53 | | Repository baseline files (`CONTRIBUTING.md`, `SECURITY.md`, `SUPPORT.md`, `CODE_OF_CONDUCT.md`, `AGENTS.md`, `CLAUDE.md`) | MSX Ways of Working: Repository Standard | PSModule Repository-Defaults (required common files, managed distribution guidance) | Fixed in this PR. `.github/pull_request_template.md` and `.github/copilot-instructions.md` are Distributor-managed shared files and are deliberately not hand-authored in this repository. | ### Issue convergence sweep (Pass B) - Sweep scope: existing PR-linked candidates (`#2, #4, #5, #7, #8, #20, #21, #22, #24, #25, #26, #27, #29, #43, #44`) plus targeted open-issue searches by command names, YAML chapter-2/conformance terms, and representation-preserving file and merge terms. `#50` was added after its delivery slice (#53) merged into this branch. - Dedupe note: `#1` is a pull request, not an issue leaf, and `#3` is already closed historical work. #### Convergence evidence by closed issue - `#2` — core conversion surface delivered via `ConvertFrom-Yaml` and `ConvertTo-Yaml`, with coverage in `tests/ConvertFrom-Yaml.Tests.ps1` and `tests/ConvertTo-Yaml.Tests.ps1`. - `#4` — `Test-Yaml` delivered and covered in `tests/Test-Yaml.Tests.ps1`. - `#5` — deterministic stream formatting delivered via `Format-Yaml`, covered in `tests/Format-Yaml.Tests.ps1`. - `#7` and `#27` — flow sequence/mapping parsing delivered and covered in parser behavior tests and `tests/Specification.Tests.ps1` chapter-2 examples. - `#8` — multiline literal/folded scalar support delivered and covered in `tests/ConvertFrom-Yaml.Tests.ps1` and chapter-2 examples. - `#20` — chapter-2 compliance harness delivered in `tests/Specification.Tests.ps1` (examples 2.01 through 2.28). - `#21` — multi-document stream support delivered and validated in chapter-2 stream examples and command tests. - `#22` — anchors/aliases support delivered and verified in `tests/ConvertFrom-Yaml.Tests.ps1` and merge behavior suites. - `#24` — explicit tag and `!!` handling delivered and covered across conversion/specification tests. - `#25` — complex mapping key handling delivered (including hashtable-safe paths) and covered in conversion/specification tests. - `#26` — core-schema numeric forms and special floats delivered and covered in `tests/ConvertFrom-Yaml.Tests.ps1`, `tests/ConvertTo-Yaml.Tests.ps1`, and `tests/Specification.Tests.ps1`. - `#29` — file I/O commands delivered via `Import-Yaml` and `Export-Yaml`, covered in `tests/Import-Yaml.Tests.ps1` and `tests/Export-Yaml.Tests.ps1`. - `#43` — representation-preserving merge delivered via `Merge-Yaml`, covered in `tests/Merge-Yaml.Tests.ps1`. - `#50` — domain grouping delivered via #53, which merged into this branch: public commands grouped into `Conversion`, `Files` and `Streams`, private helpers mirrored into those domains plus a shared `Engine` group, and a `<Group>.md` overview page beside each public group. Enforced by new layout assertions in `tests/Packaging.Tests.ps1`. The closing keyword lives here because closing keywords only fire on merge to the default branch. #### Deferred (not delivered in this release) - `#44` — representation-preserving entry removal is intentionally out of scope for the v1.0.0 module go-live. The module surface is parse/serialize, validate, import/export, format, and merge; entry removal remains open for a future release and is kept as a non-closing reference below. - `#54` — the `Process-PSModule` workflow only triggers on pull requests targeting `main`, so pull requests stacked on a release branch run no checks and are not gated by the `main` ruleset. Found while stacking #53 on this branch; #53 was instead validated by an explicit workflow dispatch and then by this pull request's own run after merging. Fixing the trigger is repository plumbing, not release content. </details> <details> <summary>Relevant issues (or links)</summary> - Fixes #2 - Closes #4 - Closes #5 - Closes #7 - Closes #8 - Closes #20 - Closes #21 - Closes #22 - Closes #24 - Closes #25 - Closes #26 - Closes #27 - Closes #29 - Closes #43 - Closes #50 - #44 (deferred: representation-preserving entry removal is out of scope for the v1.0.0 module go-live and is not delivered in this diff) - #54 (deferred: release-stacked pull requests run no CI; repository plumbing, not release content) - #23 (partially convergent: explicit `!!timestamp` handling is delivered; implicit core-schema timestamp resolution remains open by design) - #28 (not convergent: comment-preserving `ConvertFrom-Yaml | ConvertTo-Yaml` round-trip is not delivered in this diff) - #30 (partially convergent: parser/emitter hardening and budget controls shipped, but full operator-to-.NET substitution scope remains open) - #52 (closed as not a defect while validating this release; the module matches the official corpus and the reference parser is the outlier) </details> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
Yamlmodule's commands are now organised by what you are trying to do — convert values, work with files, or work with YAML streams — and each area has its own in-depth guide on the documentation site. The guides carry the long-form material that previously only existed in the repository README, plus a new reference that shows exactly what a parsed YAML document becomes in PowerShell. Nothing about how the commands behave has changed.New: Task-oriented guides for each command area
The documentation site now has three landing pages, one per area:
ConvertFrom-YamlandConvertTo-Yaml. Moving data between YAML text and PowerShell values.Import-YamlandExport-Yaml. Path resolution, strict decoding, and atomic writes on top of conversion.Test-Yaml,Format-YamlandMerge-Yaml. Working on YAML text at the representation level, never projecting to PowerShell objects.Each page explains how that area works, when to reach for each command, and shows runnable examples.
New: Reference for the YAML to PowerShell projection
The Conversion guide answers the question "what will this document actually look like once I parse it?". It documents which scalar forms produce
string,bool,Int32/Int64/BigInteger,Decimal/Double,$nulland timestamps; which YAML 1.1-only forms such asyes,0b1010and bare dates deliberately stay strings; how mappings project toPSCustomObjectversus-AsHashtableordered dictionaries that can hold complex, null and numeric keys; how sequences and-NoEnumerateinteract; how multi-document streams emit one object per document; how anchors and aliases preserve object identity, including recursive nodes; what!!set,!!omap,!!pairsand!!binaryproduce; and the four cases that deliberately fail rather than silently lose data.A worked example shows a representative document alongside a diagram of the resulting PowerShell shape, so the types, nesting and shared references can be read off directly.
Changed: The README is a landing page again
The README keeps the overview, requirements, installation, the full command table, and a short taster for each area, then links onward to the guides. The long-form depth moved into the guides rather than being dropped.
Changed: Command documentation URLs now include the group
Because each command's generated page now lives under its group, the published address of a command page gains the group segment — for example
psmodule.io/Yaml/Functions/Conversion/ConvertFrom-Yaml/. EveryGet-Help -Onlinelink and every in-repo cross-reference was updated to match.Technical details
Purely organisational. No function body, parameter, or behaviour change: every source move is a pure
git mv—git statusreported 98 renames with zero content edits — and the only later source edits are single-line.LINKcomment updates.Public grouping (
src/functions/public/):ConversionConvertFrom-Yaml,ConvertTo-YamlFilesImport-Yaml,Export-YamlStreamsTest-Yaml,Format-Yaml,Merge-YamlStreamsis a behavioural domain, not a catch-all:Test-Yaml,Format-YamlandMerge-Yamlall operate on YAML text at the representation level and never project to PowerShell objects, which is exactly what separates them fromConversion.Private grouping (
src/functions/private/, 91 files). Assignment was mechanical, not by taste: a reachability graph was computed from each public command through private call references, and each helper was placed in the single public domain that reaches it, or in a shared group when more than one domain reaches it.ConversionConvertFrom-Yaml/ConvertTo-Yaml— the PowerShell object projector and serialization-graph helpersFilesImport-Yaml/Export-Yaml—Get-YamlTextEncodingStreamsFormat-Yaml/Merge-Yaml— the merge engine and representation-text emitterEngineEnginewas chosen overCorebecause the standard asks for resource or behaviour groups.Corenames a position in the dependency tree;Enginenames what the files are — the shared YAML processing engine of scanner, block and flow readers, composer, tag resolution, emitter primitives, diagnostics and resource limits. Only one shared group was introduced; no verb folders, no sub-grouping below the domain level.Documentation mechanism.
Document-PSModule'sBuild-PSModuleDocumentationpublishessrc/functions/public/<Group>/<Group>.mdas that section'sindex.md, and relocates each generated command page to mirror the source layout. Command reference pages are generated separately from comment-based help, so the group pages are narrative only and contain no generated parameter tables, per the README guidance inRepository-Standard.md.That relocation is why every canonical link had to move too. The framework's own
PublicHelpLinksource-code test caught this on the first CI run: it expectshttps://psmodule.io/Yaml/Functions/<Group>/<Name>/. All 98.LINKvalues (7 public, 91 private helpers pointing at the public command they serve) were updated by scripted replacement, and cross-group links inside the three group pages use the same absolute published URLs rather than relative paths, because a relative.mdlink would not survive theindex.mdrename on the site.README relocation. Moved out and absorbed:
## Parse YAMLand## Serialize PowerShell valuesintoConversion.md;## Import YAML filesand## Export YAML filesintoFiles.md;## Format YAML streams,## Merge YAML streamsand## Validate YAMLintoStreams.md. Kept in the README: the overview and layering paragraph, requirements, installation, the full command table (now grouped and linked), a short taster per group, and## Data model and safety,## Conformance corpusand## Compatibility boundariesin full — those are repository-level compatibility claims about the artifact rather than task guidance, so they belong on the landing page. Nothing was deleted.Verification of the new reference. Every factual claim in
Conversion.mdwas executed against a module composed from this branch's source, not inferred from reading. That includes the scalar type table,Int32/Int64/BigIntegerwidening,DecimalversusDoubleselection,DateTime(Kind = Utc) versusDateTimeOffsetfor!!timestamp,[System.DBNull]::Valuefor null dictionary keys,ReferenceEqualsidentity through aliases,!!set/!!omap/!!pairs/!!binaryprojections, the pipeline record counts for-NoEnumerate, and the four error IDsYamlMappingKeyNotString,YamlPropertyNameCollision,YamlPropertyNameReservedandYamlDuplicateKey. Three drafting errors were caught this way and corrected: a wrong dictionary entry count, anExport-Yaml -LiteralPathexample for a parameter that does not exist, and a$config.newKey = …example that aPSCustomObjectrejects.Tests changed.
tests/Packaging.Tests.ps1,'keeps the owned processor layers explicit and source-level'— the ten expected private paths now carry their domain folder (Engine\,Conversion\,Streams\). Two tests were added so the layout itself is enforced rather than only documented:'groups every function file under a domain folder'(no loose.ps1directly underpublic/private, and exactly one folder level) and'ships a group overview page beside every public domain'(each public group has its<Group>.md). The wholetests/tree was grepped for other hardcodedfunctions\publicorfunctions\privatepaths; there were none.Validation. Baseline on this branch point was 392 passed / 0 failed / 4 skipped across the nine functional suites; after the reorganisation the same nine suites report 392 / 0 / 4.
Invoke-ScriptAnalyzerwith.github/linters/.powershell-psscriptanalyzer.psd1is clean on the changed test file.markdownlint-cli2,textlint(withtextlint-rule-terminology) andcodespellare clean on the four markdown files using the repository's own linter configs.Process-PSModuleonly triggers on pull requests whose base ismain, so this stacked pull request gets no automatic checks. It was validated by dispatching the full workflow against the branch: run30756441401is green end to end — Build-Module, Build-Docs, Lint-SourceCode, Test-SourceCode, Test-Module and every Test-ModuleLocal suite on Linux, macOS and Windows. OnlyPublish-SiteandPublish-Moduleare skipped, as expected offmain. The absence of PR checks on release-stacked branches is a workflow-trigger gap, not a gap in this change.No
src/manifest.psd1was added;src/header.ps1remains the source of truth for#Requires -Version 7.6and#Requires -PSEdition Core.Change type:
Docs. The only user-visible outcome is documentation; the source reorganisation is internal upkeep with no shipped behaviour change.DocsandMaintenanceboth map toNoRelease, so the release impact is identical either way. The repository had noNoReleaselabel, so it was created to match the rest of the PSModule organisation.Implementation plan progress: completes all four scope items in #50.
src/functions/**(PowerShell)Test-PSModulePublicHelpLinkcontractsrc/functions/public/*/*.md(docs)Build-PSModuleDocumentationsection-index mechanismREADME.mdtests/**Issue convergence sweep: scoped to open PSModule/Yaml issues touching module source layout and documentation structure. Only #50 is fully satisfied by this diff. #47 is the release pull request this one stacks on and is linked as context only.
Relevant issues (or links)