172 lines
6.9 KiB
Markdown
172 lines
6.9 KiB
Markdown
# Bugfix Request: Preserve Publish Destination Derivation with Explicit Rules
|
|
|
|
Status: Implemented; release target v2.1.1
|
|
|
|
## Summary
|
|
|
|
Narratio should successfully derive a publish destination when a valid
|
|
`publish.outputs` rule omits `dest`, regardless of whether that rule explicitly
|
|
sets `required`. Narratio v2.1.0 currently rejects this valid configuration in
|
|
some cases with a ConfigKit `resolve.unrecorded_change` diagnostic.
|
|
|
|
This is a configuration-resolution bookkeeping defect, not a YAML formatting
|
|
restriction. The fix should preserve the existing public configuration
|
|
contract: operators may omit a derivable destination and may independently set
|
|
`required` to either `true` or `false`.
|
|
|
|
## Observed Behavior
|
|
|
|
A pipeline containing a rule equivalent to the following fails during native
|
|
configuration loading:
|
|
|
|
```yaml
|
|
publish:
|
|
outputs: [{"source": "narratio.transcript.final", "required": true}]
|
|
```
|
|
|
|
Narratio reports:
|
|
|
|
```text
|
|
error [resolve.unrecorded_change] /publish/outputs: application resolution changed a value without a covering record
|
|
```
|
|
|
|
The same failure is possible with ordinary block-style YAML when `required` is
|
|
explicit and `dest` is omitted. Flow-style YAML is valid input and is not the
|
|
cause of the defect.
|
|
|
|
The existing destination-derivation tests do not expose this case because
|
|
their rules also omit `required`. Narratio then supplies the default value for
|
|
`required` and records a change covering the complete `publish.outputs`
|
|
sequence. That broader record happens to cover the destination mutation as
|
|
well, masking the missing resolution record that occurs when `required` is
|
|
already explicit.
|
|
|
|
## Expected Contract
|
|
|
|
The current configuration contract remains unchanged:
|
|
|
|
- `publish.outputs[].source` is required;
|
|
- `publish.outputs[].dest` may be omitted when Narratio can derive it from the
|
|
selected built-in or configured artifact source;
|
|
- `publish.outputs[].required` defaults to `true`, but an operator may set it
|
|
explicitly to either `true` or `false`; and
|
|
- equivalent valid YAML representations have equivalent configuration
|
|
semantics.
|
|
|
|
For example, both rules below should load and validate, retain their explicit
|
|
requiredness, and receive the canonical derived destination:
|
|
|
|
```yaml
|
|
publish:
|
|
outputs:
|
|
- source: narratio.transcript.final
|
|
required: true
|
|
- source: narratio.transcript.final_markdown
|
|
required: false
|
|
```
|
|
|
|
## Root Cause
|
|
|
|
ConfigKit intentionally treats sequences as atomic when comparing values
|
|
before and after application resolution. Changing a field within one
|
|
`publish.outputs` entry is therefore observed as a change to the
|
|
`/publish/outputs` sequence.
|
|
|
|
Narratio currently derives each missing destination by recording a
|
|
normalization at the individual `publish.outputs[index].dest` path. That child
|
|
record does not cover the atomic parent-sequence change. A separate
|
|
container-level record is currently created only when Narratio also defaults
|
|
an omitted `required` value, so configurations with explicit `required` values
|
|
lose that incidental coverage.
|
|
|
|
## Recommended Fix
|
|
|
|
Make Narratio's publish-output resolution bookkeeping conform to ConfigKit's
|
|
atomic-sequence contract. Whenever resolution derives one or more destinations
|
|
inside `publish.outputs`, the recorded resolution changes must cover the
|
|
`/publish/outputs` container. Destination derivation must not depend on an
|
|
unrelated `required` default being applied in the same list.
|
|
|
|
The implementation may retain more specific provenance where useful, but it
|
|
must provide valid container-level coverage for the sequence mutation. Do not
|
|
change ConfigKit's sequence comparison semantics merely to accommodate this
|
|
call site.
|
|
|
|
## Scope
|
|
|
|
- Correct destination derivation for publish rules that explicitly set
|
|
`required` and omit a derivable `dest`.
|
|
- Preserve explicit `required: true` and `required: false` values.
|
|
- Preserve canonical destination derivation for built-in transcript sources
|
|
and configured artifact sources.
|
|
- Add focused regression coverage through Narratio's real configuration load
|
|
and validation boundary.
|
|
- Prepare a backward-compatible patch release after the fix passes the normal
|
|
repository validation.
|
|
|
|
## Non-goals
|
|
|
|
- Changing the `publish.outputs` schema, defaults, source vocabulary, or
|
|
destination derivation rules.
|
|
- Requiring operators or configuration generators to supply `dest` when it is
|
|
derivable.
|
|
- Requiring operators to omit explicit `required: true` values.
|
|
- Treating flow-style YAML or JSON-compatible YAML values as unsupported.
|
|
- Changing ConfigKit's documented atomic treatment of sequences.
|
|
- Adding storage, publication, or network integration behavior.
|
|
|
|
## Regression Coverage
|
|
|
|
Add one focused package-level behavioral test that loads and validates a
|
|
pipeline with omitted destinations and explicit requiredness. A representative
|
|
list should include both `required: true` and `required: false`, then assert
|
|
that:
|
|
|
|
- loading and validation succeed;
|
|
- each destination is derived from its source; and
|
|
- each explicit `required` value is preserved.
|
|
|
|
Using the compact flow-style representation that exposed the integration
|
|
failure is reasonable, while the existing block-style cases continue to cover
|
|
omitted `required`. The test should exercise the public configuration behavior,
|
|
not ConfigKit call choreography or Narratio's private recording mechanism.
|
|
|
|
No network service, object store, credential, or test double is required.
|
|
|
|
## Target End State
|
|
|
|
All valid publish rules with derivable sources behave consistently across
|
|
these cases:
|
|
|
|
| `dest` | `required` | Result |
|
|
| --- | --- | --- |
|
|
| omitted | omitted | destination derived; required defaults to `true` |
|
|
| omitted | `true` | destination derived; explicit `true` retained |
|
|
| omitted | `false` | destination derived; explicit `false` retained |
|
|
| explicit | either value | explicit destination and requiredness retained |
|
|
|
|
`narratio config validate`, `narratio config show`, and ordinary pipeline
|
|
loading accept the corrected cases without an unrecorded-change diagnostic.
|
|
No operator configuration migration or workaround is required.
|
|
|
|
## Release
|
|
|
|
This is a backward-compatible correction to documented configuration behavior
|
|
and should be released as Narratio `v2.1.1`. The release note should identify
|
|
the corrected explicit-required/derived-destination interaction and state that
|
|
no special upgrade action is required.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- The focused regression test fails against v2.1.0 behavior and passes after
|
|
the fix.
|
|
- Publish rules with explicit `required: true` or `required: false` and omitted
|
|
derivable destinations load and validate successfully.
|
|
- Derived destinations and explicit requiredness appear correctly in the safe
|
|
effective configuration.
|
|
- Existing destination derivation, publish validation, imports, profiles, and
|
|
configuration inspection tests remain green.
|
|
- The complete repository validation and release-candidate checks pass.
|
|
- The correction is published as the immutable `v2.1.1` tag with the required
|
|
release note.
|