diff --git a/docs/roadmap/configkit.md b/docs/roadmap/configkit.md index d8ed11c2..9685fd87 100644 --- a/docs/roadmap/configkit.md +++ b/docs/roadmap/configkit.md @@ -7,27 +7,26 @@ default application, and configuration-inspection machinery with ConfigKit, while preserving Notarius ownership of pipeline semantics, module catalogs, runtime resolution, and invocation behavior. -This roadmap is based on ConfigKit v0.2.0, the current stable release in the -local `../ConfigKit` checkout. ConfigKit's maintained consumer guide includes a -Notarius-shaped example, and its complete test suite passes locally. The -dependency starts at v0.2.0; completing source-aware normalization requires a -reviewed release with the extension below. Do not follow an unreviewed branch -tip. Ordered implementation work belongs in +This roadmap targets ConfigKit v0.3.0. Its released source-origin APIs support +source-aware normalization within one load, removing the prior upstream +dependency gate. Ordered implementation work belongs in [the implementation plan](implementation.md). -### Source Metadata Dependency +### Released Source Metadata Support -Review for implementation identified an API gap in v0.2.0: `Resolution` -exposes input presence and change recording, but not declaration origins or -source paths. Those are available only on the completed `Result`, whose -effective and explanation snapshots are already frozen. The target of -recording declaration-relative path normalization in those snapshots therefore -requires an upstream extension. The selected approach is to request source -origins and canonical source records during resolution, then adopt a reviewed -release providing that API. The implementation plan records the dependency -gate. Resolving paths only after loading with a separate inspection report was -considered but not selected; the agreed effective/explanation contract remains -the target. ConfigKit v0.2.0 alone is insufficient for that part of the target. +Use `Resolution.InputInfo(path) (FieldInfo, bool)` to query the fixed decoded +entry state, and `Resolution.Source(id) (SourceInfo, bool)` to retrieve an +already-acquired source's canonical file path. These callback-scoped queries +return detached records and preserve original input attribution despite +consumer normalization or map-key moves. Notarius records resolved values +through the existing normalization API before ConfigKit freezes inspection +output; no post-load path conversion or second load is needed. + +The v0.3.0 public-facade refactor preserves consumer signatures and introduces +no additional migration. Use only the root public package. Go 1.25 remains +supported; the private YAML v4 prerelease dependency is unchanged. ConfigKit's +macOS support is now best-effort, consistent with Notarius's platform policy. +No application platform expansion or new release gate is required. ## Motivation @@ -119,7 +118,7 @@ requirements defined by the architecture and current integration contracts. - the meaning and validation of every Notarius field; - pipeline, module, validator, artifact, and reference catalogs; - conversion from configuration-facing values to runtime pipeline types; -- source-relative interpretation of configured external reference paths; +- source-relative interpretation of configured filesystem paths; - run-time selectors and actions such as lane selection, input, resume, recomputation, session identity, and CLI reference changes; - PromptKit profile discovery and validation; and @@ -142,7 +141,7 @@ Use the following root declaration: version: 5 ``` -ConfigKit v0.2.0 accepts string or strict decimal-integer input at the exact +ConfigKit v0.3.0 accepts string or strict decimal-integer input at the exact path selected by its `VersionRule`. It passes the original spelling to the application checker and can decode an accepted integer into a string-backed Go field. This narrow exception lets Notarius retain its existing numeric YAML @@ -295,20 +294,38 @@ to Notarius input, reference, output, cache, debug, or PromptKit profile paths. ### Paths And Source Ownership -Resolve relative external reference paths and `promptkit.profile_file` and -`promptkit.profile_dir` relative to the file that supplied the surviving value. -This deliberately changes PromptKit profile-source paths from their current -working-directory convention in version 5. Absolute paths remain absolute. -Keep output, cache, and debug relative-directory semantics unchanged; they are -runtime destinations rather than files bundled with configuration fragments. +Resolve every nonempty relative application filesystem path authored in +configuration against the canonical directory of the file supplying the +surviving value. This includes external references, `promptkit.profile_file`, +`promptkit.profile_dir`, and output, cache, and debug directories. Absolute +paths remain unchanged. This deliberately changes configured PromptKit sources +and runtime destinations from working-directory-relative behavior in version 5. -Use ConfigKit's source metadata to identify each surviving declaration's -origin, including profile replacements and values inherited from the base. -Record path normalization so effective output and explanation reflect the -paths used at runtime. Preserve attribution when identifier normalization -renames an enclosing map key. Do not resolve overwritten or removed path -values or infer source ownership from diagnostic display labels. Explicit CLI -paths retain their working-directory interpretation. +File-authored profile replacements use the profile file's directory, including +equal-valued replacements. Inherited values keep their base-file origin. +Built-in relative defaults have no declaring file and use the canonical main +configuration directory. Explicit CLI-relative paths use the invocation's +working directory, including output/debug overrides submitted through ConfigKit. +CLI references remain invocation-owned. Empty optional cache paths keep their +existing disabled/automatic meaning; do not turn an empty path into a directory. + +Use `InputInfo` at original scalar paths and `Source` for their source IDs, +not composite parent origins or diagnostic display labels. Preserve original +path mappings when identifiers are normalized. Record normalization so runtime, +effective output, and explanation agree; do not resolve overwritten values. +Canonical target-file directories, not symlink alias directories, supply bases. + +Notarius's file-backed loader must supply the canonical main directory and the +invocation directory where needed. If a nonempty relative value unexpectedly +lacks the provenance needed to choose its base, report a safe field diagnostic +and fail loading rather than silently choose a different directory. This is a +correctness failure, not an added restriction on operators' valid paths. Do not +copy the upstream example's optional warning-and-leave-relative fallback. + +Application paths may be absolute or contain `../`; do not add confinement, +target-existence checks, or new symlink restrictions during normalization. +ConfigKit's own import and profile-source rules remain unchanged. Module-owned +dynamic options are not automatically interpreted as filesystem paths. ## Discovery, Precedence, And Overrides @@ -422,8 +439,8 @@ configuration or a new durable JSON API. There is no unredacted mode. The implementation must address the following coherent work areas: -- add and pin a reviewed `gitea.maximumdirect.net/eric/ConfigKit` release with - v0.2.0's semantics and the required source-origin API; +- pin `gitea.maximumdirect.net/eric/ConfigKit v0.3.0` and use its public + source-origin APIs; - introduce the version 5 typed document and its ConfigKit definition; - replace `LoadFileConfig`, custom `UnmarshalYAML` implementations, manual strict-parser checks, presence flags, `ApplyFileConfig`, and redundant @@ -434,7 +451,7 @@ The implementation must address the following coherent work areas: discovery; - enable ConfigKit imports and configuration profiles, wire `--config-profile` through all configuration-loading commands, and retain declaration origins - for relative file paths; + for configured filesystem paths; - retire field-level environment overlays while retaining `NOTARIUS_CONFIG`; - classify current run flags so only true configuration overrides use ConfigKit override records and invocation-only state remains outside the @@ -458,8 +475,10 @@ migration section with a concise version 4-to-5 migration guide covering: - expanding scalar module bindings into `{module: ...}` objects; - expanding scalar external references into `{path: ...}` objects; - moving lane-level reference aliases to `extract.references`; -- resolving configured PromptKit profile-source paths from their declaring - file instead of the working directory; +- resolving configured PromptKit profile-source and output/cache/debug paths + from their declaring file instead of the working directory, and built-in + relative defaults from the main configuration directory; CLI-relative paths + retain their invocation-directory base; - using imports for whole pipeline definitions and configuration profiles for overlays, including complete replacement of step sequences; - replacing retired operational environment overrides with YAML or supported @@ -517,9 +536,10 @@ Required Notarius coverage includes: and profile precedence through the real ConfigKit loader; - profile sequence replacement and explicit clearing, so Notarius does not accidentally append or merge ordered steps; -- relative reference and PromptKit profile-source paths from root files, - imported fragments, and overlays, including an unchanged inherited value, - a replaced value, and an enclosing normalized identifier; +- relative references, PromptKit sources, and destination paths from root files, + imported fragments, and overlays, including inherited and equal-valued replaced + values, normalized enclosing identifiers, default bases, CLI bases, and empty + optional cache settings; - matching configuration-profile selection across execution, validation, pipeline listing, effective output, and explanation, without provider calls; - checked dynamic integer conversion at each production module option boundary; @@ -572,9 +592,8 @@ profile validation, as documented today; no live provider call is required. ## Acceptance Criteria -- All configuration-loading commands use a pinned ConfigKit release providing - the required source-origin API, building on v0.2.0, and no production code - directly imports a YAML parser. +- All configuration-loading commands use ConfigKit v0.3.0 through its public + package, and no production code directly imports a YAML parser. - Version 5 is the only accepted file contract, documents `version: 5` as its canonical spelling, and has one canonical object representation for module bindings and references. @@ -592,9 +611,9 @@ profile validation, as documented today; no live provider call is required. - Imports compose whole pipeline definitions and selected configuration profiles apply ConfigKit's documented overlay semantics consistently across all configuration-loading commands. -- Relative external references and PromptKit profile-source paths use the - surviving declaration's source directory, and safe inspection agrees with - the paths used at runtime. +- Configured relative filesystem paths use the surviving declaration's canonical + directory; built-in defaults use the main configuration directory and CLI + values use the invocation directory. Safe inspection agrees with runtime. - The resolved pipeline remains detached, reference content remains excluded from ordinary summaries, and its identity changes under the same semantic conditions as before. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 3513b43a..69dccd90 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -16,12 +16,10 @@ preserve the requested flexibility, and do not add application restrictions under the guise of this migration. Tests protect behavior, not exact helper structure, incidental formatting, or a duplicated implementation of ConfigKit. -The selected approach requires an upstream extension: ConfigKit v0.2.0 cannot -expose source origins during its resolution callback. See **Upstream -Prerequisite**. Stages 1–6 are independent of that missing capability. Stage 7 -and all later stages have an explicit gate; the sprint is not ready for -unattended execution through that gate until the upstream release is available. -Do not mark a gated stage complete by weakening the feature contract. +ConfigKit v0.3.0 supplies the required source-origin APIs. Pin that release from +Stage 1; there is no remaining upstream gate. Stage 7 uses the released +`Resolution.InputInfo` and `Resolution.Source` methods. Keep the fourteen +stages ordered and bounded as below. Temporary coexistence of the old production loader and the new loader during these stages is implementation scaffolding only. There is no supported dual @@ -32,7 +30,8 @@ final stage establishes repository-wide readiness. ## Repository Baseline Planning examined Notarius at `946fa10` plus the outstanding policy and roadmap -changes. The sibling ConfigKit checkout is tagged `v0.2.0`. +changes. The dependency review now uses the sibling ConfigKit checkout tagged +`v0.3.0`, including its release guide and source-aware consumer example. Relevant current owners are: @@ -47,7 +46,7 @@ Relevant current owners are: | Debug configuration and resolved-pipeline output | `internal/core/config/redaction.go`, `internal/core/debugbundle/summary.go`, and the CLI debug handoff | | Maintained examples | `examples/dnd-minimal.config.yml`, `examples/dnd-complete.config.yml`, `examples/profiles/dnd-extraction.yml`, and CLI production contract tests | | Direct YAML test dependency | `internal/cli/production_contract_test.go` also parses embedded prompt definitions using `yaml.v3` | -| Upstream contract | ConfigKit `docs/config.md`, `docs/consumers/README.md`, `load.go`, `resolution.go`, `metadata.go`, `records.go`, `path.go`, and consumer examples at v0.2.0 | +| Upstream contract | ConfigKit `docs/config.md`, `docs/consumers/README.md`, public `load.go`, `lifecycle.go`, `metadata.go`, `records.go`, `path.go`, and consumer examples at v0.3.0 | The sibling checkout is reference material, not a production dependency path. Do not commit a local `replace`, modify upstream, or invent a future API as part @@ -57,9 +56,8 @@ of these Notarius stages. ### Configuration And Dependency Boundary -- Initially pin `gitea.maximumdirect.net/eric/ConfigKit v0.2.0`. Stage 7 may - require a reviewed release providing the agreed upstream extension. A later - pin must be explicit, never an unreviewed `main` revision. +- Pin `gitea.maximumdirect.net/eric/ConfigKit v0.3.0`. Use the root public + package, never its private engine or an unreviewed `main` revision. - Use a string-backed document version and a `/version` rule accepting exactly text `5`; both numeric `version: 5` and quoted `version: "5"` are accepted. Maintained examples use the numeric form. Reject missing/null/unsupported @@ -135,14 +133,18 @@ rules; empty and exact `default` select no overlay. Do not invent stricter name validation or whitespace normalization than the upstream profile contract. Enable imports without a separate Notarius enablement flag. The canonical -selected file's directory is the configuration root. Relative file references -and PromptKit profile sources use the surviving declaration's directory; -absolute values remain absolute. Resolve these to absolute paths before -runtime conversion, without reading the referenced contents. Output/cache/debug -relative directories retain working-directory semantics. CLI reference paths -retain the existing working-directory semantics and generated-reference +selected file's directory is the configuration root. Every nonempty configured +relative filesystem path (external references, PromptKit profile sources, and +output/cache/debug directories) uses the surviving declaration's canonical +directory. Built-in relative defaults use the main configuration directory; +explicit CLI values use the invocation directory. Absolute values remain +unchanged. Empty optional cache paths retain their existing meaning. + +Resolve paths before runtime conversion without reading target contents. +CLI references retain their existing runtime handling and generated-reference protection. Configuration-root confinement applies only to ConfigKit's imported -configuration and overlay files. +configuration and overlay files, not application paths. Do not infer path +semantics for arbitrary dynamic module options. ## Stage 1 — Add ConfigKit And The Version 5 Document Types @@ -150,7 +152,7 @@ configuration and overlay files. **Work:** -1. Pin v0.2.0 in `go.mod`/`go.sum` and inspect its actual exported API. +1. Pin v0.3.0 in `go.mod`/`go.sum` and inspect its actual exported API. 2. Add `document.go` and `load.go` in `internal/core/config` (or equivalent cohesive files) with the complete plain document types, finite validator type, explicit reference union, and YAML-tagged validation-policy fields. @@ -163,7 +165,8 @@ configuration and overlay files. scaffolding; do not advertise partially implemented v5 support. 5. Add the next available ADR (expected `0018-adopt-configkit.md`) recording the accepted ownership, v5 break, imports/profiles, and environment policy. - Record the source-origin dependency without claiming it is implemented. + Record application path-base policy and the released source-origin APIs; + do not claim the Notarius migration is implemented merely because upstream is. **Acceptance:** v5 numeric/quoted versions decode; unsupported versions fail before document decoding; representative canonical bindings and references @@ -191,7 +194,8 @@ small; generic YAML exhaustiveness remains upstream. and required fields in a present local-backend object. Preserve mutually exclusive profile sources and the existing endpoint/range constraints. 4. Keep filesystem checks and PromptKit profile catalog lookup out of callbacks. - This stage trims profile-source paths but does not yet resolve their bases. + This stage trims filesystem paths but does not yet resolve their bases; + Stage 7 owns all source-aware path normalization. 5. Extract small reusable value checks where needed so runtime validation and document validation do not separately implement the same rule. Collect independent errors through ConfigKit with safe messages and field paths. @@ -343,46 +347,58 @@ Test rendering failure and caller mutation at these real local boundaries. **Goal:** Normalize file paths using their effective source origins and retain correct ConfigKit inspection history. -**Prerequisite gate:** A reviewed ConfigKit release must expose surviving input origin and canonical -source information during resolution. Confirm the actual API and pin that -release before implementing this stage. If only v0.2.0 is available, stop here -and report the unmet dependency. Do not implement an upstream fork, reload the -configuration twice, parse YAML independently, or silently change the promised -effective output. +**Work:** -**Work after the gate:** - -1. Resolve path origins at original input paths before map-key normalization - and before recording changes can replace a source origin. Use the scalar - origin and source ID to get the canonical declaring file; do not pick the - first origin of a composite parent mapping. -2. Join nonempty relative external reference paths and PromptKit profile-source - paths to that file's directory. Preserve absolute values. Do not read or - require existence of referenced files in this callback. -3. Record normalized paths and map moves so the frozen effective view matches - runtime values and explanation can trace back to the original declaration. - Keep map collision checks ahead of mutation; process stable sorted keys. -4. Leave destination directories unchanged except existing whitespace - normalization. CLI reference paths remain runtime-owned. No configuration - root confinement or symlink policy is added to referenced application files. -5. Enable imports and selected profiles on the public new loader, using - `File` and the selected file's canonical directory as the root. Use - ConfigKit's composition and profile behavior directly. +1. Query `resolution.InputInfo(originalPath)` for each path-bearing scalar. + It returns `(FieldInfo, bool)` from fixed entry-state metadata, unaffected by + earlier normalization, additions, removals, or moves. Use Stage 3's original + path mapping for renamed keys. Read `Origin.Layer` and the exact scalar's + `Origin.Locations`; use `resolution.Source(location.SourceID)` to obtain + `(SourceInfo, bool)` and its canonical `Path`. Do not select an arbitrary + origin of a composite parent. +2. Implement one narrow application path-base helper shared by typed traversals. + Base/profile file values use the declaring file directory; default relative + values use the canonical main directory; CLI relative values use the + invocation directory supplied by the CLI load boundary. Capture that + directory once for the load. Obtain canonical main-file identity consistently + with ConfigKit's acquired source records; no second parse or load. +3. Normalize nonempty relative external references, PromptKit profile sources, + and output/cache/debug directories to absolute paths. Preserve already + absolute values after existing whitespace handling. Empty optional cache + paths retain their automatic/disabled semantics. Do not inspect target + existence or read referenced content. +4. Record normalized paths and map moves so frozen inspection matches runtime + and traces the original declaration. Keep collision checks ahead of mutation + and process stable sorted keys. Use retained input metadata only as provenance, + not as a substitute for reading the current working value. +5. Fail with a safe field-level resolution diagnostic if a relative value + unexpectedly lacks an unambiguous required source/base. Defaults are a known + case, not missing provenance. Do not copy the upstream example's optional + warning-and-leave-relative fallback or impose confinement on application + paths. Queries are callback-scoped; detached records may be retained, but + never retain the `Resolution` context for post-load queries. +6. Enable imports and selected profiles on the public new loader, using + `File` and the selected file's canonical directory as the root. Keep CLI + references runtime-owned and use ConfigKit's composition semantics directly. **Acceptance:** root, fragment, and overlay values resolve from their own files; -inherited values retain their base origin; profile-replaced sequences use the -new declarations; normalized enclosing IDs do not lose attribution. Effective -and explanation output agree with the actual paths. Removed/overridden file -values are not opened or resolved. Root containment affects only ConfigKit -configuration sources. +inherited values retain their base origin; equal-valued replacements identify +their new source; profile-replaced sequences and normalized enclosing IDs retain +attribution. Destinations follow the same rule as references. Defaults use the +main directory; CLI overrides use the invocation directory. Effective and +explanation output agree with runtime. No second load or new file-access policy. **Validation:** `go test ./internal/core/config ./internal/framework/pipeline`. -Use temporary configuration trees with same-named reference files in different -directories, aliases/symlinks accepted by ConfigKit, profile replacements, -normalized keys, and absolute external paths outside the config root. Use real -reference materialization for one case to prove the intended bytes are chosen. +Use temporary trees with same-named references in different directories, +canonical symlink targets accepted by ConfigKit, profile replacements, +normalized keys, default and CLI destinations, empty cache paths, and absolute +paths outside the config root. Change the working directory in a controlled +test to distinguish the bases. Materialize a real reference in one case to +prove the intended bytes are chosen. Protect Notarius policy at the load +boundary; generic fixed-view/record-detachment tests remain upstream. -**Prompt size:** Medium, with a firm upstream gate; no CLI or example migration. +**Prompt size:** Medium; one shared path-base helper and its typed consumers, +with no CLI activation or example migration. ## Stage 8 — Prepare Shared CLI Loading And Diagnostics @@ -392,7 +408,8 @@ reference materialization for one case to prove the intended bytes are chosen. 1. Add a CLI helper that discovers the file, passes profile selection and explicit override records to the new loader, and returns `LoadedConfig` - plus the selected config path. Keep discovery order and injected test lookup. + plus the selected config path. Supply the invocation directory needed for + relative CLI overrides. Keep discovery order and injected test lookup. 2. Add a shared load-error reporter using `errors.As` for ConfigKit load errors and `WriteDiagnostics`; supply safe source context using the dependency's source records. Do not flatten multi-error output into repeated wrappers or @@ -469,12 +486,16 @@ incompatible generated references. 4. Convert both maintained D&D example configurations to v5 object syntax in place, still as self-contained files in this stage. Change the complete example's PromptKit source path for declaration-relative interpretation. + Review destination paths in both examples: retain their declared relative + strings unless an example's intended location requires a deliberate change; + explain their new configuration-file base. 5. Migrate run/reference/state tests and the maintained-example loader to v5. Split mechanical fixture updates from behavioral assertions within the diff so regressions remain reviewable. Retired env-based tests now supply YAML or explicit flags; keep one test proving those variables have no effect. 6. Update current CLI, configuration, operations, and consumer references for - v5, profile selection, environment retirement, and `effective-config.yml`. + v5, profile selection, environment retirement, path bases (including defaults + and destinations), and `effective-config.yml`. **Acceptance:** all production commands use the new helper; imported/profile configuration and explicit overrides agree with execution and debug output; @@ -658,43 +679,32 @@ unnecessary for a configuration migration. **Prompt size:** Medium; verification and focused documentation reconciliation, with no planned production redesign. -## Upstream Prerequisite +## Released API And Readiness -### Source Origins During ConfigKit Resolution +ConfigKit v0.3.0 provides both required query methods in its public +`lifecycle.go`: -**Evidence:** In v0.2.0, `Resolution` exports `IsExplicit`, `Record`, `Error`, -and `Warning`, with generic `Default` and `Normalize` helpers. It does not -expose source metadata. `Result.Metadata()` and `Result.Sources()` become -available only after `Load` returns. Mutating `Result.Config` afterward does -not change `WriteEffective` or `WriteExplanation`, which use frozen snapshots. -Therefore a single v0.2.0 load cannot both use declaration origins to normalize -paths and record that normalization in ConfigKit's inspection history. +- `(*Resolution).InputInfo(Path) (FieldInfo, bool)`: fixed decoded entry + metadata, including retained defaults and explicit null; missing or + shape-mismatched paths return false. +- `(*Resolution).Source(string) (SourceInfo, bool)`: detached acquired-source + records; empty/unknown IDs return false; non-file sources have no invented + filesystem path. -**Selected approach:** Request a small upstream API exposing surviving -input origins and canonical source records during the existing resolution -callback. It should distinguish base/profile/CLI/default origins, identify the -declaring file for a scalar, and permit lookup at original paths before -application map-key normalization. Keep the existing recorded mutation API. -Exact method names and the containing release must be confirmed upstream; -none are assumed to exist by this plan. This preserves one load, ConfigKit -ownership of composition/provenance, and the agreed effective-output contract. -The tradeoff is an upstream release dependency at Stage 7. This plan does not -authorize changes to the sibling repository. +Both methods are callback-scoped and perform no new filesystem or credential +lookup. Their entry-state contract permits querying original paths even after +map-key normalization. Existing `Normalize`/`Record` and result renderers +complete the one-load workflow; no upstream extension remains outstanding. -**Alternative considered:** Stay on v0.2.0 and resolve paths in the narrow Notarius -runtime converter using completed-result metadata. Preserve original document -paths for origin lookup. Present ConfigKit's effective configuration as a -logical configuration view, and expose resolved runtime paths separately in -debug/inspection output. This is implementable without an upstream change and -keeps composition centralized, but changes the agreed inspection contract, -requires Notarius to own the extra path report, and cannot make ConfigKit's -explanation record the post-load normalization. It requires explicit approval -and revision of Stage 7 and the feature roadmap before dependent work begins; -the user selected the upstream extension instead. +Review `docs/releases/v0.3.0.md`, the recorded-resolution contract in +`docs/config.md`, and `examples/consumers/consumers.go` / +`consumers_test.go` in the pinned dependency. The example demonstrates +`LoadNotariusPaths`; adopt its public-API usage, not its optional fallback +policy. Its warning-and-leave-relative behavior is not the Notarius contract. -Do not resolve this by silently dropping imports/profiles, changing all file -paths to working-directory-relative, reparsing YAML, or performing a second -load that might observe different files. All other product decisions in this -plan are settled; the upstream release is the sole remaining gate. No product -questions remain open. Once the API is released, review its exact signatures, -update the pinned version and this stage's API mapping, and then resume Stage 7. +The public-facade refactor needs no consumer migration; do not reach into the +private engine. Go 1.25 compatibility and the private YAML v4 prerelease pin +are unchanged. Upstream macOS support is best-effort, matching Notarius policy; +do not add platform gates as part of this adoption. + +No open product decisions or upstream prerequisites remain.