Remove legacy manifest provenance decoding
All checks were successful
ci/woodpecker/push/verify Pipeline was successful

This commit is contained in:
2026-09-25 19:45:19 +00:00
parent 73c4986f3d
commit b750c42ad6
7 changed files with 73 additions and 39 deletions

View File

@@ -23,11 +23,11 @@ portable opaque segments. Unsafe legacy identities are rejected with migration
guidance rather than being normalized into a different workspace or remote guidance rather than being normalized into a different workspace or remote
namespace. namespace.
Decoding is strict at typed execution-state fields. Unknown informational fields Decoding rejects unknown typed fields, including informational fields inside
inside `effective_config` are ignored so older selection labels can be read; `effective_config` and its nested selection. The schema version remains
unknown fields elsewhere are rejected. The schema version remains `narratio.session-manifest.v1`; there is no compatibility reader that silently
`narratio.session-manifest.v1` and there is no compatibility reader that drops retired fields. See [overlay migration](../operations.md#migrating-manifest-overlay-provenance)
silently drops retired execution state. Cleanup progress uses for deliberate migration of retired selection provenance. Cleanup progress uses
only the cleanup entry in `stages`. only the cleanup entry in `stages`.
Every persisted session manifest has a non-nil `stages` map. Each entry is Every persisted session manifest has a non-nil `stages` map. Each entry is

View File

@@ -410,6 +410,30 @@ execute. Deleting Narratio's local reference snapshots does not invalidate a
complete durable extraction; missing snapshots are reconstructed only when a complete durable extraction; missing snapshots are reconstructed only when a
later extraction actually runs. later extraction actually runs.
## Migrating Manifest Overlay Provenance
The pipeline-selection rename is a clean cutover. Manifests containing
`effective_config.selected_profile` are rejected by strict decoding; Narratio
does not silently discard or translate that retired field.
Before using an affected local session with the new version:
1. Stop Narratio activity for the session and back up its manifest.
2. Rename only `effective_config.selected_profile` to `selected_overlay`, keeping
its `name` and `source`, or remove the retired informational member. If a
current `selected_overlay` member already exists, retain it and remove the
retired member rather than creating duplicate keys.
3. Preserve the schema version, digest, stage records, checkpoint evidence,
output references, and other session state. Retry the intended command.
This metadata is informational, not resume authority. Do not delete session
progress or force all stages merely to perform this migration. Other unknown
fields still require investigation rather than blanket removal.
Do not edit immutable remote committed snapshots. Restoring an older snapshot
with retired provenance requires a compatible previous Narratio version, followed
by migration of the restored local manifest before using the new version.
## Migrating Audita and Provider Credentials ## Migrating Audita and Provider Credentials
This is a breaking clean cutover to Audita v2. Narratio does not translate an This is a breaking clean cutover to Audita v2. Narratio does not translate an

View File

@@ -93,8 +93,11 @@ newly written selection provenance use overlay terminology as well.
Maintain existing value-based digest and stage-reuse semantics; renaming Maintain existing value-based digest and stage-reuse semantics; renaming
informational selection provenance is not a reason to invalidate completed informational selection provenance is not a reason to invalidate completed
work or rewrite historical manifests. Update maintained examples and operator work or automatically rewrite historical manifests. Strict manifest decoding
migration guidance for the directory and flag changes. rejects retired provenance fields; operators must deliberately migrate that
informational member on a backed-up manifest, preserving execution/checkpoint
state. Update maintained examples and operator migration guidance for the
directory, flag, and provenance changes.
This rename does not apply to downstream model profiles, Scriptorium This rename does not apply to downstream model profiles, Scriptorium
`profile_id`, Audita's proposal/validation profiles, or Notarius's `profile_id`, Audita's proposal/validation profiles, or Notarius's

View File

@@ -46,9 +46,11 @@ as base selection; do not add a persistent default selector or new precedence.
Rename newly written informational provenance to overlay terminology, including Rename newly written informational provenance to overlay terminology, including
`selected_overlay` in manifest configuration provenance. Do not add a dual-field `selected_overlay` in manifest configuration provenance. Do not add a dual-field
reader, rewrite historical manifests, or introduce a manifest-version rejection reader, automatically rewrite historical manifests, or introduce a manifest-version
solely for this informational rename. Existing generic decoding behavior applies rejection solely for this informational rename. Existing strict typed decoding
to old optional provenance; it does not become resume authority. Preserve rejects retired provenance fields. Document backed-up operator migration of
that informational member without changing execution/checkpoint state; it does
not become resume authority. Preserve
semantic fingerprints and effective-value digest rules when values are unchanged. semantic fingerprints and effective-value digest rules when values are unchanged.
Only Narratio pipeline configuration selection is renamed. Keep model/profile Only Narratio pipeline configuration selection is renamed. Keep model/profile
@@ -199,8 +201,9 @@ config commands in `internal/app/`; configuration provenance in
directory is not a discovery fallback. Do not freeze incidental error wording. directory is not a discovery fallback. Do not freeze incidental error wording.
5. Verify unchanged effective values retain digest and stage-reuse behavior; 5. Verify unchanged effective values retain digest and stage-reuse behavior;
only diagnostic source labels/paths and selected provenance terminology change. only diagnostic source labels/paths and selected provenance terminology change.
Test loading representative existing manifests without inventing a new Test current provenance round trips and strict rejection of unknown/retired
invalidation or rejection gate for old optional selection metadata. provenance fields. Document deliberate operator migration; do not add a
compatibility decoder, semantic invalidation rule, or schema-version gate.
6. Run focused and full tests before proceeding so failures from the dependency 6. Run focused and full tests before proceeding so failures from the dependency
and terminology change are separated from adapter migrations. No Audita or and terminology change are separated from adapter migrations. No Audita or
Notarius credential/CLI behavior changes in this stage. Notarius credential/CLI behavior changes in this stage.

View File

@@ -1,6 +1,6 @@
# Audita v2 and ConfigKit Migration Review Remediation # Audita v2 and ConfigKit Migration Review Remediation
Status: Pending. Status: Complete.
Reviewed `83eaa96` through `fae18bf` against the approved clean-cutover decisions, Reviewed `83eaa96` through `fae18bf` against the approved clean-cutover decisions,
[feature roadmap](audita.md), and [implementation plan](implementation.md). [feature roadmap](audita.md), and [implementation plan](implementation.md).
@@ -46,7 +46,14 @@ Required remediation:
## 2. Remove the unapproved manifest compatibility decoder ## 2. Remove the unapproved manifest compatibility decoder
Priority: P2. Status: Pending. Priority: P2. Status: Complete.
Removed the permissive custom decoder; ordinary strict typed decoding now
applies to provenance as well. Replaced historical-acceptance coverage with
retired/unknown-field rejection tests, all of which failed before removal.
Current provenance round-trip coverage remains intact. Corrected the roadmap,
plan, internal contract, and operator guidance for narrow backed-up migration
without discarding execution/checkpoint state or changing resume authority.
Location: `internal/manifest/manifest.go:79-89`, Location: `internal/manifest/manifest.go:79-89`,
`internal/manifest/store_test.go:78`, and `docs/internal/manifest.md`'s decoding `internal/manifest/store_test.go:78`, and `docs/internal/manifest.md`'s decoding
@@ -92,6 +99,8 @@ reader. Do not treat that exception as already granted.
## Review verification ## Review verification
The following records the original review, before remediation.
- Existing `go test ./...` passed. - Existing `go test ./...` passed.
- Race tests passed for subprocess, Audita, Notarius, config, stage, app, and - Race tests passed for subprocess, Audita, Notarius, config, stage, app, and
manifest packages after removing the temporary reproduction. manifest packages after removing the temporary reproduction.
@@ -105,6 +114,13 @@ shared named credential forwarding/redaction, a v2-only Audita subprocess
adapter, ordinary reports as diagnostics, and versioned ordered-module polish adapter, ordinary reports as diagnostics, and versioned ordered-module polish
evidence. No live model/provider runs were performed during this review. evidence. No live model/provider runs were performed during this review.
## Migration remediation verification
Completed on 2026-09-25. Both new regressions failed before their respective
fixes. After remediation, `go test ./...`, `go test -race ./...`,
`go vet ./...`, `go build ./...`, documentation checks, maintained configuration
example checks, and `git diff --check` passed. No live provider runs were needed.
## Historical completed transcription review ## Historical completed transcription review
Status: Complete. Status: Complete.

View File

@@ -1,7 +1,6 @@
package manifest package manifest
import ( import (
"encoding/json"
"strings" "strings"
"time" "time"
@@ -76,18 +75,6 @@ type EffectiveConfigProvenance struct {
EffectiveConfigDigest string `json:"effective_config_digest,omitempty"` EffectiveConfigDigest string `json:"effective_config_digest,omitempty"`
} }
// UnmarshalJSON accepts unknown informational provenance from older manifests.
// The enclosing manifest and its execution state remain strictly decoded.
func (p *EffectiveConfigProvenance) UnmarshalJSON(data []byte) error {
type provenance EffectiveConfigProvenance
var decoded provenance
if err := json.Unmarshal(data, &decoded); err != nil {
return err
}
*p = EffectiveConfigProvenance(decoded)
return nil
}
// SelectedOverlayProvenance identifies an explicitly or default-selected // SelectedOverlayProvenance identifies an explicitly or default-selected
// pipeline overlay without recording overlay content. // pipeline overlay without recording overlay content.
type SelectedOverlayProvenance struct { type SelectedOverlayProvenance struct {

View File

@@ -75,18 +75,19 @@ func TestLocalStoreCreateSaveLoadRoundTrip(t *testing.T) {
} }
} }
func TestLocalStoreLoadsManifestWithHistoricalSelectionProvenance(t *testing.T) { func TestLocalStoreRejectsUnknownProvenanceFields(t *testing.T) {
path := filepath.Join(t.TempDir(), "manifest.json") for _, tc := range []struct{ name, provenance, field string }{
data := `{"schema_version":"narratio.session-manifest.v1","session_id":"2026-05-03","created_at":"2026-05-03T10:00:00Z","updated_at":"2026-05-03T10:00:00Z","effective_config":{"selected_profile":{"name":"testing","source":"explicit"},"effective_config_digest":"001122"},"stages":{}}` {"retired selection", `{"selected_profile":{"name":"testing","source":"explicit"}}`, "selected_profile"},
if err := os.WriteFile(path, []byte(data), 0o600); err != nil { {"unknown provenance", `{"unexpected":true}`, "unexpected"},
t.Fatal(err) {"unknown selection field", `{"selected_overlay":{"name":"testing","source":"explicit","unexpected":true}}`, "unexpected"},
} } {
loaded, err := (&LocalStore{}).Load(context.Background(), path) t.Run(tc.name, func(t *testing.T) {
if err != nil { data := `{"schema_version":"narratio.session-manifest.v1","session_id":"2026-05-03","created_at":"2026-05-03T10:00:00Z","updated_at":"2026-05-03T10:00:00Z","effective_config":` + tc.provenance + `,"stages":{}}`
t.Fatal(err) _, err := (&LocalStore{}).LoadReader(context.Background(), strings.NewReader(data))
} if err == nil || !strings.Contains(err.Error(), tc.field) {
if loaded.EffectiveConfig == nil || loaded.EffectiveConfig.EffectiveConfigDigest != "001122" || loaded.EffectiveConfig.SelectedOverlay != nil { t.Fatalf("expected rejection identifying %q, got %v", tc.field, err)
t.Fatalf("historical informational provenance = %#v", loaded.EffectiveConfig) }
})
} }
} }